diff --git a/.ci/build-freebsd.sh b/.ci/build-freebsd.sh index 95c00a8733..c1affc46b1 100755 --- a/.ci/build-freebsd.sh +++ b/.ci/build-freebsd.sh @@ -7,9 +7,9 @@ git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ && !/opencv/ { # 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-16.0.1/llvm-project-16.0.1.src.tar.xz +fetch https://github.com/llvm/llvm-project/releases/download/llvmorg-19.1.7/llvm-project-19.1.7.src.tar.xz tar xf llvm*.tar.xz -export CC=clang16 CXX=clang++16 +export CC=clang19 CXX=clang++19 cmake -B libcxx_build -G Ninja -S llvm*/libcxx \ -DLLVM_CCACHE_BUILD=ON \ -DLIBCXX_INCLUDE_BENCHMARKS=OFF \ diff --git a/.ci/build-linux-aarch64.sh b/.ci/build-linux-aarch64.sh index a0c4724bff..4a98002659 100644 --- a/.ci/build-linux-aarch64.sh +++ b/.ci/build-linux-aarch64.sh @@ -14,6 +14,9 @@ mkdir build && cd build || exit 1 export CC="${CLANG_BINARY}" export CXX="${CLANGXX_BINARY}" +export LINKER="${LLD_BINARY}" +export CFLAGS="$CFLAGS -fuse-ld=${LINKER}" +export CXXFLAGS="$CXXFLAGS -fuse-ld=${LINKER}" cmake .. \ -DCMAKE_INSTALL_PREFIX=/usr \ diff --git a/.ci/build-mac-arm64.sh b/.ci/build-mac-arm64.sh index a40a37972c..3c89fd3e5a 100644 --- a/.ci/build-mac-arm64.sh +++ b/.ci/build-mac-arm64.sh @@ -47,6 +47,7 @@ ln -s /usr/local/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl brew_arm64_install_packages 0mq aom aribb24 ca-certificates cjson dav1d ffmpeg@5 fontconfig freetype freetype2 gettext glew gmp gnutls lame libbluray libidn2 libnettle libogg libpng librist libsodium libsoxr libtasn libtasn1 libunistring libvmaf libvorbis libvpx libx11 libxau libxcb libxdmcp llvm@$LLVM_COMPILER_VER mbedtls molten-vk nettle opencore-amr openjpeg openssl opus p11-kit pkg-config pkgconfig pzstd rav1e sdl2 snappy speex srt svt-av1 theora vulkan-headers webp x264 x265 xz z3 zeromq zmq zstd "$BREW_ARM64_PATH/bin/brew" link -f ffmpeg@5 +ln -s /opt/homebrew1/opt/llvm@19/lib/unwind/libunwind.1.dylib /opt/homebrew1/opt/llvm@19/lib/libunwind.1.dylib # moltenvk based on commit for 1.2.11 release wget https://raw.githubusercontent.com/Homebrew/homebrew-core/6bfc8950c696d1f952425e8af2a6248603dc0df9/Formula/m/molten-vk.rb diff --git a/.ci/get_keys-windows.sh b/.ci/get_keys-windows.sh index 9ef56dda62..efc63978f1 100644 --- a/.ci/get_keys-windows.sh +++ b/.ci/get_keys-windows.sh @@ -1,4 +1,4 @@ #!/bin/sh -ex -curl -fLo "./llvm.lock" "https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z.sha256" +curl -fLo "./llvm.lock" "https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-19.1.7/llvmlibs_mt.7z.sha256" curl -fLo "./glslang.lock" "https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z.sha256" diff --git a/.ci/install-freebsd.sh b/.ci/install-freebsd.sh index c6528ad923..0cc0a71ba3 100755 --- a/.ci/install-freebsd.sh +++ b/.ci/install-freebsd.sh @@ -9,7 +9,7 @@ export ASSUME_ALWAYS_YES=true pkg info # debug # Prefer newer Clang than in base system (see also .ci/build-freebsd.sh) -pkg install llvm16 +pkg install llvm19 # Mandatory dependencies (qt6-base is pulled via qt6-multimedia) pkg install git ccache cmake ninja qt6-multimedia qt6-svg glew openal-soft ffmpeg diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index e75154ae01..07433d3505 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -19,7 +19,7 @@ QT_DECL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtdeclarative${QT_SUFFIX}" QT_TOOL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttools${QT_SUFFIX}" QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia${QT_SUFFIX}" QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}" -LLVMLIBS_URL='https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z' +LLVMLIBS_URL='https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-19.1.7/llvmlibs_mt.7z' GLSLANG_URL='https://github.com/RPCS3/glslang/releases/latest/download/glslanglibs_mt.7z' VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-1.3.268.0-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1" diff --git a/.cirrus.yml b/.cirrus.yml index 0a66160c13..1a973cd183 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -56,7 +56,7 @@ env: # linux_task: # container: -# image: rpcs3/rpcs3-ci-focal:1.9 +# image: rpcs3/rpcs3-ci-jammy:1.0 # cpu: 4 # memory: 16G # env: @@ -132,7 +132,7 @@ linux_aarch64_task: matrix: - name: Cirrus Linux AArch64 Clang arm_container: - image: 'docker.io/rpcs3/rpcs3-ci-focal-aarch64:1.0' + image: 'docker.io/rpcs3/rpcs3-ci-jammy-aarch64:1.0' cpu: 8 memory: 8G clang_script: diff --git a/3rdparty/FAudio b/3rdparty/FAudio index 707114aef2..af74e661c1 160000 --- a/3rdparty/FAudio +++ b/3rdparty/FAudio @@ -1 +1 @@ -Subproject commit 707114aef2907793644d4067a6e7b09b51502ca9 +Subproject commit af74e661c1bd8b105840d14485cc01d9c782b513 diff --git a/3rdparty/OpenAL/openal-soft.vcxproj b/3rdparty/OpenAL/openal-soft.vcxproj index 37945fdf01..d309093527 100644 --- a/3rdparty/OpenAL/openal-soft.vcxproj +++ b/3rdparty/OpenAL/openal-soft.vcxproj @@ -49,11 +49,11 @@ call vsdevcmd.bat -arch=amd64 cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)" - cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -DCMAKE_SYSTEM_VERSION=6.1 -DLIBTYPE=STATIC -DFORCE_STATIC_VCRT=true -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" + cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DFORCE_STATIC_VCRT=true -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" call vsdevcmd.bat -arch=amd64 cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)" - cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -DCMAKE_SYSTEM_VERSION=6.1 -DLIBTYPE=STATIC -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" + cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" echo Copying.. diff --git a/3rdparty/curl/curl b/3rdparty/curl/curl index 75a2079d5c..34cf9d54a4 160000 --- a/3rdparty/curl/curl +++ b/3rdparty/curl/curl @@ -1 +1 @@ -Subproject commit 75a2079d5c28debb2eaa848ca9430f1fe0d7844c +Subproject commit 34cf9d54a46598c44938aa7598820484d7af7133 diff --git a/3rdparty/curl/libcurl.vcxproj b/3rdparty/curl/libcurl.vcxproj index 7981aa4b22..710ea9b225 100644 --- a/3rdparty/curl/libcurl.vcxproj +++ b/3rdparty/curl/libcurl.vcxproj @@ -66,7 +66,6 @@ - @@ -124,6 +123,7 @@ + @@ -175,6 +175,7 @@ + @@ -191,8 +192,11 @@ + + + @@ -241,21 +245,17 @@ - - - - @@ -315,6 +315,7 @@ + @@ -363,6 +364,7 @@ + @@ -379,8 +381,13 @@ + + + + + diff --git a/3rdparty/curl/libcurl.vcxproj.filters b/3rdparty/curl/libcurl.vcxproj.filters index aaa5580113..3592158a28 100644 --- a/3rdparty/curl/libcurl.vcxproj.filters +++ b/3rdparty/curl/libcurl.vcxproj.filters @@ -417,9 +417,6 @@ Source Files - - Source Files - Source Files @@ -510,6 +507,21 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -554,12 +566,6 @@ Header Files - - Header Files - - - Header Files - Header Files @@ -575,9 +581,6 @@ Header Files - - Header Files - Header Files @@ -935,9 +938,6 @@ Header Files - - Header Files - Header Files @@ -1034,6 +1034,27 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/3rdparty/fusion/fusion b/3rdparty/fusion/fusion index fecf2f0af3..066d4a63b2 160000 --- a/3rdparty/fusion/fusion +++ b/3rdparty/fusion/fusion @@ -1 +1 @@ -Subproject commit fecf2f0af3bd23cbba553ceedc2bc6c1cd410fc1 +Subproject commit 066d4a63b2c714b20b0a8073a01fda7c5c6763f6 diff --git a/3rdparty/hidapi/hidapi b/3rdparty/hidapi/hidapi index 8b43a97a93..6bfdcf7368 160000 --- a/3rdparty/hidapi/hidapi +++ b/3rdparty/hidapi/hidapi @@ -1 +1 @@ -Subproject commit 8b43a97a9330f8b0035439ce9e255e4be202deca +Subproject commit 6bfdcf7368169efe1b745cd4468d45cda05ef8de diff --git a/3rdparty/libpng/libpng b/3rdparty/libpng/libpng index 51f5bd68b9..0024abd279 160000 --- a/3rdparty/libpng/libpng +++ b/3rdparty/libpng/libpng @@ -1 +1 @@ -Subproject commit 51f5bd68b9b806d2c92b4318164d28b49357da31 +Subproject commit 0024abd279d3a06435c0309a3f4172eed7c7a19a diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL index fa24d868ac..8236e01a9f 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit fa24d868ac2f8fd558e4e914c9863411245db8fd +Subproject commit 8236e01a9f758d15927624925c6043f84d8a261f diff --git a/3rdparty/llvm/CMakeLists.txt b/3rdparty/llvm/CMakeLists.txt index e2eb18f129..cad20a7e09 100644 --- a/3rdparty/llvm/CMakeLists.txt +++ b/3rdparty/llvm/CMakeLists.txt @@ -41,7 +41,7 @@ if(WITH_LLVM) set(STATIC_LINK_LLVM ON CACHE BOOL "Link against LLVM statically. This will get set to ON if you build LLVM from the submodule." FORCE) - find_package(LLVM 16.0 CONFIG) + find_package(LLVM 19.1 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() @@ -59,8 +59,8 @@ if(WITH_LLVM) message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. \ Enable BUILD_LLVM option to build LLVM from included as a git submodule.") endif() - if (LLVM_VERSION VERSION_LESS 16) - message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 16 or above. \ + if (LLVM_VERSION VERSION_LESS 18) + message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 18 or above. \ Enable BUILD_LLVM option to build LLVM from included as a git submodule.") endif() endif() @@ -68,7 +68,7 @@ if(WITH_LLVM) if (STATIC_LINK_LLVM) if (NOT DEFINED LLVM_TARGETS_TO_BUILD) if(COMPILER_ARM) - set(LLVM_TARGETS_TO_BUILD "AArch64;X86" CACHE STRING "Semicolon-separated list of targets to build, or \"all\".") + set(LLVM_TARGETS_TO_BUILD "AArch64" CACHE STRING "Semicolon-separated list of targets to build, or \"all\".") else() set(LLVM_TARGETS_TO_BUILD "X86" CACHE STRING "Semicolon-separated list of targets to build, or \"all\".") endif() diff --git a/3rdparty/llvm/llvm b/3rdparty/llvm/llvm index cd89023f79..cd708029e0 160000 --- a/3rdparty/llvm/llvm +++ b/3rdparty/llvm/llvm @@ -1 +1 @@ -Subproject commit cd89023f797900e4492da58b7bed36f702120011 +Subproject commit cd708029e0b2869e80abe31ddb175f7c35361f90 diff --git a/3rdparty/llvm/llvm_build.vcxproj b/3rdparty/llvm/llvm_build.vcxproj index 73a33db1a2..160ec50222 100644 --- a/3rdparty/llvm/llvm_build.vcxproj +++ b/3rdparty/llvm/llvm_build.vcxproj @@ -40,11 +40,11 @@ call vsdevcmd.bat -arch=amd64 cd "$(SolutionDir)build\tmp\llvm_build-$(Configuration)-$(Platform)" - cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -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_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MDd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm" + cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -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_BUILD_BENCHMARKS=OFF -DLLVM_BUILD_DOCS=OFF -DLLVM_BUILD_EXAMPLES=OFF -DLLVM_BUILD_TESTS=OFF\ -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DLLVM_INSTALL_UTILS=OFF -DLLVM_ENABLE_DIA_SDK=OFF -DLLVM_ENABLE_PDB=OFF -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm" call vsdevcmd.bat -arch=amd64 cd "$(SolutionDir)build\tmp\llvm_build-$(Configuration)-$(Platform)" - cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -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_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MDd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm" + cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL -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_BUILD_BENCHMARKS=OFF -DLLVM_BUILD_DOCS=OFF -DLLVM_BUILD_EXAMPLES=OFF -DLLVM_BUILD_TESTS=OFF\ -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DLLVM_INSTALL_UTILS=OFF -DLLVM_ENABLE_DIA_SDK=OFF -DLLVM_ENABLE_PDB=OFF -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm" echo Cleaning.. diff --git a/3rdparty/llvm/llvm_build_clang_cl.vcxproj b/3rdparty/llvm/llvm_build_clang_cl.vcxproj index f9380e896d..c0ccb204c6 100644 --- a/3rdparty/llvm/llvm_build_clang_cl.vcxproj +++ b/3rdparty/llvm/llvm_build_clang_cl.vcxproj @@ -40,11 +40,11 @@ call vsdevcmd.bat -arch=amd64 cd "$(SolutionDir)build\tmp\llvm_build-$(Configuration)-$(Platform)" - cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -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_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MDd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm" + cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -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_BUILD_BENCHMARKS=OFF -DLLVM_BUILD_DOCS=OFF -DLLVM_BUILD_EXAMPLES=OFF -DLLVM_BUILD_TESTS=OFF\ -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DLLVM_INSTALL_UTILS=OFF -DLLVM_ENABLE_DIA_SDK=OFF -DLLVM_ENABLE_PDB=OFF -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm" call vsdevcmd.bat -arch=amd64 cd "$(SolutionDir)build\tmp\llvm_build-$(Configuration)-$(Platform)" - cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -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_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MDd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm" + cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL -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_BUILD_BENCHMARKS=OFF -DLLVM_BUILD_DOCS=OFF -DLLVM_BUILD_EXAMPLES=OFF -DLLVM_BUILD_TESTS=OFF\ -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DLLVM_INSTALL_UTILS=OFF -DLLVM_ENABLE_DIA_SDK=OFF -DLLVM_ENABLE_PDB=OFF -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm" echo Cleaning.. diff --git a/BUILDING.md b/BUILDING.md index cc868c1352..a8c92e26a6 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -128,7 +128,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r **NOTE:** The recommended build configuration is `Release`. (On older revisions: `Release - LLVM`) To speed up the compilation time, you may want to download and extract to `\build\lib_ext\<$(Configuration)>-x64` (e.g. `c:\rpcs3\build\lib_ext\Release-x64`; the path needs to be created) some of the following precompiled libs: -- [LLVM libs](https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z) +- [LLVM libs](https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-19.1.7/llvmlibs_mt.7z) - [additional libs](https://github.com/RPCS3/glslang/releases/latest/download/glslanglibs_mt.7z) **NOTES:** diff --git a/Utilities/File.cpp b/Utilities/File.cpp index ec0f49c89e..1adce40f5b 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -1,8 +1,6 @@ #include "File.h" #include "mutex.h" #include "StrFmt.h" -#include "StrUtil.h" -#include "Crypto/sha1.h" #include #include @@ -18,6 +16,8 @@ using namespace std::literals::string_literals; #ifdef _WIN32 +#include "Utilities/StrUtil.h" + #include #include diff --git a/Utilities/JIT.h b/Utilities/JIT.h index cbba82b960..fcdc59352e 100644 --- a/Utilities/JIT.h +++ b/Utilities/JIT.h @@ -77,7 +77,7 @@ struct jit_runtime_base jit_runtime_base& operator=(const jit_runtime_base&) = delete; const asmjit::Environment& environment() const noexcept; - void* _add(asmjit::CodeHolder* code) noexcept; + void* _add(asmjit::CodeHolder* code, usz align = 64) noexcept; virtual uchar* _alloc(usz size, usz align) noexcept = 0; }; @@ -93,6 +93,10 @@ struct jit_runtime final : jit_runtime_base // Allocate memory static u8* alloc(usz size, usz align, bool exec = true) noexcept; + // Allocate 0 bytes, observe memory location + // Same as alloc(0, 1, exec) + static u8* peek(bool exec = true) noexcept; + // Should be called at least once after global initialization static void initialize(); @@ -432,7 +436,7 @@ namespace asmjit // Build runtime function with asmjit::X86Assembler template -inline FT build_function_asm(std::string_view name, F&& builder, ::jit_runtime* custom_runtime = nullptr) +inline FT build_function_asm(std::string_view name, F&& builder, ::jit_runtime* custom_runtime = nullptr, bool reduced_size = false) { #ifdef __APPLE__ pthread_jit_write_protect_np(false); @@ -467,6 +471,7 @@ inline FT build_function_asm(std::string_view name, F&& builder, ::jit_runtime* Asm compiler(&code); compiler.addEncodingOptions(EncodingOptions::kOptimizedAlign); + compiler.addEncodingOptions(EncodingOptions::kOptimizeForSize); if constexpr (std::is_invocable_r_v) { if (!builder(compiler, args)) @@ -483,7 +488,7 @@ inline FT build_function_asm(std::string_view name, F&& builder, ::jit_runtime* compiler(); } - const auto result = rt._add(&code); + const auto result = rt._add(&code, reduced_size ? 16 : 64); jit_announce(result, code.codeSize(), name); return reinterpret_cast(uptr(result)); } @@ -498,6 +503,8 @@ namespace llvm class StringRef; } +enum class thread_state : u32; + // Temporary compiler interface class jit_compiler final { @@ -514,8 +521,9 @@ class jit_compiler final atomic_t m_disk_space = umax; public: - jit_compiler(const std::unordered_map& _link, const std::string& _cpu, u32 flags = 0); - ~jit_compiler(); + jit_compiler(const std::unordered_map& _link, const std::string& _cpu, u32 flags = 0, std::function symbols_cement = {}) noexcept; + jit_compiler& operator=(thread_state) noexcept; + ~jit_compiler() noexcept; // Get LLVM context auto& get_context() diff --git a/Utilities/JITASM.cpp b/Utilities/JITASM.cpp index c39d261c22..63628f448b 100644 --- a/Utilities/JITASM.cpp +++ b/Utilities/JITASM.cpp @@ -147,6 +147,12 @@ static u8* add_jit_memory(usz size, usz align) return pointer; } + if (!size && align == 1) + { + // Return memory top address + return pointer + (Ctr.load() & 0xffff'ffff); + } + u64 olda, newa; // Simple allocation by incrementing pointer to the next free data @@ -211,7 +217,7 @@ const asmjit::Environment& jit_runtime_base::environment() const noexcept return g_env; } -void* jit_runtime_base::_add(asmjit::CodeHolder* code) noexcept +void* jit_runtime_base::_add(asmjit::CodeHolder* code, usz align) noexcept { ensure(!code->flatten()); ensure(!code->resolveUnresolvedLinks()); @@ -219,7 +225,7 @@ void* jit_runtime_base::_add(asmjit::CodeHolder* code) noexcept if (!codeSize) return nullptr; - auto p = ensure(this->_alloc(codeSize, 64)); + auto p = ensure(this->_alloc(codeSize, align)); ensure(!code->relocateToBase(uptr(p))); { @@ -231,6 +237,11 @@ void* jit_runtime_base::_add(asmjit::CodeHolder* code) noexcept for (asmjit::Section* section : code->_sections) { + if (section->offset() + section->bufferSize() > utils::align(codeSize, align)) + { + fmt::throw_exception("CodeHolder section exceeds range: Section->offset: 0x%x, Section->bufferSize: 0x%x, alloted-memory=0x%x", section->offset(), section->bufferSize(), utils::align(codeSize, align)); + } + std::memcpy(p + section->offset(), section->data(), section->bufferSize()); } } @@ -268,6 +279,18 @@ u8* jit_runtime::alloc(usz size, usz align, bool exec) noexcept } } +u8* jit_runtime::peek(bool exec) noexcept +{ + if (exec) + { + return add_jit_memory(0, 1); + } + else + { + return add_jit_memory(0, 1); + } +} + void jit_runtime::initialize() { if (!s_code_init.empty() || !s_data_init.empty()) diff --git a/Utilities/JITLLVM.cpp b/Utilities/JITLLVM.cpp index f93a46645d..b6061634bb 100644 --- a/Utilities/JITLLVM.cpp +++ b/Utilities/JITLLVM.cpp @@ -1,5 +1,6 @@ #include "util/types.hpp" #include "util/sysinfo.hpp" +#include "Utilities/Thread.h" #include "JIT.h" #include "StrFmt.h" #include "File.h" @@ -77,8 +78,7 @@ static u64 make_null_function(const std::string& name) if (res.ec == std::errc() && res.ptr == name.c_str() + name.size() && addr < 0x8000'0000) { - // Point the garbage to reserved, non-executable memory - return reinterpret_cast(vm::g_sudo_addr + addr); + fmt::throw_exception("Unhandled symbols cementing! (name='%s'", name); } } @@ -174,18 +174,34 @@ struct JITAnnouncer : llvm::JITEventListener struct MemoryManager1 : llvm::RTDyldMemoryManager { // 256 MiB for code or data - static constexpr u64 c_max_size = 0x20000000 / 2; + static constexpr u64 c_max_size = 0x1000'0000; // Allocation unit (2M) static constexpr u64 c_page_size = 2 * 1024 * 1024; - // Reserve 512 MiB - u8* const ptr = static_cast(utils::memory_reserve(c_max_size * 2)); + // Reserve 256 MiB blocks + void* m_code_mems = nullptr; + void* m_data_ro_mems = nullptr; + void* m_data_rw_mems = nullptr; u64 code_ptr = 0; - u64 data_ptr = c_max_size; + u64 data_ro_ptr = 0; + u64 data_rw_ptr = 0; - MemoryManager1() = default; + // First fallback for non-existing symbols + // May be a memory container internally + std::function m_symbols_cement; + + MemoryManager1(std::function symbols_cement = {}) noexcept + : m_symbols_cement(std::move(symbols_cement)) + { + auto ptr = reinterpret_cast(utils::memory_reserve(c_max_size * 3)); + m_code_mems = ptr; + // ptr += c_max_size; + // m_data_ro_mems = ptr; + ptr += c_max_size; + m_data_rw_mems = ptr; + } MemoryManager1(const MemoryManager1&) = delete; @@ -194,13 +210,22 @@ struct MemoryManager1 : llvm::RTDyldMemoryManager ~MemoryManager1() override { // Hack: don't release to prevent reuse of address space, see jit_announce - utils::memory_decommit(ptr, c_max_size * 2); + // constexpr auto how_much = [](u64 pos) { return utils::align(pos, pos < c_page_size ? c_page_size / 4 : c_page_size); }; + // utils::memory_decommit(m_code_mems, how_much(code_ptr)); + // utils::memory_decommit(m_data_ro_mems, how_much(data_ro_ptr)); + // utils::memory_decommit(m_data_rw_mems, how_much(data_rw_ptr)); + utils::memory_decommit(m_code_mems, c_max_size * 3); } llvm::JITSymbol findSymbol(const std::string& name) override { u64 addr = RTDyldMemoryManager::getSymbolAddress(name); + if (!addr && m_symbols_cement) + { + addr = m_symbols_cement(name); + } + if (!addr) { addr = make_null_function(name); @@ -214,45 +239,79 @@ struct MemoryManager1 : llvm::RTDyldMemoryManager return {addr, llvm::JITSymbolFlags::Exported}; } - u8* allocate(u64& oldp, uptr size, uint align, utils::protection prot) + u8* allocate(u64& alloc_pos, void* block, uptr size, u64 align, utils::protection prot) { - if (align > c_page_size) + align = align ? align : 16; + + const u64 sizea = utils::align(size, align); + + if (!size || align > c_page_size || sizea > c_max_size || sizea < size) { - jit_log.fatal("Unsupported alignment (size=0x%x, align=0x%x)", size, align); + jit_log.fatal("Unsupported size/alignment (size=0x%x, align=0x%x)", size, align); return nullptr; } - const u64 olda = utils::align(oldp, align); - const u64 newp = utils::align(olda + size, align); + u64 oldp = alloc_pos; - if ((newp - 1) / c_max_size != oldp / c_max_size) + u64 olda = utils::align(oldp, align); + + ensure(olda >= oldp); + ensure(olda < ~sizea); + + u64 newp = olda + sizea; + + if ((newp - 1) / c_max_size != (oldp - 1) / c_max_size) { - jit_log.fatal("Out of memory (size=0x%x, align=0x%x)", size, align); - return nullptr; + constexpr usz num_of_allocations = 1; + + if ((newp - 1) / c_max_size > num_of_allocations) + { + // Allocating more than one region does not work for relocations, needs more robust solution + fmt::throw_exception("Out of memory (size=0x%x, align=0x%x)", size, align); + } } - if ((oldp - 1) / c_page_size != (newp - 1) / c_page_size) + // Update allocation counter + alloc_pos = newp; + + constexpr usz page_quarter = c_page_size / 4; + + // Optimization: split the first allocation to 512 KiB for single-module compilers + if (oldp < c_page_size && align < page_quarter && (std::min(newp, c_page_size) - 1) / page_quarter != (oldp - 1) / page_quarter) + { + const u64 pagea = utils::align(oldp, page_quarter); + const u64 psize = utils::align(std::min(newp, c_page_size) - pagea, page_quarter); + utils::memory_commit(reinterpret_cast(block) + (pagea % c_max_size), psize, prot); + + // Advance + oldp = pagea + psize; + } + + if ((newp - 1) / c_page_size != (oldp - 1) / c_page_size) { // Allocate pages on demand const u64 pagea = utils::align(oldp, c_page_size); const u64 psize = utils::align(newp - pagea, c_page_size); - utils::memory_commit(this->ptr + pagea, psize, prot); + utils::memory_commit(reinterpret_cast(block) + (pagea % c_max_size), psize, prot); } - // Update allocation counter - oldp = newp; - - return this->ptr + olda; + return reinterpret_cast(block) + (olda % c_max_size); } u8* allocateCodeSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/) override { - return allocate(code_ptr, size, align, utils::protection::wx); + return allocate(code_ptr, m_code_mems, size, align, utils::protection::wx); } - u8* allocateDataSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/, bool /*is_ro*/) override + u8* allocateDataSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/, bool is_ro) override { - return allocate(data_ptr, size, align, utils::protection::rw); + if (is_ro) + { + // Disabled + //return allocate(data_ro_ptr, m_data_ro_mems, size, align, utils::protection::rw); + } + + return allocate(data_rw_ptr, m_data_rw_mems, size, align, utils::protection::rw); } bool finalizeMemory(std::string* = nullptr) override @@ -272,7 +331,14 @@ struct MemoryManager1 : llvm::RTDyldMemoryManager // Simple memory manager struct MemoryManager2 : llvm::RTDyldMemoryManager { - MemoryManager2() = default; + // First fallback for non-existing symbols + // May be a memory container internally + std::function m_symbols_cement; + + MemoryManager2(std::function symbols_cement = {}) noexcept + : m_symbols_cement(std::move(symbols_cement)) + { + } ~MemoryManager2() override { @@ -282,6 +348,11 @@ struct MemoryManager2 : llvm::RTDyldMemoryManager { u64 addr = RTDyldMemoryManager::getSymbolAddress(name); + if (!addr && m_symbols_cement) + { + addr = m_symbols_cement(name); + } + if (!addr) { addr = make_null_function(name); @@ -561,10 +632,22 @@ bool jit_compiler::add_sub_disk_space(ssz space) }).second; } -jit_compiler::jit_compiler(const std::unordered_map& _link, const std::string& _cpu, u32 flags) +jit_compiler::jit_compiler(const std::unordered_map& _link, const std::string& _cpu, u32 flags, std::function symbols_cement) noexcept : m_context(new llvm::LLVMContext) , m_cpu(cpu(_cpu)) { + [[maybe_unused]] static const bool s_install_llvm_error_handler = []() + { + llvm::remove_fatal_error_handler(); + llvm::install_fatal_error_handler([](void*, const char* msg, bool) + { + const std::string_view out = msg ? msg : ""; + fmt::throw_exception("LLVM Emergency Exit Invoked: '%s'", out); + }, nullptr); + + return true; + }(); + std::string result; auto null_mod = std::make_unique ("null_", *m_context); @@ -577,17 +660,17 @@ jit_compiler::jit_compiler(const std::unordered_map& _link, co // Auxiliary JIT (does not use custom memory manager, only writes the objects) if (flags & 0x1) { - mem = std::make_unique(); + mem = std::make_unique(std::move(symbols_cement)); } else { - mem = std::make_unique(); + mem = std::make_unique(std::move(symbols_cement)); null_mod->setTargetTriple(jit_compiler::triple2()); } } else { - mem = std::make_unique(); + mem = std::make_unique(std::move(symbols_cement)); } { @@ -595,11 +678,7 @@ jit_compiler::jit_compiler(const std::unordered_map& _link, co .setErrorStr(&result) .setEngineKind(llvm::EngineKind::JIT) .setMCJITMemoryManager(std::move(mem)) -#if LLVM_VERSION_MAJOR < 18 - .setOptLevel(llvm::CodeGenOpt::Aggressive) -#else .setOptLevel(llvm::CodeGenOptLevel::Aggressive) -#endif .setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small) #ifdef __APPLE__ //.setCodeModel(llvm::CodeModel::Large) @@ -636,7 +715,19 @@ jit_compiler::jit_compiler(const std::unordered_map& _link, co } } -jit_compiler::~jit_compiler() +jit_compiler& jit_compiler::operator=(thread_state s) noexcept +{ + if (s == thread_state::destroying_context) + { + // Release resources explicitly + m_engine.reset(); + m_context.reset(); + } + + return *this; +} + +jit_compiler::~jit_compiler() noexcept { } diff --git a/Utilities/StrFmt.cpp b/Utilities/StrFmt.cpp index dd59303337..3b55ae3861 100644 --- a/Utilities/StrFmt.cpp +++ b/Utilities/StrFmt.cpp @@ -2,7 +2,6 @@ #include "StrUtil.h" #include "cfmt.h" #include "util/endian.hpp" -#include "util/logs.hpp" #include "util/v128.hpp" #include diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index c31a0b804f..603c430ba3 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1703,23 +1703,58 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe cpu->state += cpu_flag::wait; } - Emu.Pause(true); - - if (!g_tls_access_violation_recovered) - { - vm_log.notice("\n%s", dump_useful_thread_info()); - } - // Note: a thread may access violate more than once after hack_alloc recovery // Do not log any further access violations in this case. if (!g_tls_access_violation_recovered) { + vm_log.notice("\n%s", dump_useful_thread_info()); vm_log.fatal("Access violation %s location 0x%x (%s)", is_writing ? "writing" : (cpu && cpu->get_class() == thread_class::ppu && cpu->get_pc() == addr ? "executing" : "reading"), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); } + while (Emu.IsPausedOrReady()) + { + if (cpu) + { + auto state = +cpu->state; + + if (::is_paused(state) && !::is_stopped(state)) + { + thread_ctrl::wait_on(cpu->state, state); + } + else + { + // Temporary until Emulator updates state + std::this_thread::yield(); + } + } + else + { + thread_ctrl::wait_for(1000); + } + } + + Emu.Pause(true); + while (Emu.IsPaused()) { - thread_ctrl::wait(); + if (cpu) + { + auto state = +cpu->state; + + if (::is_paused(state) && !::is_stopped(state)) + { + thread_ctrl::wait_on(cpu->state, state); + } + else + { + // Temporary until Emulator updates state + std::this_thread::yield(); + } + } + else + { + thread_ctrl::wait_for(1000); + } } if (Emu.IsStopped() && !hack_alloc()) @@ -1818,6 +1853,11 @@ static LONG exception_filter(PEXCEPTION_POINTERS pExp) noexcept pExp->ExceptionRecord->ExceptionInformation[0] == 1 ? "writing" : "reading"; fmt::append(msg, "Segfault %s location %p at %p.\n", cause, pExp->ExceptionRecord->ExceptionInformation[1], pExp->ExceptionRecord->ExceptionAddress); + + if (vm::try_get_addr(reinterpret_cast(pExp->ExceptionRecord->ExceptionInformation[1])).second) + { + fmt::append(msg, "Sudo Addr: %p, VM Addr: %p\n", vm::g_sudo_addr, vm::g_base_addr); + } } else { @@ -1999,6 +2039,11 @@ static void signal_handler(int /*sig*/, siginfo_t* info, void* uct) noexcept std::string msg = fmt::format("Segfault %s location %p at %p.\n", cause, info->si_addr, RIP(context)); + if (vm::try_get_addr(info->si_addr).second) + { + fmt::append(msg, "Sudo Addr: %p, VM Addr: %p\n", vm::g_sudo_addr, vm::g_base_addr); + } + append_thread_name(msg); sys_log.fatal("\n%s", msg); diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 37c4a56f4c..52096a2702 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -5,10 +5,6 @@ #include "util/shared_ptr.hpp" #include -#include - -#include "mutex.h" -#include "lockless.h" // Hardware core layout enum class native_core_arrangement : u32 @@ -769,7 +765,7 @@ public: } // Move the context (if movable) - new (static_cast(m_threads + m_count - 1)) Thread(std::string(name) + std::to_string(m_count - 1), std::forward(f)); + new (static_cast(m_threads + m_count - 1)) Thread(std::string(name) + std::to_string(m_count), std::forward(f)); } // Constructor with a function performed before adding more threads diff --git a/Utilities/cfmt.h b/Utilities/cfmt.h index 3b2c94dcbc..9d23c6f759 100644 --- a/Utilities/cfmt.h +++ b/Utilities/cfmt.h @@ -2,7 +2,6 @@ #include "util/types.hpp" #include -#include #include #include "util/asm.hpp" diff --git a/Utilities/cheat_info.cpp b/Utilities/cheat_info.cpp index 42ffb89e52..c46f2089e9 100644 --- a/Utilities/cheat_info.cpp +++ b/Utilities/cheat_info.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "cheat_info.h" -#include "Config.h" #include "StrUtil.h" LOG_CHANNEL(log_cheat, "Cheat"); diff --git a/Utilities/cond.cpp b/Utilities/cond.cpp index af572d191e..425f74d806 100644 --- a/Utilities/cond.cpp +++ b/Utilities/cond.cpp @@ -1,5 +1,4 @@ #include "cond.h" -#include "sync.h" // use constants, increase signal space diff --git a/Utilities/ppu_patch.h b/Utilities/ppu_patch.h index 667242dac5..f88786cf24 100644 --- a/Utilities/ppu_patch.h +++ b/Utilities/ppu_patch.h @@ -2,7 +2,6 @@ #include #include -#include #include // Patch utilities specific to PPU code diff --git a/Utilities/sync.h b/Utilities/sync.h index 7264ba9248..db46d6104f 100644 --- a/Utilities/sync.h +++ b/Utilities/sync.h @@ -12,18 +12,12 @@ #include #include #elif __linux__ -#include #include #include #include #include #include #endif -#include -#include -#include -#include -#include #ifdef _WIN32 DYNAMIC_IMPORT("ntdll.dll", NtWaitForKeyedEvent, NTSTATUS(HANDLE, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout)); @@ -60,6 +54,9 @@ struct futex_waitv }; #endif #else + +#include + enum { FUTEX_PRIVATE_FLAG = 0, diff --git a/Utilities/transactional_storage.h b/Utilities/transactional_storage.h index dda86efe05..55e8aa9f95 100644 --- a/Utilities/transactional_storage.h +++ b/Utilities/transactional_storage.h @@ -1,6 +1,7 @@ #include "util/types.hpp" #include #include +#include "Emu/Cell/timers.hpp" // Thread-safe object pool with garbage collection class universal_pool diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 81e4a92a9b..082ffa3ea8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -38,13 +38,13 @@ jobs: displayName: ccache - bash: | - docker pull --quiet rpcs3/rpcs3-ci-focal:1.9 + docker pull --quiet rpcs3/rpcs3-ci-jammy:1.0 docker run \ -v $(pwd):/rpcs3 \ --env-file .ci/docker.env \ -v $CCACHE_DIR:/root/.ccache \ -v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \ - rpcs3/rpcs3-ci-focal:1.9 \ + rpcs3/rpcs3-ci-jammy:1.0 \ /rpcs3/.ci/build-linux.sh displayName: Docker setup and build @@ -134,7 +134,7 @@ jobs: ARTDIR: $(Build.ArtifactStagingDirectory) QT_VER: '6.7.3' QT_VER_MAIN: '6' - LLVM_COMPILER_VER: '16' + LLVM_COMPILER_VER: '19' pool: vmImage: "macOS-13" @@ -195,7 +195,7 @@ jobs: ARTDIR: $(Build.ArtifactStagingDirectory) QT_VER: '6.7.3' QT_VER_MAIN: '6' - LLVM_COMPILER_VER: '16' + LLVM_COMPILER_VER: '19' pool: vmImage: "macOS-13" diff --git a/buildfiles/msvc/rpcs3_debug.props b/buildfiles/msvc/rpcs3_debug.props index d727f7073a..b843fdf82b 100644 --- a/buildfiles/msvc/rpcs3_debug.props +++ b/buildfiles/msvc/rpcs3_debug.props @@ -14,7 +14,7 @@ LLVM_AVAILABLE;%(PreprocessorDefinitions) - %(AdditionalLibraryDirectories);$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\lib;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\$(Configuration)\lib + %(AdditionalLibraryDirectories);$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\lib;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\$(Configuration)\lib;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\lib %(AdditionalDependencies); LLVMAggressiveInstCombine.lib; LLVMAnalysis.lib; @@ -25,11 +25,13 @@ LLVMBitstreamReader.lib; LLVMBitWriter.lib; LLVMCFGuard.lib; - LLVMCFIVerify.lib; LLVMCodeGen.lib; + LLVMCodeGenData.lib; + LLVMCodeGenTypes.lib; LLVMCore.lib; LLVMCoroutines.lib; LLVMCoverage.lib; + LLVMDebugInfoBTF.lib; LLVMDebugInfoCodeView.lib; LLVMDebuginfod.lib; LLVMDebugInfoDWARF.lib; @@ -38,25 +40,27 @@ LLVMDebugInfoMSF.lib; LLVMDebugInfoPDB.lib; LLVMDemangle.lib; - LLVMDiff.lib; LLVMDlltoolDriver.lib; LLVMDWARFLinker.lib; + LLVMDWARFLinkerClassic.lib; LLVMDWARFLinkerParallel.lib; LLVMDWP.lib; LLVMExecutionEngine.lib; - LLVMExegesis.lib; - LLVMExegesisX86.lib; LLVMExtensions.lib; LLVMFileCheck.lib; + LLVMFrontendDriver.lib; LLVMFrontendHLSL.lib; + LLVMFrontendOffloading.lib; LLVMFrontendOpenACC.lib; LLVMFrontendOpenMP.lib; LLVMFuzzerCLI.lib; LLVMFuzzMutate.lib; LLVMGlobalISel.lib; + LLVMHipStdPar.lib; LLVMInstCombine.lib; LLVMInstrumentation.lib; LLVMIntelJITEvents.lib; + LLVMIntelJITProfiling.lib; LLVMInterfaceStub.lib; LLVMInterpreter.lib; LLVMipo.lib; @@ -67,10 +71,10 @@ LLVMLineEditor.lib; LLVMLinker.lib; LLVMLTO.lib; + LLVMMC.lib; LLVMMCA.lib; LLVMMCDisassembler.lib; LLVMMCJIT.lib; - LLVMMC.lib; LLVMMCParser.lib; LLVMMIRParser.lib; LLVMObjCARCOpts.lib; @@ -78,6 +82,7 @@ LLVMObject.lib; LLVMObjectYAML.lib; LLVMOption.lib; + LLVMOrcDebugging.lib; LLVMOrcJIT.lib; LLVMOrcShared.lib; LLVMOrcTargetProcess.lib; @@ -85,15 +90,18 @@ LLVMProfileData.lib; LLVMRemarks.lib; LLVMRuntimeDyld.lib; + LLVMSandboxIR.lib; LLVMScalarOpts.lib; LLVMSelectionDAG.lib; LLVMSupport.lib; LLVMSymbolize.lib; - LLVMTableGenGlobalISel.lib; LLVMTableGen.lib; + LLVMTableGenBasic.lib; + LLVMTableGenCommon.lib; LLVMTarget.lib; LLVMTargetParser.lib; LLVMTextAPI.lib; + LLVMTextAPIBinaryReader.lib; LLVMTransformUtils.lib; LLVMVectorize.lib; LLVMWindowsDriver.lib; diff --git a/buildfiles/msvc/rpcs3_release.props b/buildfiles/msvc/rpcs3_release.props index c040937a75..98066601c4 100644 --- a/buildfiles/msvc/rpcs3_release.props +++ b/buildfiles/msvc/rpcs3_release.props @@ -15,7 +15,7 @@ true true - %(AdditionalLibraryDirectories);$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\lib;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\$(Configuration)\lib + %(AdditionalLibraryDirectories);$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\lib;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\$(Configuration)\lib;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\lib %(AdditionalDependencies); LLVMAggressiveInstCombine.lib; LLVMAnalysis.lib; @@ -26,11 +26,13 @@ LLVMBitstreamReader.lib; LLVMBitWriter.lib; LLVMCFGuard.lib; - LLVMCFIVerify.lib; LLVMCodeGen.lib; + LLVMCodeGenData.lib; + LLVMCodeGenTypes.lib; LLVMCore.lib; LLVMCoroutines.lib; LLVMCoverage.lib; + LLVMDebugInfoBTF.lib; LLVMDebugInfoCodeView.lib; LLVMDebuginfod.lib; LLVMDebugInfoDWARF.lib; @@ -39,25 +41,27 @@ LLVMDebugInfoMSF.lib; LLVMDebugInfoPDB.lib; LLVMDemangle.lib; - LLVMDiff.lib; LLVMDlltoolDriver.lib; LLVMDWARFLinker.lib; + LLVMDWARFLinkerClassic.lib; LLVMDWARFLinkerParallel.lib; LLVMDWP.lib; LLVMExecutionEngine.lib; - LLVMExegesis.lib; - LLVMExegesisX86.lib; LLVMExtensions.lib; LLVMFileCheck.lib; + LLVMFrontendDriver.lib; LLVMFrontendHLSL.lib; + LLVMFrontendOffloading.lib; LLVMFrontendOpenACC.lib; LLVMFrontendOpenMP.lib; LLVMFuzzerCLI.lib; LLVMFuzzMutate.lib; LLVMGlobalISel.lib; + LLVMHipStdPar.lib; LLVMInstCombine.lib; LLVMInstrumentation.lib; LLVMIntelJITEvents.lib; + LLVMIntelJITProfiling.lib; LLVMInterfaceStub.lib; LLVMInterpreter.lib; LLVMipo.lib; @@ -68,10 +72,10 @@ LLVMLineEditor.lib; LLVMLinker.lib; LLVMLTO.lib; + LLVMMC.lib; LLVMMCA.lib; LLVMMCDisassembler.lib; LLVMMCJIT.lib; - LLVMMC.lib; LLVMMCParser.lib; LLVMMIRParser.lib; LLVMObjCARCOpts.lib; @@ -79,6 +83,7 @@ LLVMObject.lib; LLVMObjectYAML.lib; LLVMOption.lib; + LLVMOrcDebugging.lib; LLVMOrcJIT.lib; LLVMOrcShared.lib; LLVMOrcTargetProcess.lib; @@ -86,15 +91,18 @@ LLVMProfileData.lib; LLVMRemarks.lib; LLVMRuntimeDyld.lib; + LLVMSandboxIR.lib; LLVMScalarOpts.lib; LLVMSelectionDAG.lib; LLVMSupport.lib; LLVMSymbolize.lib; - LLVMTableGenGlobalISel.lib; LLVMTableGen.lib; + LLVMTableGenBasic.lib; + LLVMTableGenCommon.lib; LLVMTarget.lib; LLVMTargetParser.lib; LLVMTextAPI.lib; + LLVMTextAPIBinaryReader.lib; LLVMTransformUtils.lib; LLVMVectorize.lib; LLVMWindowsDriver.lib; diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 2b76f3c5e7..bff3ce2529 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -114,6 +114,8 @@ target_link_libraries(rpcs3 3rdparty::fusion ${ADDITIONAL_LIBS}) +set_target_properties(rpcs3_emu PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) + # Unix display manager if(X11_FOUND) target_link_libraries(rpcs3 PRIVATE X11::X11) diff --git a/rpcs3/Crypto/aes.cpp b/rpcs3/Crypto/aes.cpp index 034e192e2c..61ae4ae85c 100644 --- a/rpcs3/Crypto/aes.cpp +++ b/rpcs3/Crypto/aes.cpp @@ -598,7 +598,10 @@ int aes_setkey_dec( aes_context *ctx, const unsigned char *key, unsigned int key *RK++ = *SK++; *RK++ = *SK++; +#if defined(__SSE2__) || defined(_M_X64) done: +#endif + // Wipe the stack buffer clean std::fill_n(reinterpret_cast(&cty), sizeof(cty), 0); diff --git a/rpcs3/Crypto/ec.cpp b/rpcs3/Crypto/ec.cpp index a4b11d7c3d..efef91228c 100644 --- a/rpcs3/Crypto/ec.cpp +++ b/rpcs3/Crypto/ec.cpp @@ -2,7 +2,7 @@ // Licensed under the terms of the GNU GPL, version 2 // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt -#include "utils.h" +#include "util/types.hpp" #include static inline int bn_compare(u8* a, u8* b, u32 n) diff --git a/rpcs3/Crypto/lz.cpp b/rpcs3/Crypto/lz.cpp index da2b05c361..30e201c5f5 100644 --- a/rpcs3/Crypto/lz.cpp +++ b/rpcs3/Crypto/lz.cpp @@ -2,6 +2,7 @@ // Licensed under the terms of the GNU GPL, version 2.0 or later versions. // http://www.gnu.org/licenses/gpl-2.0.txt +#include #include "lz.h" void decode_range(unsigned int *range, unsigned int *code, unsigned char **src) diff --git a/rpcs3/Crypto/lz.h b/rpcs3/Crypto/lz.h index b8ad0f3a0e..5ad68ea66e 100644 --- a/rpcs3/Crypto/lz.h +++ b/rpcs3/Crypto/lz.h @@ -6,8 +6,6 @@ // Reverse-engineered custom Lempel–Ziv–Markov based compression. -#include - void decode_range(unsigned int *range, unsigned int *code, unsigned char **src); int decode_bit(unsigned int *range, unsigned int *code, int *index, unsigned char **src, unsigned char *c); int decode_number(unsigned char *ptr, int index, int *bit_flag, unsigned int *range, unsigned int *code, unsigned char **src); diff --git a/rpcs3/Crypto/unedat.cpp b/rpcs3/Crypto/unedat.cpp index 0192265298..d7eacd5ca1 100644 --- a/rpcs3/Crypto/unedat.cpp +++ b/rpcs3/Crypto/unedat.cpp @@ -4,6 +4,7 @@ #include "sha1.h" #include "lz.h" #include "ec.h" +#include "utils.h" #include "Emu/system_utils.hpp" diff --git a/rpcs3/Crypto/unedat.h b/rpcs3/Crypto/unedat.h index fa26da2f42..31fce60d4e 100644 --- a/rpcs3/Crypto/unedat.h +++ b/rpcs3/Crypto/unedat.h @@ -1,9 +1,6 @@ #pragma once #include - -#include "utils.h" - #include "Utilities/File.h" constexpr u32 SDAT_FLAG = 0x01000000; diff --git a/rpcs3/Crypto/unpkg.cpp b/rpcs3/Crypto/unpkg.cpp index 5bd04e7e49..baf612657d 100644 --- a/rpcs3/Crypto/unpkg.cpp +++ b/rpcs3/Crypto/unpkg.cpp @@ -5,7 +5,6 @@ #include "util/logs.hpp" #include "Utilities/StrUtil.h" #include "Utilities/Thread.h" -#include "Utilities/mutex.h" #include "Emu/System.h" #include "Emu/system_utils.hpp" #include "Emu/VFS.h" diff --git a/rpcs3/Crypto/unself.cpp b/rpcs3/Crypto/unself.cpp index e6a112e921..66000c4f12 100644 --- a/rpcs3/Crypto/unself.cpp +++ b/rpcs3/Crypto/unself.cpp @@ -1,14 +1,11 @@ #include "stdafx.h" #include "aes.h" -#include "utils.h" #include "unself.h" -#include "Emu/VFS.h" +#include "util/asm.hpp" #include "Emu/System.h" #include "Emu/system_utils.hpp" #include "Crypto/unzip.h" -#include - inline u8 Read8(const fs::file& f) { u8 ret; diff --git a/rpcs3/Crypto/utils.h b/rpcs3/Crypto/utils.h index b7e4634f06..25cdeb7193 100644 --- a/rpcs3/Crypto/utils.h +++ b/rpcs3/Crypto/utils.h @@ -5,7 +5,6 @@ // http://www.gnu.org/licenses/gpl-2.0.txt #include "util/types.hpp" -#include "util/asm.hpp" #include diff --git a/rpcs3/Emu/Audio/AudioBackend.cpp b/rpcs3/Emu/Audio/AudioBackend.cpp index 73c017eef9..e0d96c8c2b 100644 --- a/rpcs3/Emu/Audio/AudioBackend.cpp +++ b/rpcs3/Emu/Audio/AudioBackend.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "AudioBackend.h" -#include "Emu/system_config.h" #include "Emu/IdManager.h" #include "Emu//Cell/Modules/cellAudioOut.h" diff --git a/rpcs3/Emu/Audio/Cubeb/CubebBackend.h b/rpcs3/Emu/Audio/Cubeb/CubebBackend.h index 93456adc53..1f230ec238 100644 --- a/rpcs3/Emu/Audio/Cubeb/CubebBackend.h +++ b/rpcs3/Emu/Audio/Cubeb/CubebBackend.h @@ -1,7 +1,5 @@ #pragma once -#include -#include "Utilities/mutex.h" #include "util/atomic.hpp" #include "Emu/Audio/AudioBackend.h" diff --git a/rpcs3/Emu/Audio/FAudio/FAudioBackend.cpp b/rpcs3/Emu/Audio/FAudio/FAudioBackend.cpp index 58cd189e77..d0e5728b27 100644 --- a/rpcs3/Emu/Audio/FAudio/FAudioBackend.cpp +++ b/rpcs3/Emu/Audio/FAudio/FAudioBackend.cpp @@ -4,7 +4,6 @@ #include "stdafx.h" #include "FAudioBackend.h" -#include "Emu/system_config.h" #include "Emu/System.h" #include "Emu/Audio/audio_device_enumerator.h" #include "Utilities/StrUtil.h" diff --git a/rpcs3/Emu/Audio/FAudio/FAudioBackend.h b/rpcs3/Emu/Audio/FAudio/FAudioBackend.h index 3f3d6e2ef8..734f56156e 100644 --- a/rpcs3/Emu/Audio/FAudio/FAudioBackend.h +++ b/rpcs3/Emu/Audio/FAudio/FAudioBackend.h @@ -4,8 +4,6 @@ #error "FAudio support disabled but still being built." #endif -#include -#include "Utilities/mutex.h" #include "Emu/Audio/AudioBackend.h" #include "FAudio.h" diff --git a/rpcs3/Emu/Audio/FAudio/faudio_enumerator.cpp b/rpcs3/Emu/Audio/FAudio/faudio_enumerator.cpp index 7308f54b19..fdbfd2a6af 100644 --- a/rpcs3/Emu/Audio/FAudio/faudio_enumerator.cpp +++ b/rpcs3/Emu/Audio/FAudio/faudio_enumerator.cpp @@ -3,7 +3,6 @@ #endif #include "Emu/Audio/FAudio/faudio_enumerator.h" -#include #include #include "Utilities/StrUtil.h" #include "util/logs.hpp" diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h index e03cf6cc51..0312e7f633 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h @@ -4,8 +4,6 @@ #error "XAudio2 can only be built on Windows." #endif -#include -#include "Utilities/mutex.h" #include "Emu/Audio/AudioBackend.h" #include diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index f5baa5f068..7d7353cc5a 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -462,6 +462,7 @@ target_sources(rpcs3_emu PRIVATE NP/rpcn_countries.cpp NP/upnp_config.cpp NP/upnp_handler.cpp + NP/ip_address.cpp ) # Memory @@ -478,6 +479,7 @@ target_sources(rpcs3_emu PRIVATE RSX/Common/TextureUtils.cpp RSX/Common/texture_cache.cpp RSX/Core/RSXContext.cpp + RSX/Core/RSXDisplay.cpp RSX/Core/RSXDrawCommands.cpp RSX/gcm_enums.cpp RSX/gcm_printing.cpp @@ -492,6 +494,7 @@ target_sources(rpcs3_emu PRIVATE RSX/GL/GLPipelineCompiler.cpp RSX/GL/GLPresent.cpp RSX/GL/GLRenderTargets.cpp + RSX/GL/GLResolveHelper.cpp RSX/GL/GLShaderInterpreter.cpp RSX/GL/GLTexture.cpp RSX/GL/GLTextureCache.cpp diff --git a/rpcs3/Emu/CPU/CPUTranslator.cpp b/rpcs3/Emu/CPU/CPUTranslator.cpp index 7c9550ead5..f6893358ff 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.cpp +++ b/rpcs3/Emu/CPU/CPUTranslator.cpp @@ -3,7 +3,6 @@ #include "CPUTranslator.h" #include "util/v128.hpp" -#include "util/simd.hpp" #include "util/logs.hpp" LOG_CHANNEL(llvm_log, "LLVM"); diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index 56612b8279..7bab6b335a 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -21,7 +21,6 @@ #include "llvm/IR/Module.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Support/KnownBits.h" -#include "llvm/Support/ModRef.h" #include "llvm/Analysis/ConstantFolding.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/IntrinsicsX86.h" @@ -37,7 +36,6 @@ #include "util/types.hpp" #include "util/sysinfo.hpp" #include "Utilities/StrFmt.h" -#include "Utilities/BitField.h" #include "Utilities/JIT.h" #include "util/v128.hpp" diff --git a/rpcs3/Emu/Cell/Modules/HLE_PATCHES.cpp b/rpcs3/Emu/Cell/Modules/HLE_PATCHES.cpp index 0d5fa62f63..8b3ad0b63e 100644 --- a/rpcs3/Emu/Cell/Modules/HLE_PATCHES.cpp +++ b/rpcs3/Emu/Cell/Modules/HLE_PATCHES.cpp @@ -4,7 +4,6 @@ #include "Utilities/Thread.h" #include "Emu/Cell/lv2/sys_spu.h" -#include "Emu/Cell/lv2/sys_sync.h" #include diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.cpp b/rpcs3/Emu/Cell/Modules/cellAudio.cpp index ceeb02da63..379c17f112 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudio.cpp @@ -3,9 +3,9 @@ #include "Emu/system_config.h" #include "Emu/Audio/audio_utils.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/timers.hpp" #include "Emu/Cell/lv2/sys_process.h" #include "Emu/Cell/lv2/sys_event.h" -#include "Emu/Cell/Modules/cellAudioOut.h" #include "cellAudio.h" #include "util/video_provider.h" @@ -704,7 +704,7 @@ void cell_audio_thread::operator()() thread_ctrl::scoped_priority high_prio(+1); - while (Emu.IsPaused()) + while (Emu.IsPausedOrReady()) { thread_ctrl::wait_for(5000); } diff --git a/rpcs3/Emu/Cell/Modules/cellAudioIn.h b/rpcs3/Emu/Cell/Modules/cellAudioIn.h index a630ce47cd..dc2ca6d130 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudioIn.h +++ b/rpcs3/Emu/Cell/Modules/cellAudioIn.h @@ -1,7 +1,6 @@ #pragma once #include "util/types.hpp" -#include "util/endian.hpp" // Error codes enum CellAudioInError : u32 diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index 95cf0a56f9..6c0abcc160 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -6,6 +6,7 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_event.h" #include "Emu/IdManager.h" +#include "Emu/Cell/timers.hpp" #include @@ -1640,14 +1641,14 @@ void camera_context::operator()() while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) { // send ATTACH event - if (init && is_attached_dirty && !Emu.IsPaused()) + if (init && is_attached_dirty && !Emu.IsPausedOrReady()) { send_attach_state(is_attached); } const s32 fps = info.framerate; - if (!init || !fps || Emu.IsPaused() || g_cfg.io.camera == camera_handler::null) + if (!init || !fps || Emu.IsPausedOrReady() || g_cfg.io.camera == camera_handler::null) { thread_ctrl::wait_for(1000); // hack continue; diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.h b/rpcs3/Emu/Cell/Modules/cellCamera.h index 28e8a3152c..56b8334c44 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.h +++ b/rpcs3/Emu/Cell/Modules/cellCamera.h @@ -1,9 +1,9 @@ #pragma once -#include "Utilities/Timer.h" -#include "Emu/Cell/lv2/sys_memory.h" #include "Utilities/Thread.h" #include "Emu/Io/camera_handler_base.h" +#include "Emu/Memory/vm_ptr.h" +#include "Utilities/mutex.h" #include diff --git a/rpcs3/Emu/Cell/Modules/cellDmux.cpp b/rpcs3/Emu/Cell/Modules/cellDmux.cpp index 5c94463ea3..d7f6f84f3f 100644 --- a/rpcs3/Emu/Cell/Modules/cellDmux.cpp +++ b/rpcs3/Emu/Cell/Modules/cellDmux.cpp @@ -9,6 +9,8 @@ #include "util/asm.hpp" +#include + LOG_CHANNEL(cellDmux); template <> diff --git a/rpcs3/Emu/Cell/Modules/cellDmux.h b/rpcs3/Emu/Cell/Modules/cellDmux.h index 884a3eb050..1767165283 100644 --- a/rpcs3/Emu/Cell/Modules/cellDmux.h +++ b/rpcs3/Emu/Cell/Modules/cellDmux.h @@ -1,6 +1,7 @@ #pragma once #include "Emu/Memory/vm_ptr.h" +#include "cellPamf.h" // Error Codes enum CellDmuxError :u32 diff --git a/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp b/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp index a7b24c3973..70162d4031 100644 --- a/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp +++ b/rpcs3/Emu/Cell/Modules/cellDmuxPamf.cpp @@ -2,7 +2,6 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/IdManager.h" -#include "cellPamf.h" #include "cellDmux.h" #include "cellDmuxPamf.h" diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index 6534d07677..17e190898d 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -6,6 +6,7 @@ #include "Emu/IdManager.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/timers.hpp" #include "Emu/Cell/lv2/sys_fs.h" #include "Emu/Cell/lv2/sys_sync.h" diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 8475b57b04..f051b67131 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -3,6 +3,7 @@ #include "cellCamera.h" #include "Emu/Cell/lv2/sys_event.h" +#include "Emu/Cell/lv2/sys_memory.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/timers.hpp" #include "Emu/Io/MouseHandler.h" @@ -247,7 +248,7 @@ public: u32 hue = 0; // Tracking hue of the motion controller f32 distance_mm{3000.0f}; // Distance from the camera in mm f32 radius{5.0f}; // Radius of the sphere in camera pixels - bool radius_valid = true; // If the radius and distance of the sphere was computed. + bool radius_valid = false; // If the radius and distance of the sphere was computed. Also used for visibility. bool is_calibrating{false}; // Whether or not we are currently calibrating u64 calibration_start_us{0}; // The start timestamp of the calibration in microseconds @@ -321,13 +322,28 @@ public: void update_connections() { + connected_controllers = 0; + + const auto update_connection = [this](u32 i, bool connected) + { + if (connected) + { + connected_controllers++; + controllers[i].status = CELL_GEM_STATUS_READY; + controllers[i].port = port_num(i); + } + else + { + controllers[i].status = CELL_GEM_STATUS_DISCONNECTED; + controllers[i].port = 0; + } + }; + switch (g_cfg.io.move) { case move_handler::real: case move_handler::fake: { - connected_controllers = 0; - std::lock_guard lock(pad::g_pad_mutex); const auto handler = pad::get_pad_thread(true); if (!handler) break; @@ -335,51 +351,41 @@ public: for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++) { const auto& pad = ::at32(handler->GetPads(), pad_num(i)); - const bool connected = (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && i < attribute.max_connect); + const bool connected = pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && i < attribute.max_connect; const bool is_real_move = g_cfg.io.move != move_handler::real || pad->m_pad_handler == pad_handler::move; - if (connected && is_real_move) - { - connected_controllers++; - controllers[i].status = CELL_GEM_STATUS_READY; - controllers[i].port = port_num(i); - } - else - { - controllers[i].status = CELL_GEM_STATUS_DISCONNECTED; - controllers[i].port = 0; - } + update_connection(i, connected && is_real_move); } break; } + case move_handler::mouse: case move_handler::raw_mouse: { - connected_controllers = 0; - auto& handler = g_fxo->get(); std::lock_guard mouse_lock(handler.mutex); - const MouseInfo& info = handler.GetInfo(); for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++) { - const bool connected = i < attribute.max_connect && info.status[i] == CELL_MOUSE_STATUS_CONNECTED; - - if (connected) - { - connected_controllers++; - controllers[i].status = CELL_GEM_STATUS_READY; - controllers[i].port = port_num(i); - } - else - { - controllers[i].status = CELL_GEM_STATUS_DISCONNECTED; - controllers[i].port = 0; - } + update_connection(i, i < attribute.max_connect && info.status[i] == CELL_MOUSE_STATUS_CONNECTED); } break; } - default: +#ifdef HAVE_LIBEVDEV + case move_handler::gun: + { + gun_thread& gun = g_fxo->get(); + std::scoped_lock lock(gun.handler.mutex); + gun.num_devices = gun.handler.init() ? gun.handler.get_num_guns() : 0; + + for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++) + { + update_connection(i, i < attribute.max_connect && i < gun.num_devices); + } + break; + } +#endif + case move_handler::null: { break; } @@ -1598,13 +1604,17 @@ static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_contro rsx::overlays::set_cursor(rsx::overlays::cursor_offset::cell_gem + gem_num, x, y, color, 2'000'000, false); } -static inline void pos_to_gem_image_state(u32 gem_num, const gem_config::gem_controller& controller, vm::ptr& gem_image_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max) +static inline void pos_to_gem_image_state(u32 gem_num, gem_config::gem_controller& controller, vm::ptr& gem_image_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max) { const auto& shared_data = g_fxo->get(); if (x_max <= 0) x_max = shared_data.width; if (y_max <= 0) y_max = shared_data.height; + // Move the cursor out of the screen if we're at the screen border (Time Crisis 4 needs this) + if (x_pos <= 0) x_pos -= x_max / 10; else if (x_pos >= x_max) x_pos += x_max / 10; + if (y_pos <= 0) y_pos -= y_max / 10; else if (y_pos >= y_max) y_pos += y_max / 10; + const f32 scaling_width = x_max / static_cast(shared_data.width); const f32 scaling_height = y_max / static_cast(shared_data.height); const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius; @@ -1629,6 +1639,13 @@ static inline void pos_to_gem_image_state(u32 gem_num, const gem_config::gem_con gem_image_state->projectionx = camera_x / controller.distance_mm; gem_image_state->projectiony = camera_y / controller.distance_mm; + // Update visibility for fake handlers + if (g_cfg.io.move != move_handler::real) + { + // Let's say the sphere is not visible if the position is at the edge of the screen + controller.radius_valid = x_pos > 0 && x_pos < x_max && y_pos > 0 && y_pos < y_max; + } + if (g_cfg.io.show_move_cursor) { draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max); @@ -1647,6 +1664,10 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con if (x_max <= 0) x_max = shared_data.width; if (y_max <= 0) y_max = shared_data.height; + // Move the cursor out of the screen if we're at the screen border (Time Crisis 4 needs this) + if (x_pos <= 0) x_pos -= x_max / 10; else if (x_pos >= x_max) x_pos += x_max / 10; + if (y_pos <= 0) y_pos -= y_max / 10; else if (y_pos >= y_max) y_pos += y_max / 10; + const f32 scaling_width = x_max / static_cast(shared_data.width); const f32 scaling_height = y_max / static_cast(shared_data.height); const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius; @@ -1712,6 +1733,13 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con gem_state->quat[3] = q_w; } + // Update visibility for fake handlers + if (g_cfg.io.move != move_handler::real) + { + // Let's say the sphere is not visible if the position is at the edge of the screen + controller.radius_valid = x_pos > 0 && x_pos < x_max && y_pos > 0 && y_pos < y_max; + } + if (g_cfg.io.show_move_cursor) { draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max); @@ -1730,7 +1758,7 @@ extern bool is_input_allowed(); * Unavoidably buttons conflict with DS3 mappings, which is problematic for some games. * \param gem_num gem index to use * \param digital_buttons Bitmask filled with CELL_GEM_CTRL_* values - * \param analog_t Analog value of Move's Trigger. Currently mapped to R2. + * \param analog_t Analog value of Move's Trigger. * \return true on success, false if controller is disconnected */ static void ds3_input_to_pad(const u32 gem_num, be_t& digital_buttons, be_t& analog_t) @@ -1810,22 +1838,17 @@ static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr& const auto& cfg = ::at32(g_cfg_gem_fake.players, gem_num); cfg->handle_input(pad, true, [&](gem_btn btn, pad_button /*pad_btn*/, u16 value, bool pressed, bool& /*abort*/) - { - if (!pressed) - return; + { + if (!pressed) + return; - switch (btn) - { - case gem_btn::x_axis: - x_pos = value; - break; - case gem_btn::y_axis: - y_pos = value; - break; - default: - break; - } - }); + switch (btn) + { + case gem_btn::x_axis: x_pos = value; break; + case gem_btn::y_axis: y_pos = value; break; + default: break; + } + }); } template @@ -2095,7 +2118,7 @@ static bool mouse_input_to_pad(u32 mouse_no, be_t& digital_buttons, be_t& digital_buttons, be_t& if (gun.handler.get_button(gem_no, gun_button::btn_6) == 1) digital_buttons |= CELL_GEM_CTRL_SQUARE; - analog_t = gun.handler.get_button(gem_no, gun_button::btn_left) ? 0xFFFF : 0; + analog_t = gun.handler.get_button(gem_no, gun_button::btn_left) ? 255 : 0; return true; } @@ -2657,6 +2680,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag cellGem.warning("cellGemGetImageState(gem_num=%d, image_state=&0x%x)", gem_num, gem_image_state); auto& gem = g_fxo->get(); + std::scoped_lock lock(gem.mtx); if (!gem.state) { @@ -2677,10 +2701,6 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag gem_image_state->frame_timestamp = shared_data.frame_timestamp_us.load(); gem_image_state->timestamp = gem_image_state->frame_timestamp + 10; - gem_image_state->r = controller.radius; // Radius in camera pixels - gem_image_state->distance = controller.distance_mm; - gem_image_state->visible = gem.is_controller_ready(gem_num); - gem_image_state->r_valid = controller.radius_valid; switch (g_cfg.io.move) { @@ -2702,6 +2722,11 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag case move_handler::null: fmt::throw_exception("Unreachable"); } + + gem_image_state->r = controller.radius; // Radius in camera pixels + gem_image_state->distance = controller.distance_mm; + gem_image_state->visible = controller.radius_valid && gem.is_controller_ready(gem_num); + gem_image_state->r_valid = controller.radius_valid; } return CELL_OK; @@ -3234,7 +3259,35 @@ error_code cellGemPrepareCamera(s32 max_exposure, f32 image_quality) max_exposure = std::clamp(max_exposure, static_cast(CELL_GEM_MIN_CAMERA_EXPOSURE), static_cast(CELL_GEM_MAX_CAMERA_EXPOSURE)); image_quality = std::clamp(image_quality, 0.0f, 1.0f); - // TODO: prepare camera + // TODO: prepare camera properly + + extern error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr arg1, vm::ptr arg2); + extern error_code cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2); + extern error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr info); + + vm::var info = vm::make_var({}); + vm::var arg1 = vm::make_var({}); + vm::var arg2 = vm::make_var({}); + + cellCameraGetAttribute(0, 0x3e6, arg1, arg2); + cellCameraSetAttribute(0, 0x3e6, 0x3e, *arg2 | 0x80); + cellCameraGetBufferInfoEx(0, info); + + if (info->width == 640) + { + // Disable some features + cellCameraSetAttribute(0, CELL_CAMERA_AGC, 0, 0); + cellCameraSetAttribute(0, CELL_CAMERA_AWB, 0, 0); + cellCameraSetAttribute(0, CELL_CAMERA_AEC, 0, 0); + cellCameraSetAttribute(0, CELL_CAMERA_GAMMA, 0, 0); + cellCameraSetAttribute(0, CELL_CAMERA_PIXELOUTLIERFILTER, 0, 0); + + // Set new values for others + cellCameraSetAttribute(0, CELL_CAMERA_GREENGAIN, 96, 0); + cellCameraSetAttribute(0, CELL_CAMERA_REDBLUEGAIN, 64, 96); + cellCameraSetAttribute(0, CELL_CAMERA_GAIN, 0, 0); // TODO + cellCameraSetAttribute(0, CELL_CAMERA_EXPOSURE, 0, 0); // TODO + } return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellGifDec.cpp b/rpcs3/Emu/Cell/Modules/cellGifDec.cpp index 80242686cb..9a49e03ec3 100644 --- a/rpcs3/Emu/Cell/Modules/cellGifDec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGifDec.cpp @@ -9,8 +9,6 @@ #include "Emu/Cell/lv2/sys_fs.h" #include "cellGifDec.h" -#include "util/asm.hpp" - LOG_CHANNEL(cellGifDec); // Temporarily diff --git a/rpcs3/Emu/Cell/Modules/cellJpgDec.cpp b/rpcs3/Emu/Cell/Modules/cellJpgDec.cpp index 124531826d..be72ce4294 100644 --- a/rpcs3/Emu/Cell/Modules/cellJpgDec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellJpgDec.cpp @@ -9,8 +9,6 @@ #include "Emu/Cell/lv2/sys_fs.h" #include "cellJpgDec.h" -#include "util/asm.hpp" - LOG_CHANNEL(cellJpgDec); // Temporarily diff --git a/rpcs3/Emu/Cell/Modules/cellKey2char.cpp b/rpcs3/Emu/Cell/Modules/cellKey2char.cpp index bbe7e186fe..ba09a702ba 100644 --- a/rpcs3/Emu/Cell/Modules/cellKey2char.cpp +++ b/rpcs3/Emu/Cell/Modules/cellKey2char.cpp @@ -1,6 +1,6 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" -#include "cellKb.h" +#include "Emu/Io/Keyboard.h" LOG_CHANNEL(cellKey2char); diff --git a/rpcs3/Emu/Cell/Modules/cellL10n.cpp b/rpcs3/Emu/Cell/Modules/cellL10n.cpp index 01df4988b8..6b3ffc4448 100644 --- a/rpcs3/Emu/Cell/Modules/cellL10n.cpp +++ b/rpcs3/Emu/Cell/Modules/cellL10n.cpp @@ -16,8 +16,6 @@ typedef const char *HostCode; #include "cellL10n.h" -#include "util/asm.hpp" - LOG_CHANNEL(cellL10n); // Translate code id to code name. some codepage may has another name. @@ -56,9 +54,7 @@ bool _L10nCodeParse(s32 code, HostCode& retCode) case L10N_CODEPAGE_866: retCode = 866; return true; case L10N_CODEPAGE_932: retCode = 932; return true; case L10N_CODEPAGE_936: retCode = 936; return true; // GBK - case L10N_GBK: retCode = 936; return true; case L10N_CODEPAGE_949: retCode = 949; return true; // UHC - case L10N_UHC: retCode = 949; return true; // UHC case L10N_CODEPAGE_950: retCode = 950; return true; case L10N_CODEPAGE_1251: retCode = 1251; return true; // CYRL case L10N_CODEPAGE_1252: retCode = 1252; return true; // ANSI @@ -66,14 +62,11 @@ bool _L10nCodeParse(s32 code, HostCode& retCode) case L10N_EUC_JP: retCode = 51932; return true; case L10N_EUC_KR: retCode = 51949; return true; case L10N_ISO_2022_JP: retCode = 50222; return true; - case L10N_JIS: retCode = 50222; return true; // Maybe 708/720/864/1256/10004/20420/28596/ case L10N_ARIB: retCode = 20420; return true; // TODO: think that should be ARABIC. case L10N_HZ: retCode = 52936; return true; case L10N_GB18030: retCode = 54936; return true; case L10N_RIS_506: retCode = 932; return true; // MS_KANJI, TODO: Code page - case L10N_SHIFT_JIS: retCode = 932; return true; // SJIS - case L10N_MUSIC_SHIFT_JIS: retCode = 932; return true; // MSJIS // These are only supported with FW 3.10 and above case L10N_CODEPAGE_852: retCode = 852; return true; case L10N_CODEPAGE_1250: retCode = 1250; return true; // EE @@ -89,7 +82,6 @@ bool _L10nCodeParse(s32 code, HostCode& retCode) case L10N_CODEPAGE_861: retCode = 861; return true; case L10N_CODEPAGE_865: retCode = 865; return true; case L10N_CODEPAGE_869: retCode = 869; return true; - case L10N_BIG5: retCode = 950; return true; // Codepage 950 default: return false; } #else @@ -123,24 +115,18 @@ bool _L10nCodeParse(s32 code, HostCode& retCode) case L10N_CODEPAGE_866: retCode = "CP866"; return true; case L10N_CODEPAGE_932: retCode = "CP932"; return true; case L10N_CODEPAGE_936: retCode = "CP936"; return true; - case L10N_GBK: retCode = "CP936"; return true; case L10N_CODEPAGE_949: retCode = "CP949"; return true; - case L10N_UHC: retCode = "CP949"; return true; case L10N_CODEPAGE_950: retCode = "CP950"; return true; - case L10N_BIG5: retCode = "CP950"; return true; // BIG5 = CodePage 950 case L10N_CODEPAGE_1251: retCode = "CP1251"; return true; // CYRL case L10N_CODEPAGE_1252: retCode = "CP1252"; return true; // ANSI case L10N_EUC_CN: retCode = "EUC-CN"; return true; // GB2312 case L10N_EUC_JP: retCode = "EUC-JP"; return true; case L10N_EUC_KR: retCode = "EUC-KR"; return true; case L10N_ISO_2022_JP: retCode = "ISO-2022-JP"; return true; - case L10N_JIS: retCode = "ISO-2022-JP"; return true; case L10N_ARIB: retCode = "ARABIC"; return true; // TODO: think that should be ARABIC. case L10N_HZ: retCode = "HZ"; return true; case L10N_GB18030: retCode = "GB18030"; return true; case L10N_RIS_506: retCode = "Shift_JIS"; return true; // MS_KANJI - case L10N_SHIFT_JIS: retCode = "Shift_JIS"; return true; // CP932 for Microsoft - case L10N_MUSIC_SHIFT_JIS: retCode = "Shift_JIS"; return true; // MusicShiftJIS // These are only supported with FW 3.10 and below case L10N_CODEPAGE_852: retCode = "CP852"; return true; case L10N_CODEPAGE_1250: retCode = "CP1250"; return true; // EE @@ -164,41 +150,36 @@ bool _L10nCodeParse(s32 code, HostCode& retCode) #ifdef _WIN32 // Use code page to transform std::string to std::wstring. -s32 _OEM2Wide(HostCode oem_code, const std::string& src, std::wstring& dst) +s32 _OEM2Wide(HostCode oem_code, std::string_view src, std::wstring& dst) { - //Such length returned should include the '\0' character. - const s32 length = MultiByteToWideChar(oem_code, 0, src.c_str(), -1, nullptr, 0); - wchar_t *store = new wchar_t[length](); + // Such length returned should include the '\0' character. + const s32 length = MultiByteToWideChar(oem_code, 0, src.data(), -1, nullptr, 0); + std::vector store(length); - MultiByteToWideChar(oem_code, 0, src.c_str(), -1, static_cast(store), length); - dst = std::wstring(store); - - delete[] store; - store = nullptr; + MultiByteToWideChar(oem_code, 0, src.data(), -1, static_cast(store.data()), length); + dst = std::wstring(store.data()); return length - 1; } // Use Code page to transform std::wstring to std::string. -s32 _Wide2OEM(HostCode oem_code, const std::wstring& src, std::string& dst) +s32 _Wide2OEM(HostCode oem_code, std::wstring_view src, std::string& dst) { //Such length returned should include the '\0' character. - const s32 length = WideCharToMultiByte(oem_code, 0, src.c_str(), -1, nullptr, 0, nullptr, nullptr); - char *store = new char[length](); + const s32 length = WideCharToMultiByte(oem_code, 0, src.data(), -1, nullptr, 0, nullptr, nullptr); + std::vector store(length); - WideCharToMultiByte(oem_code, 0, src.c_str(), -1, store, length, nullptr, nullptr); - dst = std::string(store); - - delete[] store; - store = nullptr; + WideCharToMultiByte(oem_code, 0, src.data(), -1, store.data(), length, nullptr, nullptr); + dst = std::string(store.data()); return length - 1; } // Convert Codepage to Codepage (all char*) -std::string _OemToOem(HostCode src_code, HostCode dst_code, const std::string& str) +std::string _OemToOem(HostCode src_code, HostCode dst_code, std::string_view str) { - std::wstring wide; std::string result; + std::wstring wide; + std::string result; _OEM2Wide(src_code, str, wide); _Wide2OEM(dst_code, wide, result); return result; @@ -209,21 +190,23 @@ std::string _OemToOem(HostCode src_code, HostCode dst_code, const std::string& s s32 _ConvertStr(s32 src_code, const void *src, s32 src_len, s32 dst_code, void *dst, s32 *dst_len, [[maybe_unused]] bool allowIncomplete) { HostCode srcCode = 0, dstCode = 0; //OEM code pages - bool src_page_converted = _L10nCodeParse(src_code, srcCode); //Check if code is in list. - bool dst_page_converted = _L10nCodeParse(dst_code, dstCode); + const bool src_page_converted = _L10nCodeParse(src_code, srcCode); //Check if code is in list. + const bool dst_page_converted = _L10nCodeParse(dst_code, dstCode); - if (((!src_page_converted) && (srcCode == 0)) - || ((!dst_page_converted) && (dstCode == 0))) + if (((!src_page_converted) && (srcCode == 0)) || + ((!dst_page_converted) && (dstCode == 0))) + { return ConverterUnknown; + } #ifdef _WIN32 - const std::string wrapped_source = std::string(static_cast(src), src_len); + const std::string_view wrapped_source = std::string_view(static_cast(src), src_len); const std::string target = _OemToOem(srcCode, dstCode, wrapped_source); - if (dst != nullptr) + if (dst) { if (target.length() > static_cast(*dst_len)) return DSTExhausted; - memcpy(dst, target.c_str(), target.length()); + std::memcpy(dst, target.c_str(), target.length()); } *dst_len = ::narrow(target.size()); @@ -232,7 +215,7 @@ s32 _ConvertStr(s32 src_code, const void *src, s32 src_len, s32 dst_code, void * s32 retValue = ConversionOK; iconv_t ict = iconv_open(dstCode, srcCode); usz srcLen = src_len; - if (dst != NULL) + if (dst) { usz dstLen = *dst_len; usz ictd = iconv(ict, utils::bless(&src), &srcLen, utils::bless(&dst), &dstLen); @@ -282,7 +265,7 @@ s32 _ConvertStr(s32 src_code, const void *src, s32 src_len, s32 dst_code, void * #endif } -s32 _L10nConvertStr(s32 src_code, vm::cptr src, vm::cptr src_len, s32 dst_code, vm::ptr dst, vm::ptr dst_len) +s32 _L10nConvertStr(s32 src_code, vm::cptr src, vm::cptr src_len, s32 dst_code, vm::ptr dst, vm::ptr dst_len) { s32 dstLen = *dst_len; s32 result = _ConvertStr(src_code, src.get_ptr(), *src_len, dst_code, dst ? dst.get_ptr() : nullptr, &dstLen, false); @@ -290,7 +273,7 @@ s32 _L10nConvertStr(s32 src_code, vm::cptr src, vm::cptr src_len, s32 return result; } -s32 _L10nConvertChar(s32 src_code, const void *src, s32 src_len, s32 dst_code, vm::ptr dst, vm::ptr dst_len) +s32 _L10nConvertChar(s32 src_code, const void *src, u32 src_len, s32 dst_code, vm::ptr dst, vm::ptr dst_len) { s32 dstLen = 0x7FFFFFFF; s32 result = _ConvertStr(src_code, src, src_len, dst_code, dst.get_ptr(), &dstLen, true); @@ -298,28 +281,32 @@ s32 _L10nConvertChar(s32 src_code, const void *src, s32 src_len, s32 dst_code, v return result; } -s32 _L10nConvertCharNoResult(s32 src_code, const void *src, s32 src_len, s32 dst_code, vm::ptr dst) -{ - s32 dstLen = 0x7FFFFFFF; - [[maybe_unused]] s32 result = _ConvertStr(src_code, src, src_len, dst_code, dst.get_ptr(), &dstLen, true); - return dstLen; -} - s32 UCS2toEUCJP() { cellL10n.todo("UCS2toEUCJP()"); return 0; } -s32 l10n_convert() +s32 l10n_convert(s32 cd, vm::cptr src, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("l10n_convert()"); + cellL10n.todo("l10n_convert(cd=0x%x, src=*0x%x, dst=*0x%x, dst_len=*0x%x)", cd, src, dst, dst_len); return 0; } -s32 UCS2toUTF32() +s32 UCS2toUTF32(u16 ucs2, vm::ptr utf32) { - cellL10n.todo("UCS2toUTF32()"); + cellL10n.notice("UCS2toUTF32(ucs2=0x%x, utf32=*0x%x)", ucs2, utf32); + + const s32 sucs2 = static_cast(ucs2); + + if ((sucs2 & UTF16_SURROGATES_MASK1) != UTF16_HIGH_SURROGATES) + { + ensure(!!utf32); // Not actually checked + + *utf32 = sucs2; + return 1; + } + return 0; } @@ -383,9 +370,43 @@ s32 kuten2eucjp() return 0; } -s32 sjis2jis() +u16 sjis2jis(u16 c) { - cellL10n.todo("sjis2jis()"); + cellL10n.notice("sjis2jis(c=0x%x)", c); + + u64 v0 = static_cast(static_cast(static_cast(c))) >> 8 & 0xff; + u64 v1 = v0 - 0x81; + + if (((v1 & 0xffff) >= 0x7c) || (0x3f >= ((v0 - 0xa0) & 0xffff))) + { + return 0; + } + + const u64 v2 = static_cast(static_cast(c)) & 0xff; + + if (0x3f < v2 && (v2 < 0xfd && (static_cast(v2) != 0x7f))) + { + if (0x9f < v0) + { + v1 = v0 - 0xc1; + } + + u16 v3 = static_cast(v2) - 0x7e; + v0 = (v1 & 0x7fffffff) * 2 + 0x22; + + if (v2 < 0x9f) + { + const s16 v4 = v2 < 0x7f ? 0x1f : 0x20; + v3 = static_cast(v2) - v4; + v0 = (v1 & 0x7fffffff) * 2 + 0x21; + } + + if ((v0 & 0xffff) < 0x7f) + { + return static_cast((v0 & 0xffff) << 8) | v3; + } + } + return 0; } @@ -490,9 +511,58 @@ s32 eucjp2jis() return CELL_OK; } -s32 UTF32stoUTF8s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UTF32toUTF8(u32 src, vm::ptr dst); + +s32 UTF32stoUTF8s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("UTF32stoUTF8s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + cellL10n.notice("UTF32stoUTF8s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + auto tmp = vm::make_var[4]>({0, 0, 0, 0}); + const vm::ptr utf8_tmp = vm::cast(tmp.addr()); + + for (u32 src_pos = 0; src_pos < *src_len; src_pos++) + { + const s32 utf8_len = UTF32toUTF8(src[src_pos], utf8_tmp); + + if (utf8_len == 0) + { + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + len += utf8_len; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = len - utf8_len; + return DSTExhausted; + } + + for (s32 i = 0; i < utf8_len; i++) + { + dst[dst_pos++] = utf8_tmp[i]; + } + } + } + + *dst_len = len; return ConversionOK; } @@ -502,27 +572,140 @@ s32 sjishan2zen() return 0; } -s32 UCS2toSBCS() +s32 UCS2toSBCS(u16 src, vm::ptr dst, u32 code_page) { - cellL10n.todo("UCS2toSBCS()"); + cellL10n.notice("UCS2toSBCS(src=0x%x, dst=*0x%x, code_page=0x%x)", src, dst, code_page); + + HostCode code = 0; + if ((code_page >= _L10N_CODE_) || !_L10nCodeParse(code_page, code)) + { + return -1; + } + + if (src < 0xfffe) + { + ensure(!!dst); // Not really checked + + if (src < 0x80) + { + *dst = static_cast(src); + return 1; + } + + vm::var dst_len = vm::make_var(0); + const s32 res = _L10nConvertChar(L10N_UCS2, &src, sizeof(src), code_page, dst, dst_len); + + if (res == ConversionOK) + { + return 1; + } + + if (res == ConverterUnknown) + { + return -1; + } + } + return 0; } s32 UTF8stoGBKs() { - cellL10n.todo("UCS2toSBCS()"); + cellL10n.todo("UTF8stoGBKs()"); return ConversionOK; } -s32 UTF8toUCS2() +s32 UTF8toUCS2(vm::cptr src, vm::ptr dst) { - cellL10n.todo("UTF8toUCS2()"); - return 0; + cellL10n.notice("UTF8toUCS2(src=*0x%x, dst=*0x%x)", src, dst); + + ensure(src && dst); // Not really checked + + if ((((src[0] & 0xf0) == 0xe0) && ((src[1] & 0xc0) == 0x80)) && ((src[2] & 0xc0) == 0x80)) + { + const u64 ucs2 = (static_cast(src[1]) & 0x3f) << 6 | (static_cast(src[0]) & 0xf) << 0xc | (static_cast(src[2]) & 0x3f); + + if (ucs2 < 0x800) + { + return 0; + } + + if ((static_cast(ucs2) & UTF16_SURROGATES_MASK1) == UTF16_HIGH_SURROGATES) + { + return 0; + } + + *dst = static_cast(ucs2); + return 3; + } + + if ((((src[0] & 0xe0) == 0xc0) && (0xc1 < static_cast(src[0]))) && ((src[1] & 0xc0) == 0x80)) + { + *dst = (src[0] & 0x1f) << 6 | (src[1] & 0x3f); + return 2; + } + + if (static_cast(src[0]) < '\0') + { + return 0; + } + + *dst = static_cast(src[0]); + return 1; } -s32 UCS2stoUTF8s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UCS2toUTF8(u16 ucs2, vm::ptr utf8); + +s32 UCS2stoUTF8s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("UCS2stoUTF8s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + cellL10n.notice("UCS2stoUTF8s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + auto tmp = vm::make_var[4]>({0, 0, 0, 0}); + const vm::ptr utf8_tmp = vm::cast(tmp.addr()); + + for (u32 src_pos = 0; src_pos < *src_len; src_pos++) + { + const s32 utf8_size = UCS2toUTF8(src[src_pos], utf8_tmp); + + if (utf8_size == 0) + { + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + len += utf8_size; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = len - utf8_size; + return DSTExhausted; + } + + for (s32 i = 0; i < utf8_size; i++) + { + dst[dst_pos++] = utf8_tmp[i]; + } + } + } + + *dst_len = len; return ConversionOK; } @@ -532,10 +715,57 @@ s32 EUCKRstoUTF8s() return ConversionOK; } -s32 UTF16stoUTF32s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UTF16toUTF32(vm::cptr src, vm::ptr dst); + +s32 UTF16stoUTF32s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.warning("UTF16stoUTF32s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); - return _L10nConvertStr(L10N_UTF16, src, src_len, L10N_UTF32, dst, dst_len); + cellL10n.notice("UTF16stoUTF32s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + vm::var utf32_tmp = vm::make_var(0); + + for (u32 src_pos = 0; src_pos < *src_len;) + { + const s32 utf16_len = UTF16toUTF32(src + src_pos, utf32_tmp); + + if (utf16_len == 0) + { + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + len++; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = dst_pos; + return DSTExhausted; + } + + dst[dst_pos++] = *utf32_tmp; + } + + src_pos += utf16_len; + } + + *dst_len = len; + return ConversionOK; } s32 UTF8toEUCKR() @@ -544,10 +774,51 @@ s32 UTF8toEUCKR() return 0; } -s32 UTF16toUTF8() +s32 UTF16toUTF8(vm::cptr src, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("UTF16toUTF8()"); - return 0; + cellL10n.notice("UTF16toUTF8(src=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, dst, dst_len); + + ensure(src && dst && dst_len); // Not really checked + + const u64 utf16_long = src[0]; + vm::cptr src_raw = vm::cast(src.addr()); + + if ((src[0] & UTF16_SURROGATES_MASK1) == UTF16_HIGH_SURROGATES) + { + if (((src[0] & UTF16_SURROGATES_MASK2) == UTF16_HIGH_SURROGATES) && ((src[1] & UTF16_SURROGATES_MASK2) == UTF16_LOW_SURROGATES)) + { + const s64 lVar2 = (static_cast(src[0] >> 6) & 0xf) + 1; + dst[0] = static_cast(static_cast(lVar2 << 0x20) >> 0x22) | 0xf0; + dst[1] = (static_cast(lVar2) * '\x10' & 0x30U) | (static_cast(src[0] >> 2) & 0xf) | 0x80; + dst[2] = (static_cast(src[1] >> 6) & 0xf) | (static_cast(src[0]) & 3) << 4 | 0x80; + dst[3] = (static_cast(src_raw[3]) & 0x3f) | 0x80; + *dst_len = 4; + return 2; + } + + return 0; + } + + if (0x7ff < utf16_long) + { + dst[0] = static_cast((utf16_long << 0x20) >> 0x2c) | 0xe0; + dst[1] = (static_cast(src[0] >> 6) & 0x3f) | 0x80; + dst[2] = (static_cast(src_raw[1]) & 0x3f) | 0x80; + *dst_len = 3; + return 1; + } + + if (utf16_long < 0x80) + { + dst[0] = static_cast(src[0]); + *dst_len = 1; + return 1; + } + + dst[0] = static_cast((utf16_long << 0x20) >> 0x26) | 0xc0; + dst[1] = (static_cast(src_raw[1]) & 0x3f) | 0x80; + *dst_len = 2; + return 1; } s32 ARIBstoUTF8s() @@ -556,7 +827,7 @@ s32 ARIBstoUTF8s() return ConversionOK; } -s32 SJISstoUTF8s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 SJISstoUTF8s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) { cellL10n.warning("SJISstoUTF8s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); return _L10nConvertStr(L10N_CODEPAGE_932, src, src_len, L10N_UTF8, dst, dst_len); @@ -646,10 +917,51 @@ s32 UTF8stoBIG5s() return ConversionOK; } -s32 UTF16stoUCS2s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UTF16stoUCS2s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.warning("UTF16stoUCS2s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); - return _L10nConvertStr(L10N_UTF16, src, src_len, L10N_UCS2, dst, dst_len); + cellL10n.notice("UTF16stoUCS2s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + for (u32 src_pos = 0; src_pos < *src_len; src_pos++) + { + const u16 utf16 = src[src_pos]; + + if ((utf16 & UTF16_SURROGATES_MASK1) == UTF16_HIGH_SURROGATES) + { + *src_len -= src_pos; + *dst_len = src_pos; + return SRCIllegal; + } + + len++; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = src_pos; + return DSTExhausted; + } + + dst[dst_pos++] = utf16; + } + } + + *dst_len = len; + return ConversionOK; } s32 UCS2stoGB18030s() @@ -738,9 +1050,71 @@ s32 JISstoSJISs(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::pt return 0; } -s32 UTF8toUTF16() +s32 UTF8toUTF16(vm::cptr src, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("UTF8toUTF16()"); + cellL10n.notice("UTF8toUTF16(src=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, dst, dst_len); + + ensure(src && dst && dst_len); // Not really checked + + u64 longval = src[0]; + + if ((src[0] & 0xf8) == 0xf0) + { + if ((src[1] & 0xc0) == 0x80) + { + if ((src[2] & 0xc0) == 0x80 && (src[3] & 0xc0) == 0x80) + { + longval = (longval & 7) << 2 | (static_cast(src[1] >> 4) & 3); + + if (((longval - 1) & 0xffff) < 0x10) + { + dst[0] = (src[1] & 0xf) << 2 | (src[2] >> 4 & 3) | static_cast(((longval - 1) & 0xffffffff) << 6) | UTF16_HIGH_SURROGATES; + dst[1] = (src[2] & 0xf) << 6 | (src[3] & 0x3f) | UTF16_LOW_SURROGATES; + *dst_len = 2; + return 4; + } + } + } + } + else + { + if ((src[0] & 0xf0) != 0xe0) + { + if (((src[0] & 0xe0) == 0xc0) && (0xc1 < longval)) + { + if ((src[1] & 0xc0) != 0x80) + { + return 0; + } + + dst[0] = (src[0] & 0x1f) << 6 | (src[1] & 0x3f); + *dst_len = 1; + return 2; + } + + if (static_cast(src[0]) < '\0') + { + return 0; + } + + dst[0] = static_cast(src[0]); + *dst_len = 1; + return 1; + } + + if ((src[1] & 0xc0) == 0x80 && (src[2] & 0xc0) == 0x80) + { + longval = (static_cast(src[1]) & 0x3f) << 6 | (longval & 0xf) << 0xc | (static_cast(src[2]) & 0x3f); + + if ((0x7ff < longval && ((static_cast(longval) & UTF16_SURROGATES_MASK1) != UTF16_HIGH_SURROGATES))) + { + dst[0] = static_cast(longval); + *dst_len = 1; + return 3; + } + } + } + return 0; } @@ -762,9 +1136,20 @@ s32 SjisHan2Zen() return ConversionOK; } -s32 UCS2toUTF16() +s32 UCS2toUTF16(u16 ucs2, vm::ptr utf16) { - cellL10n.todo("UCS2toUTF16()"); + cellL10n.notice("UCS2toUTF16(ucs2=0x%x, utf16=*0x%x)", ucs2, utf16); + + const s32 sucs2 = static_cast(ucs2); + + if ((sucs2 & UTF16_SURROGATES_MASK1) != UTF16_HIGH_SURROGATES) + { + ensure(!!utf16); // Not actually checked + + *utf16 = ucs2; + return 1; + } + return 0; } @@ -774,9 +1159,40 @@ s32 UCS2toMSJIS() return 0; } -s32 sjis2kuten() +u16 sjis2kuten(u16 c) { - cellL10n.todo("sjis2kuten()"); + cellL10n.notice("sjis2kuten(c=0x%x)", c); + + u64 v0 = static_cast(static_cast(static_cast(c))) >> 8 & 0xff; + u64 v1 = v0 - 0x81; + + if (((v1 & 0xffff) >= 0x7c) || (0x3f >= ((v0 - 0xa0) & 0xffff))) + { + return 0; + } + + const u64 v2 = static_cast(static_cast(c)) & 0xff; + + if (0x3f < v2 && (v2 < 0xfd && (static_cast(v2) != 0x7f))) + { + if (0x9f < v0) + { + v1 = v0 - 0xc1; + } + + u16 v3 = static_cast(v2) - 0x9e; + v0 = (v1 & 0x7fffffff) * 2 + 2; + + if (v2 < 0x9f) + { + const s16 v4 = v2 < 0x7f ? 0x1f : 0x20; + v3 = (static_cast(v2) - v4) - 0x20; + v0 = (v1 & 0x7fffffff) * 2 + 1; + } + + return static_cast((v0 & 0xffffffff) << 8) | v3; + } + return 0; } @@ -786,9 +1202,18 @@ s32 UCS2toUHC() return 0; } -s32 UTF32toUCS2() +s32 UTF32toUCS2(u32 src, vm::ptr dst) { - cellL10n.todo("UTF32toUCS2()"); + cellL10n.notice("UTF32toUCS2(src=0x%x, dst=*0x%x)", src, dst); + + if ((src < 0x10000) && (0x7ff < src - UTF16_HIGH_SURROGATES)) + { + ensure(!!dst); // Not really checked + + *dst = static_cast(src); + return 1; + } + return 0; } @@ -810,15 +1235,66 @@ s32 UCS2stoEUCJPs() return ConversionOK; } -s32 UTF16toUCS2() +s32 UTF16toUCS2(vm::cptr src, vm::ptr dst) { - cellL10n.todo("UTF16toUCS2()"); + cellL10n.notice("UTF16toUCS2(src=*0x%x, dst=*0x%x)", src, dst); + + ensure(!!src); // Not really checked + + if ((*src & UTF16_SURROGATES_MASK1) != UTF16_HIGH_SURROGATES) + { + ensure(!!dst); // Not really checked + *dst = *src; + return 1; + } + return 0; } -s32 UCS2stoUTF16s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UCS2stoUTF16s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("UCS2stoUTF16s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + cellL10n.notice("UCS2stoUTF16s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + for (u32 src_pos = 0; src_pos < *src_len; src_pos++) + { + const u16 ucs2 = src[src_pos]; + + if ((ucs2 & UTF16_SURROGATES_MASK1) == UTF16_HIGH_SURROGATES) + { + *src_len -= src_pos; + *dst_len = src_pos; + return SRCIllegal; + } + + len++; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = src_pos; + return DSTExhausted; + } + + dst[dst_pos++] = ucs2; + } + } + + *dst_len = len; return ConversionOK; } @@ -828,10 +1304,86 @@ s32 UCS2stoEUCCNs() return ConversionOK; } -s32 SBCSstoUTF8s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len, s32 enc) +s32 SBCSstoUTF8s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len, u32 code_page) { - cellL10n.warning("SBCSstoUTF8s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x, enc=*0x%x)", src, src_len, dst, dst_len, enc); - return _L10nConvertStr(enc, src, src_len, L10N_UTF8, dst, dst_len); // Might not work in some scenarios + cellL10n.notice("SBCSstoUTF8s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x, code_page=0x%x)", src, src_len, dst, dst_len, code_page); + + HostCode code = 0; + if ((code_page >= _L10N_CODE_) || !_L10nCodeParse(code_page, code)) + { + return ConverterUnknown; + } + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + for (u32 src_pos = 0; src_pos < *src_len; src_pos++) + { + u8 src_val = src[src_pos]; + u64 longval = static_cast(src_val); + s32 utf8_len = 1; + + if (static_cast(src_val) < '\0') + { + u8 dst_tmp[4] = {}; + s32 dst_len_tmp = 4; + + const s32 res = _ConvertStr(code_page, &src_val, 1, L10N_UTF8, &dst_tmp, &dst_len_tmp, false); + + if (res != ConversionOK) + { + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + longval = *reinterpret_cast(dst_tmp); + utf8_len = dst_len_tmp; + } + + len += utf8_len; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = len - utf8_len; + return DSTExhausted; + } + + src_val = static_cast(longval); + + if (utf8_len == 3) + { + dst[dst_pos++] = static_cast((longval << 0x20) >> 0x2c) | 0xe0; + dst[dst_pos++] = (static_cast(longval >> 6) & 0x3f) | 0x80; + dst[dst_pos++] = (src_val & 0x3f) | 0x80; + } + else if (utf8_len == 1) + { + dst[dst_pos++] = src_val; + } + else + { + dst[dst_pos++] = static_cast(longval >> 6) | 0xc0; + dst[dst_pos++] = (src_val & 0x3f) | 0x80; + } + } + } + + *dst_len = len; + return ConversionOK; } s32 SJISstoJISs(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) @@ -840,16 +1392,112 @@ s32 SJISstoJISs(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::pt return 0; } -s32 SBCStoUTF8() +s32 SBCStoUTF8(u8 src, vm::ptr dst, u32 code_page) { - cellL10n.todo("SBCStoUTF8()"); - return 0; + cellL10n.notice("SBCStoUTF8(src=0x%x, dst=*0x%x, code_page=0x%x)", src, *dst, code_page); + + HostCode code = 0; + if ((code_page >= _L10N_CODE_) || !_L10nCodeParse(code_page, code)) + { + return -1; + } + + ensure(!!dst); // Not really checked + + if (static_cast(src) >= 0) + { + *dst = src; + return 1; + } + + u8 dst_tmp = 0; + s32 dst_len_tmp = 1; + + const s32 res = _ConvertStr(L10N_UTF8, &src, 1, code_page, &dst_tmp, &dst_len_tmp, false); + if (res != ConversionOK) + { + return 0; + } + + const u64 longval = static_cast(dst_tmp); + const u8 val = static_cast(dst_tmp) & 0x3f; + + if (longval < 0x800) + { + dst[0] = static_cast((longval << 0x20) >> 0x26) | 0xc0; + dst[1] = val | 0x80; + return 2; + } + + dst[0] = static_cast((longval << 0x20) >> 0x2c) | 0xe0; + dst[1] = (static_cast(static_cast(dst_tmp) >> 6) & 0x3f) | 0x80; + dst[2] = val | 0x80; + return 3; } -s32 UTF8toUTF32() +s32 UTF8toUTF32(vm::cptr src, vm::ptr dst) { - cellL10n.todo("UTF8toUTF32()"); - return 0; + cellL10n.notice("UTF8toUTF32(src=*0x%x, dst=*0x%x)", src, dst); + + ensure(src && dst); // Not really checked + + u64 longval = src[0]; + + if ((src[0] & 0xf8) == 0xf0) + { + if ((src[1] & 0xc0) != 0x80 || + (src[2] & 0xc0) != 0x80 || + (src[3] & 0xc0) != 0x80) + { + return 0; + } + + longval = (static_cast(src[2]) & 0x3f) << 6 | (longval & 7) << 0x12 | (static_cast(src[1]) & 0x3f) << 0xc | (static_cast(src[3]) & 0x3f); + if (0xfffff < ((longval - 0x10000) & 0xffffffff)) + { + return 0; + } + + *dst = static_cast(longval); + return 4; + } + + if ((src[0] & 0xf0) == 0xe0) + { + if ((src[1] & 0xc0) != 0x80 || + (src[2] & 0xc0) != 0x80) + { + return 0; + } + + longval = (static_cast(src[1]) & 0x3f) << 6 | (longval & 0xf) << 0xc | (static_cast(src[2]) & 0x3f); + if (longval < 0x800 || ((longval - UTF16_HIGH_SURROGATES) & 0xffffffff) < 0x800) + { + return 0; + } + + *dst = static_cast(longval); + return 3; + } + + if (((src[0] & 0xe0) == 0xc0) && (0xc1 < longval)) + { + if ((src[1] & 0xc0) != 0x80) + { + return 0; + } + + *dst = (src[0] & 0x1f) << 6 | (src[1] & 0x3f); + return 2; + } + + if (static_cast(src[0]) < '\0') + { + return 0; + } + + *dst = static_cast(src[0]); + return 1; } s32 jstrchk(vm::cptr jstr) @@ -890,9 +1538,57 @@ s32 EucJpZen2Han() return ConversionOK; } -s32 UTF32stoUTF16s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UTF32toUTF16(u32 src, vm::ptr dst); + +s32 UTF32stoUTF16s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("UTF32stoUTF16s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + cellL10n.notice("UTF32stoUTF16s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + auto tmp = vm::make_var[2]>({0, 0}); + const vm::ptr utf16_tmp = vm::cast(tmp.addr()); + + for (u32 src_pos = 0; src_pos < *src_len; src_pos++) + { + const s32 utf16_len = UTF32toUTF16(src[src_pos], utf16_tmp); + if (utf16_len == 0) + { + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + len += utf16_len; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = len - utf16_len; + return DSTExhausted; + } + + for (s32 i = 0; i < utf16_len; i++) + { + dst[dst_pos++] = utf16_tmp[i]; + } + } + } + + *dst_len = len; return ConversionOK; } @@ -926,9 +1622,54 @@ s32 EUCKRstoUHCs(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::p return 0; } -s32 UTF8stoUTF32s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UTF8stoUTF32s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("UTF8stoUTF32s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + cellL10n.notice("UTF8stoUTF32s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + vm::var utf32_tmp = vm::make_var(0); + + for (u32 src_pos = 0; src_pos < *src_len;) + { + const s32 utf8_len = UTF8toUTF32(src + src_pos, utf32_tmp); + + if (utf8_len == 0) + { + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + len++; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = dst_pos; + return DSTExhausted; + } + + dst[dst_pos++] = *utf32_tmp; + } + + src_pos += utf8_len; + } + + *dst_len = len; return ConversionOK; } @@ -950,7 +1691,7 @@ s32 UHCtoUCS2() return 0; } -s32 L10nConvertStr(s32 src_code, vm::cptr src, vm::ptr src_len, s32 dst_code, vm::ptr dst, vm::ptr dst_len) +s32 L10nConvertStr(s32 src_code, vm::cptr src, vm::ptr src_len, s32 dst_code, vm::ptr dst, vm::ptr dst_len) { cellL10n.error("L10nConvertStr(src_code=%d, src=*0x%x, src_len=*0x%x, dst_code=%d, dst=*0x%x, dst_len=*0x%x)", src_code, src, src_len, dst_code, dst, dst_len); return _L10nConvertStr(src_code, src, src_len, dst_code, dst, dst_len); @@ -968,15 +1709,83 @@ s32 UTF8toUHC() return 0; } -s32 UTF32toUTF8() +s32 UTF32toUTF8(u32 src, vm::ptr dst) { - cellL10n.todo("UTF32toUTF8()"); - return 0; + cellL10n.notice("UTF32toUTF8(src=0x%x, dst=*0x%x)", src, dst); + + const u64 utf32 = static_cast(static_cast(src)); + if (((utf32 & 0xffffffff) >= 0x110000) || (0x7ff >= ((utf32 - UTF16_HIGH_SURROGATES) & 0xffffffff))) + { + return 0; + } + + ensure(!!dst); // Not really checked + + if (0xffff < (utf32 & 0xffffffff)) + { + dst[0] = static_cast((utf32 << 0x20) >> 0x32) | 0xf0; + dst[1] = (static_cast(utf32 >> 0xc) & 0x3f) | 0x80; + dst[2] = (static_cast(utf32 >> 6) & 0x3f) | 0x80; + dst[3] = (static_cast(src) & 0x3f) | 0x80; + return 4; + } + + if ((utf32 & 0xffffffff) < 0x80) + { + dst[0] = static_cast(src); + return 1; + } + + if ((utf32 & 0xffffffff) < 0x800) + { + dst[0] = static_cast((utf32 << 0x20) >> 0x26) | 0xc0; + dst[1] = (static_cast(src) & 0x3f) | 0x80; + return 2; + } + + dst[0] = static_cast((utf32 << 0x20) >> 0x2c) | 0xe0; + dst[1] = (static_cast(utf32 >> 6) & 0x3f) | 0x80; + dst[2] = (static_cast(src) & 0x3f) | 0x80; + return 3; } -s32 sjis2eucjp() +u16 sjis2eucjp(u16 c) { - cellL10n.todo("sjis2eucjp()"); + cellL10n.notice("sjis2eucjp(c=0x%x)", c); + + u64 v0 = static_cast(static_cast(static_cast(c))) >> 8 & 0xff; + u64 v1 = v0 - 0x81; + + if (((v1 & 0xffff) >= 0x7c) || (0x3f >= ((v0 - 0xa0) & 0xffff))) + { + return 0; + } + + const u64 v2 = static_cast(static_cast(c)) & 0xff; + + if (0x3f < v2 && (v2 < 0xfd && (static_cast(v2) != 0x7f))) + { + if (0x9f < v0) + { + v1 = v0 - 0xc1; + } + + u16 v3 = static_cast(v2) - 0x7e; + v0 = (v1 & 0x7fffffff) * 2 + 0x22; + + if (v2 < 0x9f) + { + const s16 v4 = v2 < 0x7f ? 0x1f : 0x20; + v3 = static_cast(v2) - v4; + v0 = (v1 & 0x7fffffff) * 2 + 0x21; + } + + if ((v0 & 0xffff) < 0x7f) + { + return static_cast((v0 & 0xffff) << 8) | v3 | 0x8080; + } + } + return 0; } @@ -998,10 +1807,27 @@ s32 EUCKRtoUCS2() return 0; } -s32 UTF32toUTF16() +s32 UTF32toUTF16(u32 src, vm::ptr dst) { - cellL10n.todo("UTF32toUTF16()"); - return 0; + cellL10n.notice("UTF32toUTF16(src=0x%x, dst=*0x%x)", src, dst); + + const u64 utf32 = static_cast(static_cast(src)); + if (((utf32 & 0xffffffff) >= 0x110000) || (0x7ff >= ((utf32 - UTF16_HIGH_SURROGATES) & 0xffffffff))) + { + return 0; + } + + ensure(!!dst); // Not really checked + + if (0xffff < (utf32 & 0xffffffff)) + { + dst[0] = static_cast(((utf32 - 0x10000) << 0x20) >> 0x2a) | UTF16_HIGH_SURROGATES; + dst[1] = (static_cast(src) & 0x3ff) | UTF16_LOW_SURROGATES; + return 2; + } + + dst[0] = static_cast(src); + return 1; } s32 EUCCNstoUCS2s() @@ -1010,9 +1836,67 @@ s32 EUCCNstoUCS2s() return ConversionOK; } -s32 SBCSstoUCS2s() +s32 SBCSstoUCS2s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len, u32 code_page) { - cellL10n.todo("SBCSstoUCS2s()"); + cellL10n.notice("SBCSstoUCS2s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x, code_page=0x%x)", src, src_len, dst, dst_len, code_page); + + HostCode code = 0; + if ((code_page >= _L10N_CODE_) || !_L10nCodeParse(code_page, code)) + { + return ConverterUnknown; + } + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + for (u32 src_pos = 0; src_pos < *src_len; src_pos++) + { + const u8 src_val = src[src_pos]; + u16 val = static_cast(src_val); + + if (static_cast(src_val) < '\0') + { + u16 dst_tmp = 0; + s32 dst_len_tmp = 2; + + const s32 res = _ConvertStr(code_page, &src_val, 1, L10N_UCS2, &dst_tmp, &dst_len_tmp, false); + + if (res != ConversionOK) + { + *src_len -= src_pos; + *dst_len = dst_pos; + return SRCIllegal; + } + + val = dst_tmp; + } + + len++; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = dst_pos; + return DSTExhausted; + } + + dst[dst_pos++] = val; + } + } + + *dst_len = len; return ConversionOK; } @@ -1046,23 +1930,38 @@ s32 UCS2toGBK() return 0; } -s32 UTF16toUTF32() +s32 UTF16toUTF32(vm::cptr src, vm::ptr dst) { - cellL10n.todo("UTF16toUTF32()"); + cellL10n.notice("UTF16toUTF32(src=*0x%x, dst=*0x%x)", src, dst); + + ensure(src && dst); // Not really checked + + if ((src[0] & UTF16_SURROGATES_MASK1) != UTF16_HIGH_SURROGATES) + { + *dst = static_cast(src[0]); + return 1; + } + + if (((src[0] & UTF16_SURROGATES_MASK2) == (src[0] & UTF16_SURROGATES_MASK1)) && ((src[1] & UTF16_SURROGATES_MASK2) == UTF16_LOW_SURROGATES)) + { + *dst = ((src[0] & 0x3ff) * 0x400 + 0x10000) | (src[1] & 0x3ff); + return 2; + } + return 0; } -s32 l10n_convert_str(s32 cd, vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 l10n_convert_str(s32 cd, vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) { cellL10n.warning("l10n_convert_str(cd=%d, src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", cd, src, src_len, dst, dst_len); - s32 src_code = cd >> 16; - s32 dst_code = cd & 0xffff; + const s32 src_code = cd >> 16; + const s32 dst_code = cd & 0xffff; return _L10nConvertStr(src_code, src, src_len, dst_code, dst, dst_len); } -s32 EUCJPstoJISs(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 EUCJPstoJISs(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) { cellL10n.warning("EUCJPstoJISs(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); return _L10nConvertStr(L10N_EUC_JP, src, src_len, L10N_ISO_2022_JP, dst, dst_len); @@ -1092,9 +1991,35 @@ s32 isEucJpKigou() return 0; } -s32 UCS2toUTF8() +s32 UCS2toUTF8(u16 ucs2, vm::ptr utf8) { - cellL10n.todo("UCS2toUTF8()"); + cellL10n.notice("UCS2toUTF8(ucs2=0x%x, utf8=*0x%x)", ucs2, utf8); + + const u64 val = static_cast(ucs2) & 0xffff; + + if ((static_cast(val) & UTF16_SURROGATES_MASK1) != UTF16_HIGH_SURROGATES) + { + ensure(!!utf8); // Not really checked + + if (val < 0x80) + { + utf8[0] = static_cast(ucs2); + return 1; + } + + if (val < 0x800) + { + utf8[0] = static_cast((val << 0x20) >> 0x26) | 0xc0; + utf8[1] = (static_cast(ucs2) & 0x3f) | 0x80; + return 2; + } + + utf8[0] = static_cast((val << 0x20) >> 0x2c) | 0xe0; + utf8[1] = (static_cast(val >> 6) & 0x3f) | 0x80; + utf8[2] = (static_cast(ucs2) & 0x3f) | 0x80; + return 3; + } + return 0; } @@ -1128,9 +2053,24 @@ s32 kuten2sjis() return 0; } -s32 UTF8toSBCS() +s32 UTF8toSBCS(vm::cptr src, vm::ptr dst, u32 code_page) { - cellL10n.todo("UTF8toSBCS()"); + cellL10n.notice("UTF8toSBCS(src=*0x%x, dst=*0x%x, code_page=0x%x)", src, dst, code_page); + + vm::var ucs2_tmp = vm::make_var(0); + + const s32 utf8_len = UTF8toUCS2(src, ucs2_tmp); + if (utf8_len != 0) + { + const s32 len = UCS2toSBCS(*ucs2_tmp, dst, code_page); + if (1 < len + 1U) + { + return utf8_len; + } + + return len; + } + return 0; } @@ -1170,10 +2110,35 @@ s32 UCS2toEUCKR() return 0; } -s32 SBCStoUCS2() +s32 SBCStoUCS2(u8 src, vm::ptr dst, u32 code_page) { - cellL10n.todo("SBCStoUCS2()"); - return 0; + cellL10n.notice("SBCStoUCS2(src=0x%x, dst=*0x%x, code_page=0x%x)", src, dst, code_page); + + HostCode code = 0; + if ((code_page >= _L10N_CODE_) || !_L10nCodeParse(code_page, code)) + { + return -1; + } + + ensure(!!dst); // Not really checked + + if (static_cast(src) >= 0) + { + *dst = static_cast(static_cast(src)) & 0xff; + return 1; + } + + u16 dst_tmp = 0; + s32 dst_len_tmp = sizeof(u16); + + const s32 res = _ConvertStr(code_page, &src, 1, L10N_UCS2, &dst_tmp, &dst_len_tmp, false); + if (res != ConversionOK) + { + return 0; + } + + *dst = dst_tmp; + return 1; } s32 MSJISstoUCS2s() @@ -1186,6 +2151,13 @@ s32 l10n_get_converter(u32 src_code, u32 dst_code) { cellL10n.warning("l10n_get_converter(src_code=%d, dst_code=%d)", src_code, dst_code); return (src_code << 16) | dst_code; + + if (_L10N_CODE_ <= src_code || _L10N_CODE_ <= dst_code) + { + return 0xffffffff; + } + + return (src_code << 16) | dst_code; } s32 GB18030stoUTF8s() @@ -1200,9 +2172,50 @@ s32 SJISstoEUCJPs(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm:: return 0; } -s32 UTF32stoUCS2s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UTF32stoUCS2s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("UTF32stoUCS2s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + cellL10n.notice("UTF32stoUCS2s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + for (u32 src_pos = 0; src_pos < *src_len; src_pos++) + { + const u32 utf32 = src[src_pos]; + + if (utf32 >= 0x10000 || (0x7ff >= utf32 - UTF16_HIGH_SURROGATES)) + { + *src_len -= src_pos; + *dst_len = src_pos; + return SRCIllegal; + } + + len++; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = src_pos; + return DSTExhausted; + } + + dst[dst_pos++] = static_cast(utf32); + } + } + + *dst_len = len; return ConversionOK; } @@ -1218,9 +2231,86 @@ s32 EUCCNtoUCS2() return CELL_OK; } -s32 UTF8stoSBCSs() +s32 UTF8stoSBCSs(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len, u32 code_page) { - cellL10n.todo("UTF8stoSBCSs()"); + cellL10n.notice("UTF8stoSBCSs(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x, code_page=0x%x)", src, src_len, dst, dst_len, code_page); + + HostCode code = 0; + if ((code_page >= _L10N_CODE_) || !_L10nCodeParse(code_page, code)) + { + return ConverterUnknown; + } + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + vm::var ucs2_tmp = vm::make_var(0); + + for (u32 src_pos = 0; src_pos < *src_len;) + { + const s32 utf8_len = UTF8toUCS2(src + src_pos, ucs2_tmp); + if (utf8_len == 0) + { + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + u16 ucs2 = *ucs2_tmp; + + if ((*src_len < (utf8_len + src_pos)) || (0xfffd < ucs2)) + { + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + if (0x7f < ucs2) + { + const u8 src_tmp = src[src_pos]; + u8 dst_tmp = 0; + s32 dst_len_tmp = 1; + + const s32 res = _ConvertStr(L10N_UTF8, &src_tmp, 1, code_page, &dst_tmp, &dst_len_tmp, false); + + if (res != ConversionOK) + { + *src_len -= src_pos; + *dst_len = dst_pos; + return SRCIllegal; + } + + ucs2 = dst_tmp; + } + + len++; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = dst_pos; + return DSTExhausted; + } + + dst[dst_pos++] = static_cast(ucs2); + } + + src_pos += utf8_len; + } + + *dst_len = len; return ConversionOK; } @@ -1254,45 +2344,61 @@ s32 UTF8toBIG5() return 0; } -s32 UTF16stoUTF8s(vm::cptr utf16, vm::ref utf16_len, vm::ptr utf8, vm::ref utf8_len) +s32 UTF16stoUTF8s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.error("UTF16stoUTF8s(utf16=*0x%x, utf16_len=*0x%x, utf8=*0x%x, utf8_len=*0x%x)", utf16, utf16_len.addr(), utf8, utf8_len.addr()); + cellL10n.notice("UTF16stoUTF8s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); - const u32 max_len = utf8_len; utf8_len = 0; + ensure(src_len && dst_len); // Not really checked - for (u32 i = 0, len = 0; i < static_cast(utf16_len); i++, utf8_len = len) + if (*src_len == 0u) { - const u16 ch = utf16[i]; + *dst_len = 0; + return ConversionOK; + } - // increase required length (TODO) - len = len + 1; + ensure(src); // Not really checked - // validate character (TODO) - //if () - //{ - // utf16_len -= i; - // return SRCIllegal; - //} + u32 len = 0; + u32 dst_pos = 0; - if (utf8) + auto tmp = vm::make_var[4]>({0, 0, 0, 0}); + const vm::ptr utf8_tmp = vm::cast(tmp.addr()); + vm::var utf8_len_tmp = vm::make_var(0); + + for (u32 src_pos = 0; src_pos < *src_len;) + { + *utf8_len_tmp = 4; + const s32 utf16_len = UTF16toUTF8(src + src_pos, utf8_tmp, utf8_len_tmp); + + if (utf16_len == 0) { - if (len > max_len) + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + const u32 utf8_len = *utf8_len_tmp; + len += utf8_len; + + if (dst) + { + if (*dst_len < len) { - utf16_len -= i; + *src_len -= src_pos; + *dst_len = len - utf8_len; return DSTExhausted; } - if (ch <= 0x7f) + for (u32 i = 0; i < utf8_len; i++) { - *utf8++ = static_cast(ch); - } - else - { - *utf8++ = '?'; // TODO + dst[dst_pos++] = utf8_tmp[i]; } } + + src_pos += utf16_len; } + *dst_len = len; return ConversionOK; } @@ -1308,7 +2414,7 @@ s32 GB18030toUTF8() return 0; } -s32 UTF8toSJIS(u8 ch, vm::ptr dst, vm::ptr dst_len) // Doesn't work backwards +s32 UTF8toSJIS(u8 ch, vm::ptr dst, vm::ptr dst_len) // Doesn't work backwards { cellL10n.warning("UTF8toSJIS(ch=%d, dst=*0x%x, dst_len=*0x%x)", ch, dst, dst_len); return _L10nConvertChar(L10N_UTF8, &ch, sizeof(ch), L10N_CODEPAGE_932, dst, dst_len); @@ -1320,15 +2426,122 @@ s32 ARIBstoUCS2s() return ConversionOK; } -s32 UCS2stoUTF32s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UCS2stoUTF32s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("UCS2stoUTF32s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + cellL10n.notice("UCS2stoUTF32s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + for (u32 src_pos = 0; src_pos < *src_len; src_pos++) + { + const u16 ucs2 = src[src_pos]; + + if ((ucs2 & UTF16_SURROGATES_MASK1) == UTF16_HIGH_SURROGATES) + { + *src_len -= src_pos; + *dst_len = src_pos; + return SRCIllegal; + } + + len++; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = src_pos; + return DSTExhausted; + } + + dst[dst_pos++] = static_cast(ucs2); + } + } + + *dst_len = len; return ConversionOK; } -s32 UCS2stoSBCSs() +s32 UCS2stoSBCSs(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len, u32 code_page) { - cellL10n.todo("UCS2stoSBCSs()"); + cellL10n.notice("UCS2stoSBCSs(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x, code_page=*0x%x)", src, src_len, dst, dst_len, code_page); + + HostCode code = 0; + if ((code_page >= _L10N_CODE_) || !_L10nCodeParse(code_page, code)) + { + return ConverterUnknown; + } + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + for (u32 src_pos = 0; src_pos < *src_len; src_pos++) + { + const s16 ucs2 = src[src_pos]; + + if (ucs2 >= 0xfffe) + { + *src_len -= src_pos; + *dst_len = src_pos; + return SRCIllegal; + } + + u8 val = static_cast(ucs2); + + if (0x7f < ucs2) + { + const u16 src_tmp = src[src_pos]; + u8 dst_tmp = 0; + s32 dst_len_tmp = 1; + + const s32 res = _ConvertStr(L10N_UCS2, &src_tmp, sizeof(u16), code_page, &dst_tmp, &dst_len_tmp, false); + + if (res != ConversionOK) + { + *src_len -= src_pos; + *dst_len = dst_pos; + return SRCIllegal; + } + + val = dst_tmp; + } + + len++; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = src_pos; + return DSTExhausted; + } + + dst[dst_pos++] = val; + } + } + return ConversionOK; } @@ -1350,13 +2563,64 @@ s32 SJIStoEUCJP() return 0; } -s32 UTF8stoUTF16s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UTF8stoUTF16s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.warning("UTF8stoUTF16s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); - return _L10nConvertStr(L10N_UTF8, src, src_len, L10N_UTF16, dst, dst_len); + cellL10n.notice("UTF8stoUTF16s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + auto tmp = vm::make_var[2]>({0, 0}); + const vm::ptr utf16_tmp = vm::cast(tmp.addr()); + vm::var utf16_len_tmp = vm::make_var(0); + + for (u32 src_pos = 0; src_pos < *src_len;) + { + const s32 utf8_len = UTF8toUTF16(src + src_pos, utf16_tmp, utf16_len_tmp); + + if (utf8_len == 0) + { + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + const u32 utf16_len = *utf16_len_tmp; + len += utf16_len; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = dst_pos; + return DSTExhausted; + } + + for (u32 i = 0; i < utf16_len; i++) + { + dst[dst_pos++] = utf16_tmp[i]; + } + } + + src_pos += utf8_len; + } + + *dst_len = len; + return ConversionOK; } -s32 SJISstoUCS2s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 SJISstoUCS2s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) { cellL10n.warning("SJISstoUCS2s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); return _L10nConvertStr(L10N_CODEPAGE_932, src, src_len, L10N_UCS2, dst, dst_len); @@ -1368,9 +2632,54 @@ s32 BIG5stoUCS2s() return ConversionOK; } -s32 UTF8stoUCS2s(vm::cptr src, vm::cptr src_len, vm::ptr dst, vm::ptr dst_len) +s32 UTF8stoUCS2s(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::ptr dst_len) { - cellL10n.todo("UTF8stoUCS2s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + cellL10n.notice("UTF8stoUCS2s(src=*0x%x, src_len=*0x%x, dst=*0x%x, dst_len=*0x%x)", src, src_len, dst, dst_len); + + ensure(src_len && dst_len); // Not really checked + + if (*src_len == 0u) + { + *dst_len = 0; + return ConversionOK; + } + + ensure(src); // Not really checked + + u32 len = 0; + u32 dst_pos = 0; + + vm::var ucs2_tmp = vm::make_var(5); + + for (u32 src_pos = 0; src_pos < *src_len;) + { + const s32 utf8_len = UTF8toUCS2(src + src_pos, ucs2_tmp); + + if (utf8_len == 0 || *src_len < len) + { + *src_len -= src_pos; + *dst_len = len; + return SRCIllegal; + } + + len++; + + if (dst) + { + if (*dst_len < len) + { + *src_len -= src_pos; + *dst_len = dst_pos; + return DSTExhausted; + } + + dst[dst_pos++] = ucs2_tmp[0]; + } + + src_pos += utf8_len; + } + + *dst_len = len; return ConversionOK; } diff --git a/rpcs3/Emu/Cell/Modules/cellL10n.h b/rpcs3/Emu/Cell/Modules/cellL10n.h index 2a70dfb58f..f255f006ef 100644 --- a/rpcs3/Emu/Cell/Modules/cellL10n.h +++ b/rpcs3/Emu/Cell/Modules/cellL10n.h @@ -52,25 +52,25 @@ enum L10N_CODEPAGE_863, L10N_CODEPAGE_866, L10N_CODEPAGE_932, - L10N_SHIFT_JIS, + L10N_SHIFT_JIS = L10N_CODEPAGE_932, L10N_CODEPAGE_936, - L10N_GBK, + L10N_GBK = L10N_CODEPAGE_936, L10N_CODEPAGE_949, - L10N_UHC, + L10N_UHC = L10N_CODEPAGE_949, L10N_CODEPAGE_950, - L10N_BIG5, + L10N_BIG5 = L10N_CODEPAGE_950, L10N_CODEPAGE_1251, L10N_CODEPAGE_1252, L10N_EUC_CN, L10N_EUC_JP, L10N_EUC_KR, L10N_ISO_2022_JP, - L10N_JIS, + L10N_JIS = L10N_ISO_2022_JP, L10N_ARIB, L10N_HZ, L10N_GB18030, L10N_RIS_506, - L10N_MUSIC_SHIFT_JIS, + L10N_MUSIC_SHIFT_JIS = L10N_RIS_506, //FW 3.10 and below L10N_CODEPAGE_852, L10N_CODEPAGE_1250, @@ -88,3 +88,12 @@ enum L10N_CODEPAGE_869, _L10N_CODE_ }; + +enum +{ + UTF16_SURROGATES_MASK1 = 0xf800, + UTF16_SURROGATES_MASK2 = 0xfc00, + UTF16_SURROGATES = 0xd800, + UTF16_HIGH_SURROGATES = 0xd800, + UTF16_LOW_SURROGATES = 0xdc00, +}; diff --git a/rpcs3/Emu/Cell/Modules/cellMic.cpp b/rpcs3/Emu/Cell/Modules/cellMic.cpp index a26050d875..86e4b8bafa 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMic.cpp @@ -1,5 +1,4 @@ #include "stdafx.h" -#include "Emu/System.h" #include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" #include "Utilities/StrUtil.h" @@ -10,6 +9,8 @@ #include +#include "3rdparty/OpenAL/openal-soft/include/AL/alext.h" + LOG_CHANNEL(cellMic); template<> diff --git a/rpcs3/Emu/Cell/Modules/cellMic.h b/rpcs3/Emu/Cell/Modules/cellMic.h index 3fbe5fba13..b00848218c 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.h +++ b/rpcs3/Emu/Cell/Modules/cellMic.h @@ -1,9 +1,8 @@ #pragma once #include "Utilities/Thread.h" -#include "Emu/Cell/timers.hpp" - -#include "3rdparty/OpenAL/openal-soft/include/AL/alext.h" +#include "3rdparty/OpenAL/openal-soft/include/AL/alc.h" +#include "Utilities/mutex.h" // Error Codes enum CellMicInError : u32 diff --git a/rpcs3/Emu/Cell/Modules/cellMouse.cpp b/rpcs3/Emu/Cell/Modules/cellMouse.cpp index 8b8d54715a..8bf550a633 100644 --- a/rpcs3/Emu/Cell/Modules/cellMouse.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMouse.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "Emu/IdManager.h" -#include "Emu/System.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Io/MouseHandler.h" diff --git a/rpcs3/Emu/Cell/Modules/cellMusic.cpp b/rpcs3/Emu/Cell/Modules/cellMusic.cpp index c517fe344d..157cf1906f 100644 --- a/rpcs3/Emu/Cell/Modules/cellMusic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMusic.cpp @@ -213,19 +213,29 @@ error_code cell_music_select_contents() error_code error = rsx::overlays::show_media_list_dialog(rsx::overlays::media_list_dialog::media_type::audio, vfs_dir_path, title, [&music](s32 status, utils::media_info info) { - sysutil_register_cb([&music, info, status](ppu_thread& ppu) -> s32 + sysutil_register_cb([&music, info = std::move(info), status](ppu_thread& ppu) -> s32 { std::lock_guard lock(music.mtx); const u32 result = status >= 0 ? u32{CELL_OK} : u32{CELL_MUSIC_CANCELED}; if (result == CELL_OK) { + // Let's always choose the whole directory for now + std::string track; + std::string dir = info.path; + if (fs::is_file(info.path)) + { + track = std::move(dir); + dir = fs::get_parent_dir(track); + } + music_selection_context context{}; - context.set_playlist(info.path); + context.set_playlist(dir); + context.set_track(track); // TODO: context.repeat_mode = CELL_SEARCH_REPEATMODE_NONE; // TODO: context.context_option = CELL_SEARCH_CONTEXTOPTION_NONE; - music.current_selection_context = context; + music.current_selection_context = std::move(context); music.current_selection_context.create_playlist(music_selection_context::get_next_hash()); - cellMusic.success("Media list dialog: selected entry '%s'", context.playlist.front()); + cellMusic.success("Media list dialog: selected entry '%s'", music.current_selection_context.playlist.front()); } else { @@ -556,7 +566,7 @@ error_code cellMusicSetPlaybackCommand2(s32 command, vm::ptr param) auto& music = g_fxo->get(); if (!music.func) - return CELL_MUSIC2_ERROR_GENERIC; + return { CELL_MUSIC2_ERROR_GENERIC, "Not initialized" }; error_code result = CELL_OK; @@ -585,7 +595,7 @@ error_code cellMusicSetPlaybackCommand(s32 command, vm::ptr param) auto& music = g_fxo->get(); if (!music.func) - return CELL_MUSIC_ERROR_GENERIC; + return { CELL_MUSIC_ERROR_GENERIC, "Not initialized" }; error_code result = CELL_OK; diff --git a/rpcs3/Emu/Cell/Modules/cellMusic.h b/rpcs3/Emu/Cell/Modules/cellMusic.h index d1cc13a08f..a98b305011 100644 --- a/rpcs3/Emu/Cell/Modules/cellMusic.h +++ b/rpcs3/Emu/Cell/Modules/cellMusic.h @@ -166,6 +166,7 @@ struct music_selection_context void set_playlist(const std::string& path); void create_playlist(const std::string& new_hash); bool load_playlist(); + void set_track(std::string_view track); u32 step_track(bool next); operator bool() const diff --git a/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp b/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp index 0880a5db3e..c938f723b1 100644 --- a/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp @@ -12,9 +12,6 @@ #include "cellSysutil.h" #include "util/media_utils.h" -#include - - LOG_CHANNEL(cellMusicDecode); template<> @@ -140,19 +137,29 @@ error_code cell_music_decode_select_contents() error_code error = rsx::overlays::show_media_list_dialog(rsx::overlays::media_list_dialog::media_type::audio, vfs_dir_path, title, [&dec](s32 status, utils::media_info info) { - sysutil_register_cb([&dec, info, status](ppu_thread& ppu) -> s32 + sysutil_register_cb([&dec, info = std::move(info), status](ppu_thread& ppu) -> s32 { std::lock_guard lock(dec.mutex); const u32 result = status >= 0 ? u32{CELL_OK} : u32{CELL_MUSIC_DECODE_CANCELED}; if (result == CELL_OK) { + // Let's always choose the whole directory for now + std::string track; + std::string dir = info.path; + if (fs::is_file(info.path)) + { + track = std::move(dir); + dir = fs::get_parent_dir(track); + } + music_selection_context context{}; - context.set_playlist(info.path); + context.set_playlist(dir); + context.set_track(track); // TODO: context.repeat_mode = CELL_SEARCH_REPEATMODE_NONE; // TODO: context.context_option = CELL_SEARCH_CONTEXTOPTION_NONE; - dec.current_selection_context = context; + dec.current_selection_context = std::move(context); dec.current_selection_context.create_playlist(music_selection_context::get_next_hash()); - cellMusicDecode.success("Media list dialog: selected entry '%s'", context.playlist.front()); + cellMusicDecode.success("Media list dialog: selected entry '%s'", dec.current_selection_context.playlist.front()); } else { diff --git a/rpcs3/Emu/Cell/Modules/cellMusicExport.cpp b/rpcs3/Emu/Cell/Modules/cellMusicExport.cpp index 752d7db394..c01d4b7a51 100644 --- a/rpcs3/Emu/Cell/Modules/cellMusicExport.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMusicExport.cpp @@ -2,7 +2,6 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/IdManager.h" #include "Emu/VFS.h" -#include "Utilities/StrUtil.h" #include "cellSysutil.h" LOG_CHANNEL(cellMusicExport); diff --git a/rpcs3/Emu/Cell/Modules/cellMusicSelectionContext.cpp b/rpcs3/Emu/Cell/Modules/cellMusicSelectionContext.cpp index 9582df894c..8de86380d8 100644 --- a/rpcs3/Emu/Cell/Modules/cellMusicSelectionContext.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMusicSelectionContext.cpp @@ -109,6 +109,8 @@ void music_selection_context::set_playlist(const std::string& path) content_type = CELL_SEARCH_CONTENTTYPE_MUSIC; playlist.push_back(dir_path + path.substr(vfs_dir_path.length())); } + + valid = true; } void music_selection_context::create_playlist(const std::string& new_hash) @@ -246,6 +248,29 @@ bool music_selection_context::load_playlist() return true; } +void music_selection_context::set_track(std::string_view track) +{ + if (track.empty()) return; + + if (playlist.empty()) + { + cellMusicSelectionContext.error("No tracks to play... (requested path='%s')", track); + return; + } + + for (usz i = 0; i < playlist.size(); i++) + { + cellMusicSelectionContext.error("Comparing track '%s' vs '%s'", track, playlist[i]); + if (track.ends_with(playlist[i])) + { + first_track = current_track = static_cast(i); + return; + } + } + + cellMusicSelectionContext.error("Track '%s' not found...", track); +} + u32 music_selection_context::step_track(bool next) { if (playlist.empty()) diff --git a/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp b/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp index e4b4fc82f0..97375c4e6d 100644 --- a/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp +++ b/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp @@ -2,7 +2,6 @@ #include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" #include "Emu/IdManager.h" -#include "Emu/Cell/lv2/sys_sync.h" #include "cellGame.h" #include "cellSysutil.h" diff --git a/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp b/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp index ecf20643ad..5b2c45e99c 100644 --- a/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp +++ b/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "Emu/System.h" -#include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Io/interception.h" #include "Emu/Io/Keyboard.h" diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 74b3c5fc60..39ee7d0359 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -3,7 +3,6 @@ #include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_process.h" -#include "Emu/Cell/lv2/sys_sync.h" #include "Emu/Io/pad_types.h" #include "Emu/RSX/Overlays/overlay_debug_overlay.h" #include "Input/pad_thread.h" diff --git a/rpcs3/Emu/Cell/Modules/cellPhotoDecode.cpp b/rpcs3/Emu/Cell/Modules/cellPhotoDecode.cpp index 843a0adc24..5cef4ba877 100644 --- a/rpcs3/Emu/Cell/Modules/cellPhotoDecode.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPhotoDecode.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" -#include "Emu/IdManager.h" #include "Emu/VFS.h" #include "Emu/System.h" #include "cellSysutil.h" diff --git a/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp b/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp index 34a1562153..8a264bc721 100644 --- a/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp @@ -2,7 +2,6 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/IdManager.h" #include "Emu/VFS.h" -#include "Utilities/StrUtil.h" #include "cellSysutil.h" LOG_CHANNEL(cellPhotoExport); diff --git a/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp b/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp index d1cbdc2b56..d56db1f060 100644 --- a/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPhotoImport.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/IdManager.h" #include "Emu/Cell/lv2/sys_fs.h" #include "Emu/RSX/Overlays/overlay_media_list_dialog.h" #include "Emu/VFS.h" diff --git a/rpcs3/Emu/Cell/Modules/cellPngEnc.cpp b/rpcs3/Emu/Cell/Modules/cellPngEnc.cpp index f723ddf888..c6da0c4e95 100644 --- a/rpcs3/Emu/Cell/Modules/cellPngEnc.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPngEnc.cpp @@ -2,7 +2,6 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/IdManager.h" #include "cellPngEnc.h" -#include "png.h" LOG_CHANNEL(cellPngEnc); diff --git a/rpcs3/Emu/Cell/Modules/cellResc.cpp b/rpcs3/Emu/Cell/Modules/cellResc.cpp index a3cc052d6f..065039b7ff 100644 --- a/rpcs3/Emu/Cell/Modules/cellResc.cpp +++ b/rpcs3/Emu/Cell/Modules/cellResc.cpp @@ -3,6 +3,7 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/RSX/GCM.h" +#include "Emu/RSX/gcm_enums.h" #include "cellResc.h" #include "cellVideoOut.h" diff --git a/rpcs3/Emu/Cell/Modules/cellRtc.cpp b/rpcs3/Emu/Cell/Modules/cellRtc.cpp index 487f9b1c19..fb92dd6f2c 100644 --- a/rpcs3/Emu/Cell/Modules/cellRtc.cpp +++ b/rpcs3/Emu/Cell/Modules/cellRtc.cpp @@ -78,7 +78,7 @@ error_code cellRtcGetCurrentTick(ppu_thread& ppu, vm::ptr pTick) error_code cellRtcGetCurrentClock(ppu_thread& ppu, vm::ptr pClock, s32 iTimeZone) { - cellRtc.notice("cellRtcGetCurrentClock(pClock=*0x%x, iTimeZone=%d)", pClock, iTimeZone); + cellRtc.trace("cellRtcGetCurrentClock(pClock=*0x%x, iTimeZone=%d)", pClock, iTimeZone); const vm::var page_attr; @@ -1505,7 +1505,7 @@ error_code cellRtcGetSystemTime(ppu_thread& ppu, vm::cptr pDate error_code cellRtcGetTime_t(ppu_thread& ppu, vm::cptr pDateTime, vm::ptr piTime) { - cellRtc.notice("cellRtcGetTime_t(pDateTime=*0x%x, piTime=*0x%x)", pDateTime, piTime); + cellRtc.trace("cellRtcGetTime_t(pDateTime=*0x%x, piTime=*0x%x)", pDateTime, piTime); const vm::var page_attr; diff --git a/rpcs3/Emu/Cell/Modules/cellSail.h b/rpcs3/Emu/Cell/Modules/cellSail.h index 7cad924f6a..2b34e015b4 100644 --- a/rpcs3/Emu/Cell/Modules/cellSail.h +++ b/rpcs3/Emu/Cell/Modules/cellSail.h @@ -2,8 +2,6 @@ #include "cellVpost.h" -#include "Emu/Memory/vm_ptr.h" - // Error Codes enum CellSailError : u32 { diff --git a/rpcs3/Emu/Cell/Modules/cellSailRec.cpp b/rpcs3/Emu/Cell/Modules/cellSailRec.cpp index e6d7d58e5d..c7a3cba549 100644 --- a/rpcs3/Emu/Cell/Modules/cellSailRec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSailRec.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" -#include "cellSail.h" LOG_CHANNEL(cellSailRec); diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp index 4ce8be031c..be7583074a 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp @@ -8,6 +8,7 @@ #include "Emu/Cell/lv2/sys_sync.h" #include "Emu/Cell/lv2/sys_process.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/timers.hpp" #include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/Cell/Modules/cellUserInfo.h" #include "Emu/RSX/Overlays/overlay_message.h" @@ -19,6 +20,7 @@ #include "Loader/PSF.h" #include "Utilities/StrUtil.h" #include "Utilities/date_time.h" +#include "Utilities/sema.h" #include #include diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.h b/rpcs3/Emu/Cell/Modules/cellSaveData.h index c88a4a3f9a..73b0af4f01 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.h +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.h @@ -1,7 +1,6 @@ #pragma once #include "util/types.hpp" -#include "util/endian.hpp" #include "Emu/Memory/vm_ptr.h" #include #include diff --git a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp index 8f3469e031..e35d154eef 100644 --- a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "Emu/System.h" #include "Emu/system_config.h" -#include "Emu/IdManager.h" #include "Emu/Memory/vm_reservation.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/SPUThread.h" diff --git a/rpcs3/Emu/Cell/Modules/cellSpurs.h b/rpcs3/Emu/Cell/Modules/cellSpurs.h index b06cbd079f..f4be6d84d6 100644 --- a/rpcs3/Emu/Cell/Modules/cellSpurs.h +++ b/rpcs3/Emu/Cell/Modules/cellSpurs.h @@ -4,6 +4,10 @@ #include "util/v128.hpp" +#include "Emu/Cell/lv2/sys_lwmutex.h" +#include "Emu/Cell/lv2/sys_lwcond.h" +#include "Emu/Cell/lv2/sys_spu.h" + struct CellSpurs; struct CellSpursTaskset; diff --git a/rpcs3/Emu/Cell/Modules/cellSpursJq.cpp b/rpcs3/Emu/Cell/Modules/cellSpursJq.cpp index dfbc3e0966..b2cb3e4869 100644 --- a/rpcs3/Emu/Cell/Modules/cellSpursJq.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSpursJq.cpp @@ -1,9 +1,6 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" -#include "Emu/Cell/lv2/sys_spu.h" -#include "cellSpursJq.h" - LOG_CHANNEL(cellSpursJq); error_code cellSpursJobQueueAttributeInitialize() diff --git a/rpcs3/Emu/Cell/Modules/cellSpursSpu.cpp b/rpcs3/Emu/Cell/Modules/cellSpursSpu.cpp index 435fa1636d..43b3e04c8b 100644 --- a/rpcs3/Emu/Cell/Modules/cellSpursSpu.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSpursSpu.cpp @@ -4,9 +4,6 @@ #include "Emu/Memory/vm_reservation.h" #include "Emu/Cell/SPUThread.h" #include "Emu/Cell/SPURecompiler.h" -#include "Emu/Cell/lv2/sys_lwmutex.h" -#include "Emu/Cell/lv2/sys_lwcond.h" -#include "Emu/Cell/lv2/sys_spu.h" #include "cellSpurs.h" #include "util/asm.hpp" diff --git a/rpcs3/Emu/Cell/Modules/cellVdec.cpp b/rpcs3/Emu/Cell/Modules/cellVdec.cpp index 7ba9644d15..4574d99d59 100644 --- a/rpcs3/Emu/Cell/Modules/cellVdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVdec.cpp @@ -1619,10 +1619,25 @@ error_code cellVdecSetFrameRate(u32 handle, CellVdecFrameRate frameRateCode) return CELL_OK; } -error_code cellVdecOpenExt() +error_code cellVdecOpenExt(ppu_thread& ppu, vm::cptr type, vm::cptr res, vm::cptr cb, vm::ptr handle) { - UNIMPLEMENTED_FUNC(cellVdec); - return CELL_OK; + cellVdec.warning("cellVdecOpenExt(type=*0x%x, res=*0x%x, cb=*0x%x, handle=*0x%x)", type, res, cb, handle); + + if (!res) + { + return CELL_VDEC_ERROR_ARG; + } + + vm::var tmp = vm::make_var({}); + tmp->memAddr = res->memAddr; + tmp->memSize = res->memSize; + tmp->ppuThreadPriority = res->ppuThreadPriority; + tmp->ppuThreadStackSize = res->ppuThreadStackSize; + tmp->spuThreadPriority = 0; + tmp->numOfSpus = res->numOfSpus; + + const vm::ptr ptr = vm::cast(tmp.addr()); + return vdecOpen(ppu, type, ptr, cb, handle); } error_code cellVdecStartSeqExt() diff --git a/rpcs3/Emu/Cell/Modules/cellVdec.h b/rpcs3/Emu/Cell/Modules/cellVdec.h index e09695bcc3..9f30b7d1b0 100644 --- a/rpcs3/Emu/Cell/Modules/cellVdec.h +++ b/rpcs3/Emu/Cell/Modules/cellVdec.h @@ -133,6 +133,16 @@ struct CellVdecResourceEx be_t spursResource_addr; }; +struct CellVdecResourceExt // speculative +{ + be_t memAddr; + be_t memSize; + be_t ppuThreadPriority; + be_t ppuThreadStackSize; + u8 unk[12]; + be_t numOfSpus; +}; + // Access Unit Information struct CellVdecAuInfo { diff --git a/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp b/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp index 8ee6238c62..8c0ed93771 100644 --- a/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp @@ -2,7 +2,6 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/IdManager.h" #include "Emu/VFS.h" -#include "Utilities/StrUtil.h" #include "cellSysutil.h" LOG_CHANNEL(cellVideoExport); diff --git a/rpcs3/Emu/Cell/Modules/cellVoice.h b/rpcs3/Emu/Cell/Modules/cellVoice.h index 80fac88c29..2b269f50c6 100644 --- a/rpcs3/Emu/Cell/Modules/cellVoice.h +++ b/rpcs3/Emu/Cell/Modules/cellVoice.h @@ -1,8 +1,5 @@ #pragma once -#include -#include - // libvoice = 0x80310801 - 0x803108ff // libvoice version 100 diff --git a/rpcs3/Emu/Cell/Modules/cellWebBrowser.cpp b/rpcs3/Emu/Cell/Modules/cellWebBrowser.cpp index 8a9991b089..7f560c9ca3 100644 --- a/rpcs3/Emu/Cell/Modules/cellWebBrowser.cpp +++ b/rpcs3/Emu/Cell/Modules/cellWebBrowser.cpp @@ -4,6 +4,8 @@ #include "cellWebBrowser.h" #include "Emu/IdManager.h" +#include "cellSysutil.h" + LOG_CHANNEL(cellSysutil); struct browser_info diff --git a/rpcs3/Emu/Cell/Modules/cellWebBrowser.h b/rpcs3/Emu/Cell/Modules/cellWebBrowser.h index 7a66f5b421..6c46c581b1 100644 --- a/rpcs3/Emu/Cell/Modules/cellWebBrowser.h +++ b/rpcs3/Emu/Cell/Modules/cellWebBrowser.h @@ -1,7 +1,5 @@ #pragma once -#include "cellSysutil.h" - #include "Emu/Memory/vm_ptr.h" //events diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index fb9f46a365..9a8e0dba6b 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "Emu/System.h" #include "Emu/system_utils.hpp" -#include "Emu/VFS.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/Modules/cellUserInfo.h" #include "Emu/Io/interception.h" @@ -15,6 +14,7 @@ #include "sceNp.h" #include "cellSysutil.h" +#include "Emu/Cell/timers.hpp" #include "Emu/Cell/lv2/sys_time.h" #include "Emu/Cell/lv2/sys_fs.h" #include "Emu/Cell/lv2/sys_sync.h" @@ -22,6 +22,7 @@ #include "Emu/NP/np_contexts.h" #include "Emu/NP/np_helpers.h" #include "Emu/NP/np_structs_extra.h" +#include "Emu/NP/signaling_handler.h" #include "Emu/system_config.h" #include "Emu/RSX/Overlays/overlay_manager.h" diff --git a/rpcs3/Emu/Cell/Modules/sceNp.h b/rpcs3/Emu/Cell/Modules/sceNp.h index e6a1bdc7ea..bc2ab7c6e2 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.h +++ b/rpcs3/Emu/Cell/Modules/sceNp.h @@ -1,7 +1,9 @@ #pragma once +#include "Emu/NP/rpcn_types.h" #include "cellRtc.h" #include "Emu/Cell/ErrorCodes.h" +#include "util/shared_ptr.hpp" #include @@ -1825,7 +1827,7 @@ public: virtual ~SendMessageDialogBase() = default; virtual error_code Exec(message_data& msg_data, std::set& npids) = 0; - virtual void callback_handler(u16 ntype, const std::string& username, bool status) = 0; + virtual void callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status) = 0; protected: std::shared_ptr m_rpcn; diff --git a/rpcs3/Emu/Cell/Modules/sceNp2.cpp b/rpcs3/Emu/Cell/Modules/sceNp2.cpp index 174a9ec0ce..ed4655ccd4 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp2.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp2.cpp @@ -7,6 +7,7 @@ #include "Emu/NP/np_handler.h" #include "Emu/NP/np_contexts.h" #include "Emu/NP/np_helpers.h" +#include "Emu/NP/signaling_handler.h" #include "cellSysutil.h" LOG_CHANNEL(sceNp2); diff --git a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp index 3be0571052..0fc2cc5052 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp @@ -4,6 +4,7 @@ #include "Emu/VFS.h" #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/timers.hpp" #include "Emu/Cell/Modules/cellMsgDialog.h" #include "Utilities/rXml.h" diff --git a/rpcs3/Emu/Cell/Modules/sceNpTrophy.h b/rpcs3/Emu/Cell/Modules/sceNpTrophy.h index f97050c968..dfea517fe0 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpTrophy.h +++ b/rpcs3/Emu/Cell/Modules/sceNpTrophy.h @@ -1,10 +1,10 @@ #pragma once #include "util/types.hpp" -#include "util/endian.hpp" #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/ErrorCodes.h" #include +#include // Error codes enum SceNpTrophyError : u32 diff --git a/rpcs3/Emu/Cell/Modules/sys_heap.cpp b/rpcs3/Emu/Cell/Modules/sys_heap.cpp index a502e26486..c0ba4fb2da 100644 --- a/rpcs3/Emu/Cell/Modules/sys_heap.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_heap.cpp @@ -2,8 +2,6 @@ #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" -#include "sysPrxForUser.h" - LOG_CHANNEL(sysPrxForUser); struct HeapInfo diff --git a/rpcs3/Emu/Cell/Modules/sys_lwmutex_.cpp b/rpcs3/Emu/Cell/Modules/sys_lwmutex_.cpp index a9ec0b5b3d..098761b259 100644 --- a/rpcs3/Emu/Cell/Modules/sys_lwmutex_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_lwmutex_.cpp @@ -2,7 +2,7 @@ #include "Emu/System.h" #include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" - +#include "Emu/Cell/timers.hpp" #include "Emu/Cell/lv2/sys_lwmutex.h" #include "Emu/Cell/lv2/sys_mutex.h" #include "sysPrxForUser.h" diff --git a/rpcs3/Emu/Cell/Modules/sys_mempool.cpp b/rpcs3/Emu/Cell/Modules/sys_mempool.cpp index b7ffa72984..2446619d3a 100644 --- a/rpcs3/Emu/Cell/Modules/sys_mempool.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_mempool.cpp @@ -7,8 +7,6 @@ #include "Emu/Cell/lv2/sys_mutex.h" #include "Emu/Cell/lv2/sys_cond.h" -#include "sysPrxForUser.h" - LOG_CHANNEL(sysPrxForUser); using sys_mempool_t = u32; diff --git a/rpcs3/Emu/Cell/Modules/sys_net_.cpp b/rpcs3/Emu/Cell/Modules/sys_net_.cpp index f566bd2070..9e487aa8eb 100644 --- a/rpcs3/Emu/Cell/Modules/sys_net_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_net_.cpp @@ -1,7 +1,5 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" -#include "Emu/IdManager.h" - #include "sys_net_.h" LOG_CHANNEL(libnet); diff --git a/rpcs3/Emu/Cell/Modules/sys_net_.h b/rpcs3/Emu/Cell/Modules/sys_net_.h index c49a65e26d..9ee344b977 100644 --- a/rpcs3/Emu/Cell/Modules/sys_net_.h +++ b/rpcs3/Emu/Cell/Modules/sys_net_.h @@ -1,7 +1,6 @@ #pragma once #include "Emu/Cell/lv2/sys_net.h" -#include "Emu/Memory/vm.h" struct sys_net_sockinfo_t { diff --git a/rpcs3/Emu/Cell/Modules/sys_ppu_thread_.cpp b/rpcs3/Emu/Cell/Modules/sys_ppu_thread_.cpp index cb3f5f62d1..033242e87c 100644 --- a/rpcs3/Emu/Cell/Modules/sys_ppu_thread_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_ppu_thread_.cpp @@ -1,7 +1,5 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" -#include "Emu/IdManager.h" - #include "Emu/Cell/lv2/sys_ppu_thread.h" #include "Emu/Cell/lv2/sys_interrupt.h" #include "Emu/Cell/lv2/sys_lwmutex.h" diff --git a/rpcs3/Emu/Cell/Modules/sys_rsxaudio_.cpp b/rpcs3/Emu/Cell/Modules/sys_rsxaudio_.cpp index d686345738..071a9debb6 100644 --- a/rpcs3/Emu/Cell/Modules/sys_rsxaudio_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_rsxaudio_.cpp @@ -1,8 +1,6 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" -#include "sysPrxForUser.h" - LOG_CHANNEL(sysPrxForUser); error_code sys_rsxaudio_close_connection() diff --git a/rpcs3/Emu/Cell/Modules/sys_spinlock.cpp b/rpcs3/Emu/Cell/Modules/sys_spinlock.cpp index e49998029c..00a71de061 100644 --- a/rpcs3/Emu/Cell/Modules/sys_spinlock.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_spinlock.cpp @@ -1,8 +1,6 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" -#include "sysPrxForUser.h" - LOG_CHANNEL(sysPrxForUser); void sys_spinlock_initialize(vm::ptr> lock) diff --git a/rpcs3/Emu/Cell/Modules/sys_spu_.cpp b/rpcs3/Emu/Cell/Modules/sys_spu_.cpp index 2c584a7fed..2bbbaee4ae 100644 --- a/rpcs3/Emu/Cell/Modules/sys_spu_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_spu_.cpp @@ -3,7 +3,6 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_spu.h" -#include "Crypto/unself.h" #include "Loader/ELF.h" #include "sysPrxForUser.h" diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 1bab12a109..a936eb6853 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -21,7 +21,6 @@ void fmt_class_string::format(std::string& out, u64 arg) { switch (value) { - case ppu_attr::known_addr: return "known_addr"; case ppu_attr::known_size: return "known_size"; case ppu_attr::no_return: return "no_return"; case ppu_attr::no_size: return "no_size"; @@ -532,10 +531,292 @@ namespace ppu_patterns }; } +static constexpr struct const_tag{} is_const; +static constexpr struct range_tag{} is_range; +static constexpr struct min_value_tag{} minv; +static constexpr struct max_value_tag{} maxv; +static constexpr struct load_addr_tag{} load_addrv; + +struct reg_state_t +{ + u64 ge_than; + u64 value_range; + u64 bit_range; + bool is_loaded; // Is loaded from memory(?) (this includes offsetting from that address) + u32 tag; + + // Check if state is a constant value + bool operator()(const_tag) const + { + return value_range == 1 && bit_range == 0; + } + + // Check if state is a ranged value + bool operator()(range_tag) const + { + return bit_range == 0; + } + + // Get minimum bound + u64 operator()(min_value_tag) const + { + return value_range ? ge_than : 0; + } + + // Get maximum bound + u64 operator()(max_value_tag) const + { + return value_range ? (ge_than | bit_range) + value_range : 0; + } + + u64 operator()(load_addr_tag) const + { + return is_loaded ? ge_than : 0; + } + + // Check if value is of the same origin + bool is_equals(const reg_state_t& rhs) const + { + return (rhs.tag && rhs.tag == this->tag) || (rhs(is_const) && (*this)(is_const) && rhs.ge_than == ge_than); + } + + void set_lower_bound(u64 value) + { + const u64 prev_max = ge_than + value_range; + ge_than = value; + value_range = prev_max - ge_than; + } + + // lower_bound = Max(bounds) + void lift_lower_bound(u64 value) + { + const u64 prev_max = ge_than + value_range; + + // Get the value closer to upper bound (may be lower in value) + // Make 0 underflow (it is actually not 0 but UINT64_MAX + 1 in this context) + ge_than = value_range - 1 > prev_max - value - 1 ? value : ge_than; + + value_range = prev_max - ge_than; + } + + // Upper bound is not inclusive + void set_upper_bound(u64 value) + { + value_range = value - ge_than; + } + + // upper_bound = Min(bounds) + void limit_upper_bound(u64 value) + { + // Make 0 underflow (it is actually not 0 but UINT64_MAX + 1 in this context) + value_range = std::min(value - ge_than - 1, value_range - 1) + 1; + } + + // Clear bits using mask + // May fail if ge_than(+)value_range is modified by the operation + bool clear_mask(u64 bit_mask, u32& reg_tag_allocator) + { + if (bit_mask == umax) + { + return true; + } + + if ((ge_than & bit_mask) > ~value_range) + { + // Discard data: mask clears the carry bit + value_range = 0; + tag = reg_tag_allocator++; + return false; + } + + if (((ge_than & bit_mask) + value_range) & ~bit_mask) + { + // Discard data: mask clears range bits + value_range = 0; + tag = reg_tag_allocator++; + return false; + } + + ge_than &= bit_mask; + bit_range &= bit_mask; + return true; + } + + bool clear_lower_bits(u64 bits, u32& reg_tag_allocator) + { + const u64 bit_mask = ~((u64{1} << bits) - 1); + return clear_mask(bit_mask, reg_tag_allocator); + } + + bool clear_higher_bits(u64 bits, u32& reg_tag_allocator) + { + const u64 bit_mask = (u64{1} << ((64 - bits) % 64)) - 1; + return clear_mask(bit_mask, reg_tag_allocator); + } + + // Undefine bits using mask + void undef_mask(u64 bit_mask) + { + ge_than &= bit_mask; + bit_range |= ~bit_mask; + } + + void undef_lower_bits(u64 bits) + { + const u64 bit_mask = ~((u64{1} << bits) - 1); + undef_mask(bit_mask); + } + + void undef_higher_bits(u64 bits) + { + const u64 bit_mask = (u64{1} << ((64 - bits) % 64)) - 1; + undef_mask(bit_mask); + } + + // Add value to state, respecting of bit_range + void add(u64 value) + { + const u64 old_ge = ge_than; + ge_than += value; + + // Adjust bit_range to undefine bits that their state may not be defined anymore + // No need to adjust value_range at the moment (wrapping around is implied) + bit_range |= ((old_ge | bit_range) + value) ^ ((old_ge) + value); + + ge_than &= ~bit_range; + } + + void sub(u64 value) + { + // This function should be perfect, so it handles subtraction as well + add(~value + 1); + } + + // TODO: For CMP/CMPD value fixup + bool can_subtract_from_both_without_loss_of_meaning(const reg_state_t& rhs, u64 value) + { + const reg_state_t& lhs = *this; + + return lhs.ge_than >= value && rhs.ge_than >= value && !!((lhs.ge_than - value) & lhs.bit_range) && !!((rhs.ge_than - value) & rhs.bit_range); + } + + // Bitwise shift left + bool shift_left(u64 value, u32& reg_tag_allocator) + { + if (!value || !value_range) + { + return true; + } + + const u64 mask_out = u64{umax} >> value; + + if ((ge_than & mask_out) > ~value_range) + { + // Discard data: shift clears the carry bit + value_range = 0; + tag = reg_tag_allocator++; + return false; + } + + if (((ge_than & mask_out) + value_range) & ~mask_out) + { + // Discard data: shift clears range bits + value_range = 0; + tag = reg_tag_allocator++; + return false; + } + + ge_than <<= value; + bit_range <<= value; + value_range = ((value_range - 1) << value) + 1; + return true; + } + + void load_const(u64 value) + { + ge_than = value; + value_range = 1; + is_loaded = false; + tag = 0; + bit_range = 0; + } + + u64 upper_bound() const + { + return ge_than + value_range; + } + + // Using comparison evaluation data to bound data to our needs + void declare_ordering(reg_state_t& right_register, bool lt, bool eq, bool gt, bool so, bool nso) + { + if (lt && gt) + { + // We don't care about inequality at the moment + + // Except for 0 + if (right_register.value_range == 1 && right_register.ge_than == 0) + { + lift_lower_bound(1); + } + else if (value_range == 1 && ge_than == 0) + { + right_register.lift_lower_bound(1); + } + + return; + } + + if (lt || gt) + { + reg_state_t* rhs = &right_register; + reg_state_t* lhs = this; + + // This is written as if for operator< + if (gt) + { + std::swap(rhs, lhs); + } + + if (lhs->value_range == 1) + { + rhs->lift_lower_bound(lhs->upper_bound() - (eq ? 1 : 0)); + } + else if (rhs->value_range == 1) + { + lhs->limit_upper_bound(rhs->ge_than + (eq ? 1 : 0)); + } + } + else if (eq) + { + if (value_range == 1) + { + right_register = *this; + } + else if (right_register.value_range == 1) + { + *this = right_register; + } + else if (0) + { + // set_lower_bound(std::max(ge_than, right_register.ge_than)); + // set_upper_bound(std::min(value_range + ge_than, right_register.ge_than + right_register.value_range)); + // right_register.ge_than = ge_than; + // right_register.value_range = value_range; + } + } + else if (so || nso) + { + // TODO: Implement(?) + } + } +}; + +static constexpr reg_state_t s_reg_const_0{ 0, 1 }; + template <> bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::vector& applied, const std::vector& exported_funcs, std::function check_aborted) { - if (segs.empty()) + if (segs.empty() || !segs[0].addr) { return false; } @@ -546,36 +827,64 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con // End of executable segment (may change) u32 end = sec_end ? sec_end : segs[0].addr + segs[0].size; + // End of all segments + u32 segs_end = end; + + for (const auto& s : segs) + { + if (s.size && s.addr != start) + { + segs_end = std::max(segs_end, s.addr + s.size); + } + } + // Known TOCs (usually only 1) std::unordered_set TOCs; + struct ppu_function_ext : ppu_function + { + //u32 stack_frame = 0; + u32 single_target = 0; + u32 trampoline = 0; + bs_t attr{}; + + std::set callers{}; + }; + // Known functions - std::map fmap; + std::map fmap; std::set known_functions; // Function analysis workload - std::vector> func_queue; + std::vector> func_queue; // Known references (within segs, addr and value alignment = 4) - std::set addr_heap; + // For seg0, must be valid code + // Value is a sample of an address that refernces it + std::map addr_heap; if (entry) { - addr_heap.emplace(entry); + addr_heap.emplace(entry, 0); } - auto verify_func = [&](u32 addr) + auto verify_ref = [&](u32 addr) { - if (entry) + if (!is_relocatable) { // Fixed addresses return true; } // Check if the storage address exists within relocations + constexpr auto compare = [](const ppu_reloc& a, u32 addr) { return a.addr < addr; }; + auto it = std::lower_bound(this->relocs.begin(), this->relocs.end(), (addr & -8), compare); + auto end = std::lower_bound(it, this->relocs.end(), (addr & -8) + 8, compare); - for (auto& rel : this->relocs) + for (; it != end; it++) { + const ppu_reloc& rel = *it; + if ((rel.addr & -8) == (addr & -8)) { if (rel.type != 38 && rel.type != 44 && (rel.addr & -4) != (addr & -4)) @@ -607,10 +916,61 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con return true; }; - // Register new function - auto add_func = [&](u32 addr, u32 toc, u32 caller) -> ppu_function& + auto is_valid_code = [](std::span> range, bool is_fixed_addr, u32 /*cia*/) { - ppu_function& func = fmap[addr]; + for (usz index = 0; index < std::min(range.size(), 10); index++) + { + const ppu_opcode_t op{+range[index]}; + + switch (s_ppu_itype.decode(op.opcode)) + { + case ppu_itype::UNK: + { + return false; + } + case ppu_itype::BC: + case ppu_itype::B: + { + if (!is_fixed_addr && op.aa) + { + return false; + } + + return true; + } + case ppu_itype::BCCTR: + case ppu_itype::BCLR: + { + if (op.opcode & 0xe000) + { + // Garbage filter + return false; + } + + return true; + } + default: + { + continue; + } + } + } + + return true; + }; + + // Register new function + auto add_func = [&](u32 addr, u32 toc, u32 caller) -> ppu_function_ext& + { + if (addr < start || addr >= end || s_ppu_itype.decode(*get_ptr(addr)) == ppu_itype::UNK) + { + if (!fmap.contains(addr)) + { + ppu_log.error("Potentially invalid function has been added: 0x%x", addr); + } + } + + ppu_function_ext& func = fmap[addr]; if (caller) { @@ -668,7 +1028,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con for (; _ptr <= seg_end;) { - if (ptr[1] == toc && FN(x >= start && x < end && x % 4 == 0)(ptr[0]) && verify_func(_ptr.addr())) + if (ptr[1] == toc && FN(x >= start && x < end && x % 4 == 0)(ptr[0]) && verify_ref(_ptr.addr())) { // New function ppu_log.trace("OPD*: [0x%x] 0x%x (TOC=0x%x)", _ptr, ptr[0], ptr[1]); @@ -691,6 +1051,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con }; // Find references indiscriminately + // For seg0, must be valid code for (const auto& seg : segs) { if (seg.size < 4) continue; @@ -703,18 +1064,23 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con { const u32 value = *ptr; - if (value % 4) + if (value % 4 || !verify_ref(_ptr.addr())) { continue; } for (const auto& _seg : segs) { - if (!_seg.addr) continue; + if (!_seg.size) continue; if (value >= start && value < end) { - addr_heap.emplace(value); + if (is_valid_code({ ptr, ptr + (end - value) }, !is_relocatable, _ptr.addr())) + { + continue; + } + + addr_heap.emplace(value, _ptr.addr()); break; } } @@ -762,7 +1128,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con break; } - if (addr % 4 || addr < start || addr >= end || !verify_func(_ptr.addr())) + if (addr % 4 || addr < start || addr >= end || !verify_ref(_ptr.addr())) { sec_end.set(0); break; @@ -785,8 +1151,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con ppu_log.trace("OPD: [0x%x] 0x%x (TOC=0x%x)", _ptr, addr, toc); TOCs.emplace(toc); - auto& func = add_func(addr, addr_heap.count(_ptr.addr()) ? toc : 0, 0); - func.attr += ppu_attr::known_addr; + add_func(addr, addr_heap.count(_ptr.addr()) ? toc : 0, 0); known_functions.emplace(addr); } } @@ -924,7 +1289,6 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con } //auto& func = add_func(addr, 0, 0); - //func.attr += ppu_attr::known_addr; //func.attr += ppu_attr::known_size; //func.size = size; //known_functions.emplace(func); @@ -975,14 +1339,13 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con const ppu_opcode_t op{*ptr}; const ppu_itype::type type = s_ppu_itype.decode(op.opcode); - if ((type == ppu_itype::B || type == ppu_itype::BC) && op.lk && (!op.aa || verify_func(iaddr))) + if ((type == ppu_itype::B || type == ppu_itype::BC) && op.lk && (!op.aa || verify_ref(iaddr))) { const u32 target = (op.aa ? 0 : iaddr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); if (target >= start && target < end && target != iaddr && target != iaddr + 4) { - // TODO: Check full executability - if (s_ppu_itype.decode(get_ref(target)) != ppu_itype::UNK) + if (is_valid_code({ get_ptr(target), get_ptr(end - 4) }, !is_relocatable, target)) { ppu_log.trace("Enqueued PPU function 0x%x using a caller at 0x%x", target, iaddr); add_func(target, 0, 0); @@ -993,6 +1356,22 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con } } + // Register state (preallocated) + // Make sure to re-initialize this for every function! + std::vector reg_state_storage(64 * (is_relocatable ? 256 : 1024)); + + struct block_local_info_t + { + u32 addr = 0; + u32 size = 0; + u32 parent_block_idx = umax; + u64 mapped_registers_mask = 0; + u64 moved_registers_mask = 0; + }; + + // Block analysis workload + std::vector block_queue_storage; + // Main loop (func_queue may grow) for (usz i = 0; i < func_queue.size(); i++) { @@ -1001,14 +1380,15 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con return false; } - ppu_function& func = func_queue[i]; + ppu_function_ext& func = func_queue[i].get(); // Fixup TOCs if (func.toc && func.toc != umax) { + // Fixup callers for (u32 addr : func.callers) { - ppu_function& caller = fmap[addr]; + ppu_function_ext& caller = fmap[addr]; if (!caller.toc) { @@ -1016,13 +1396,59 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con } } - for (u32 addr : func.calls) + // Fixup callees + auto for_callee = [&](u32 addr) { - ppu_function& callee = fmap[addr]; + if (addr < func.addr || addr >= func.addr + func.size) + { + return; + } + + const u32 iaddr = addr; + + const ppu_opcode_t op{get_ref(iaddr)}; + const ppu_itype::type type = s_ppu_itype.decode(op.opcode); + + if (type == ppu_itype::B || type == ppu_itype::BC) + { + const u32 target = (op.aa ? 0 : iaddr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); + + if (target >= start && target < end && (!op.aa || verify_ref(iaddr))) + { + if (target < func.addr || target >= func.addr + func.size) + { + ppu_function_ext& callee = fmap[target]; + + if (!callee.toc) + { + add_func(target, func.toc + func.trampoline, 0); + } + } + } + } + }; + + for (const auto& [addr, size] : func.blocks) + { + if (size) + { + for_callee(addr + size - 4); + } + } + + if (func.size) + { + for_callee(func.addr + func.size - 4); + } + + // For trampoline functions + if (func.single_target) + { + ppu_function_ext& callee = fmap[func.single_target]; if (!callee.toc) { - add_func(addr, func.toc + func.trampoline, 0); + add_func(func.single_target, func.toc + func.trampoline, 0); } } } @@ -1050,7 +1476,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con continue; } - if (target >= start && target < end && (~ptr[0] & 0x2 || verify_func(_ptr.addr()))) + if (target >= start && target < end && (~ptr[0] & 0x2 || verify_ref(_ptr.addr()))) { auto& new_func = add_func(target, func.toc, func.addr); @@ -1063,7 +1489,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con func.size = 0x4; func.blocks.emplace(func.addr, func.size); func.attr += new_func.attr & ppu_attr::no_return; - func.calls.emplace(target); + func.single_target = target; func.trampoline = 0; continue; } @@ -1078,7 +1504,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con // Simple trampoline const u32 target = (ptr[0] << 16) + ppu_opcode_t{ptr[1]}.simm16; - if (target >= start && target < end && verify_func(_ptr.addr())) + if (target >= start && target < end && verify_ref(_ptr.addr())) { auto& new_func = add_func(target, func.toc, func.addr); @@ -1091,7 +1517,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con func.size = 0x10; func.blocks.emplace(func.addr, func.size); func.attr += new_func.attr & ppu_attr::no_return; - func.calls.emplace(target); + func.single_target = target; func.trampoline = 0; continue; } @@ -1109,8 +1535,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con func.toc = -1; func.size = 0x1C; func.blocks.emplace(func.addr, func.size); - func.attr += ppu_attr::known_addr; func.attr += ppu_attr::known_size; + known_functions.emplace(func.addr); // Look for another imports to fill gaps (hack) auto _p2 = _ptr + 7; @@ -1128,8 +1554,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con auto& next = add_func(_p2.addr(), -1, func.addr); next.size = 0x1C; next.blocks.emplace(next.addr, next.size); - next.attr += ppu_attr::known_addr; next.attr += ppu_attr::known_size; + known_functions.emplace(_p2.addr()); advance(_p2, p2, 7); } @@ -1149,7 +1575,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con const u32 target = (ptr[3] << 16) + s16(ptr[4]); const u32 toc_add = (ptr[1] << 16) + s16(ptr[2]); - if (target >= start && target < end && verify_func((_ptr + 3).addr())) + if (target >= start && target < end && verify_ref((_ptr + 3).addr())) { auto& new_func = add_func(target, 0, func.addr); @@ -1180,7 +1606,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con func.size = 0x1C; func.blocks.emplace(func.addr, func.size); func.attr += new_func.attr & ppu_attr::no_return; - func.calls.emplace(target); + func.single_target = target; func.trampoline = toc_add; continue; } @@ -1196,7 +1622,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con const u32 toc_add = (ptr[1] << 16) + s16(ptr[2]); const u32 target = (ptr[3] & 0x2 ? 0 : (_ptr + 3).addr()) + ppu_opcode_t{ptr[3]}.bt24; - if (target >= start && target < end && (~ptr[3] & 0x2 || verify_func((_ptr + 3).addr()))) + if (target >= start && target < end && (~ptr[3] & 0x2 || verify_ref((_ptr + 3).addr()))) { auto& new_func = add_func(target, 0, func.addr); @@ -1227,7 +1653,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con func.size = 0x10; func.blocks.emplace(func.addr, func.size); func.attr += new_func.attr & ppu_attr::no_return; - func.calls.emplace(target); + func.single_target = target; func.trampoline = toc_add; continue; } @@ -1247,7 +1673,6 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con func.toc = -1; func.size = 0x20; func.blocks.emplace(func.addr, func.size); - func.attr += ppu_attr::known_addr; known_functions.emplace(func.addr); func.attr += ppu_attr::known_size; @@ -1268,7 +1693,6 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con auto& next = add_func(_p2.addr(), -1, func.addr); next.size = 0x20; next.blocks.emplace(next.addr, next.size); - next.attr += ppu_attr::known_addr; next.attr += ppu_attr::known_size; advance(_p2, p2, 8); known_functions.emplace(next.addr); @@ -1307,33 +1731,49 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con // Get function limit const u32 func_end = std::min(get_limit(func.addr + 1), func.attr & ppu_attr::known_size ? func.addr + func.size : end); - // Block analysis workload - std::vector>> block_queue; + auto& block_queue = block_queue_storage; + block_queue.clear(); + u32 reg_tag_allocator = 1; + + auto make_unknown_reg_state = [&]() + { + reg_state_t s{}; + s.tag = reg_tag_allocator++; + return s; + }; // Add new block for analysis - auto add_block = [&](u32 addr) -> bool + auto add_block = [&](u32 addr, u32 parent_block) -> u32 { if (addr < func.addr || addr >= func_end) { - return false; + return umax; } const auto _pair = func.blocks.emplace(addr, 0); if (_pair.second) { - block_queue.emplace_back(*_pair.first); - return true; + auto& block = block_queue.emplace_back(block_local_info_t{addr}); + block.parent_block_idx = parent_block; + + if (parent_block != umax) + { + // Inherit loaded registers mask (lazily) + block.mapped_registers_mask = ::at32(block_queue, parent_block).mapped_registers_mask; + } + + return static_cast(block_queue.size() - 1); } - return false; + return umax; }; for (auto& block : func.blocks) { if (!block.second && block.first < func_end) { - block_queue.emplace_back(block); + block_queue.emplace_back(block_local_info_t{block.first}); } } @@ -1347,31 +1787,137 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con const u32 func_end2 = _next == fmap.end() ? func_end : std::min(_next->first, func_end); // Set more block entries - std::for_each(addr_heap.lower_bound(func.addr), addr_heap.lower_bound(func_end2), add_block); + std::for_each(addr_heap.lower_bound(func.addr), addr_heap.lower_bound(func_end2), [&](auto a) { add_block(a.first, umax); }); } - const bool was_empty = block_queue.empty(); + bool postpone_analysis = false; // Block loop (block_queue may grow, may be aborted via clearing) - for (usz j = 0; j < block_queue.size(); j++) + for (u32 j = 0; !postpone_analysis && j < block_queue.size(); [&]() { - auto& block = block_queue[j].get(); + if (u32 size = block_queue[j].size) + { + // Update size + func.blocks[block_queue[j].addr] = size; + } - vm::cptr _ptr = vm::cast(block.first); + j++; + }()) + { + block_local_info_t* block = &block_queue[j]; + + const u32 block_addr = block->addr; + + vm::cptr _ptr = vm::cast(block_addr); auto ptr = ensure(get_ptr(_ptr)); + auto is_reg_mapped = [&](u32 index) + { + return !!(block_queue[j].mapped_registers_mask & (u64{1} << index)); + }; + + reg_state_t dummy_state{}; + + const auto get_reg = [&](usz index) -> reg_state_t& + { + block_local_info_t* block = &block_queue[j]; + + const usz reg_mask = u64{1} << index; + + if (~block->moved_registers_mask & reg_mask) + { + if ((j + 1) * 64 >= reg_state_storage.size()) + { + dummy_state.value_range = 0; + dummy_state.tag = reg_tag_allocator++; + return dummy_state; + } + + usz begin_block = umax; + + // Try searching for register origin + if (block->mapped_registers_mask & reg_mask) + { + for (u32 i = block->parent_block_idx; i != umax; i = block_queue[i].parent_block_idx) + { + if (~block_queue[i].moved_registers_mask & reg_mask) + { + continue; + } + + begin_block = i; + break; + } + } + + // Initialize register or copy from origin + if (begin_block != umax) + { + reg_state_storage[64 * j + index] = reg_state_storage[64 * begin_block + index]; + } + else + { + reg_state_storage[64 * j + index] = make_unknown_reg_state(); + } + + block->mapped_registers_mask |= reg_mask; + block->moved_registers_mask |= reg_mask; + } + + return reg_state_storage[64 * j + index]; + }; + + const auto store_block_reg = [&](u32 block_index, u32 index, const reg_state_t& rhs) + { + if ((block_index + 1) * 64 >= reg_state_storage.size()) + { + return; + } + + reg_state_storage[64 * block_index + index] = rhs; + + const usz reg_mask = u64{1} << index; + block_queue[block_index].mapped_registers_mask |= reg_mask; + block_queue[block_index].moved_registers_mask |= reg_mask; + }; + + const auto unmap_reg = [&](u32 index) + { + block_local_info_t* block = &block_queue[j]; + + const usz reg_mask = u64{1} << index; + + block->mapped_registers_mask &= ~reg_mask; + block->moved_registers_mask &= ~reg_mask; + }; + + enum : u32 + { + c_reg_lr = 32, + c_reg_ctr = 33, + c_reg_vrsave = 34, + c_reg_xer = 35, + c_reg_cr0_arg_rhs = 36, + c_reg_cr7_arg_rhs = c_reg_cr0_arg_rhs + 7, + c_reg_cr0_arg_lhs = 44, + c_reg_cr7_arg_lhs = c_reg_cr0_arg_lhs + 7, + }; + for (; _ptr.addr() < func_end;) { const u32 iaddr = _ptr.addr(); const ppu_opcode_t op{*advance(_ptr, ptr, 1)}; const ppu_itype::type type = s_ppu_itype.decode(op.opcode); - if (type == ppu_itype::UNK) + switch (type) + { + case ppu_itype::UNK: { // Invalid blocks will remain empty break; } - else if (type == ppu_itype::B || type == ppu_itype::BC) + case ppu_itype::B: + case ppu_itype::BC: { const u32 target = (op.aa ? 0 : iaddr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); @@ -1383,7 +1929,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con if (!op.aa && target == _ptr.addr() && _ptr.addr() < func_end) { - ppu_log.notice("[0x%x] Branch to next at 0x%x -> 0x%x", func.addr, iaddr, target); + (!!op.lk ? ppu_log.notice : ppu_log.trace)("[0x%x] Branch to next at 0x%x -> 0x%x", func.addr, iaddr, target); } const bool is_call = op.lk && target != iaddr && target != _ptr.addr() && _ptr.addr() < func_end; @@ -1392,14 +1938,83 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con if (pfunc && pfunc->blocks.empty()) { // Postpone analysis (no info) - block_queue.clear(); + postpone_analysis = true; break; } // Add next block if necessary if ((is_call && !(pfunc->attr & ppu_attr::no_return)) || (type == ppu_itype::BC && (op.bo & 0x14) != 0x14)) { - add_block(_ptr.addr()); + const u32 next_idx = add_block(_ptr.addr(), j); + + if (next_idx != umax && target != iaddr + 4 && type == ppu_itype::BC && (op.bo & 0b10100) == 0b00100) + { + bool lt{}; + bool gt{}; + bool eq{}; + bool so{}; + bool nso{}; + + // Because this is the following instruction, reverse the flags + if ((op.bo & 0b01000) != 0) + { + switch (op.bi % 4) + { + case 0x0: gt = true; eq = true; break; + case 0x1: lt = true; eq = true; break; + case 0x2: gt = true; lt = true; break; + case 0x3: nso = true; break; + default: fmt::throw_exception("Unreachable"); + } + } + else + { + switch (op.bi % 4) + { + case 0x0: lt = true; break; + case 0x1: gt = true; break; + case 0x2: eq = true; break; + case 0x3: so = true; break; + default: fmt::throw_exception("Unreachable"); + } + } + + const u32 rhs_cr_state = c_reg_cr0_arg_rhs + op.bi / 4; + const u32 lhs_cr_state = c_reg_cr0_arg_lhs + op.bi / 4; + + // Complete the block information based on the data that the jump is NOT taken + // This information would be inherited to next block + + auto lhs_state = get_reg(lhs_cr_state); + auto rhs_state = get_reg(rhs_cr_state); + + lhs_state.declare_ordering(rhs_state, lt, eq, gt, so, nso); + + store_block_reg(next_idx, lhs_cr_state, lhs_state); + store_block_reg(next_idx, rhs_cr_state, rhs_state); + + const u64 reg_mask = block_queue[j].mapped_registers_mask; + + for (u32 bit = std::countr_zero(reg_mask); bit < 64 && reg_mask & (u64{1} << bit); + bit += 1, bit = std::countr_zero(reg_mask >> (bit % 64)) + bit) + { + if (bit == lhs_cr_state || bit == rhs_cr_state) + { + continue; + } + + auto& reg_state = get_reg(bit); + + if (reg_state.is_equals(lhs_state)) + { + store_block_reg(next_idx, bit, lhs_state); + } + else if (reg_state.is_equals(rhs_state)) + { + store_block_reg(next_idx, bit, rhs_state); + } + } + } } if (is_call && pfunc->attr & ppu_attr::no_return) @@ -1414,53 +2029,134 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con else { // Add block - add_block(target); + add_block(target, umax); } - block.second = _ptr.addr() - block.first; + block_queue[j].size = _ptr.addr() - block_addr; break; } - else if (type == ppu_itype::BCLR) + case ppu_itype::BCLR: { if (op.lk || (op.bo & 0x14) != 0x14) { - add_block(_ptr.addr()); + add_block(_ptr.addr(), j); } - block.second = _ptr.addr() - block.first; + block_queue[j].size = _ptr.addr() - block_addr; break; } - else if (type == ppu_itype::BCCTR) + case ppu_itype::BCCTR: { if (op.lk || (op.bo & 0x10) != 0x10) { - add_block(_ptr.addr()); + add_block(_ptr.addr(), j); + } + else if (get_reg(c_reg_ctr)(is_const)) + { + ppu_log.todo("[0x%x] BCTR to constant destination at 0x%x: 0x%x", func.addr, iaddr, get_reg(c_reg_ctr)(minv)); } else { // Analyse jumptable (TODO) - const u32 jt_addr = _ptr.addr(); - const u32 jt_end = func_end; + u32 jt_addr = _ptr.addr(); + u32 jt_end = func_end; + const auto code_end = get_ptr(func_end); - for (; _ptr.addr() < jt_end; advance(_ptr, ptr, 1)) + auto get_jumptable_end = [&](vm::cptr& _ptr, be_t*& ptr, bool is_relative) { - const u32 addr = jt_addr + *ptr; - - if (addr == jt_addr) + for (; _ptr.addr() < jt_end; advance(_ptr, ptr, 1)) { - // TODO (cannot branch to jumptable itself) - break; - } + const u32 addr = (is_relative ? jt_addr : 0) + *ptr; - if (addr % 4 || addr < func.addr || addr >= jt_end) + if (addr == jt_addr) + { + // TODO (cannot branch to jumptable itself) + return; + } + + if (addr % 4 || addr < func.addr || addr >= func_end || !is_valid_code({ get_ptr(addr), code_end }, !is_relocatable, addr)) + { + return; + } + + add_block(addr, j); + } + }; + + get_jumptable_end(_ptr, ptr, true); + + bool found_jt = jt_addr == jt_end || _ptr.addr() != jt_addr; + + if (!found_jt && is_reg_mapped(c_reg_ctr)) + { + // Fallback: try to extract jumptable address from registers + const reg_state_t ctr = get_reg(c_reg_ctr); + + if (ctr.is_loaded) { - break; - } + vm::cptr jumpatble_off = vm::cast(static_cast(ctr(load_addrv))); - add_block(addr); + if (be_t* jumpatble_ptr_begin = get_ptr(jumpatble_off)) + { + be_t* jumpatble_ptr = jumpatble_ptr_begin; + + jt_addr = jumpatble_off.addr(); + + jt_end = segs_end; + + for (const auto& seg : segs) + { + if (seg.size) + { + if (jt_addr < seg.addr + seg.size && jt_addr >= seg.addr) + { + jt_end = seg.addr + seg.size; + break; + } + } + } + + jt_end = utils::align(static_cast(std::min(jt_end - 1, ctr(maxv) - 1) + 1), 4); + + get_jumptable_end(jumpatble_off, jumpatble_ptr, false); + + // If we do not have jump-table bounds, try manually extract reference address (last resort: may be inaccurate in theory) + if (jumpatble_ptr == jumpatble_ptr_begin && addr_heap.contains(_ptr.addr())) + { + // See who is referencing it + const u32 ref_addr = addr_heap.at(_ptr.addr()); + + if (ref_addr > jt_addr && ref_addr < jt_end) + { + ppu_log.todo("[0x%x] Jump table not found through GPR search, retrying using addr_heap. (iaddr=0x%x, ref_addr=0x%x)", func.addr, iaddr, ref_addr); + + jumpatble_off = vm::cast(ref_addr); + jumpatble_ptr_begin = get_ptr(jumpatble_off); + jumpatble_ptr = jumpatble_ptr_begin; + jt_addr = ref_addr; + + get_jumptable_end(jumpatble_off, jumpatble_ptr, false); + } + } + + if (jumpatble_ptr != jumpatble_ptr_begin) + { + found_jt = true; + + for (be_t addr : std::span>{ jumpatble_ptr_begin , jumpatble_ptr }) + { + if (addr == _ptr.addr()) + { + ppu_log.success("[0x%x] Found address of next code in jump table at 0x%x! 0x%x-0x%x", func.addr, iaddr, jt_addr, jt_addr + 4 * (jumpatble_ptr - jumpatble_ptr_begin)); + break; + } + } + } + } + } } - if (jt_addr != jt_end && _ptr.addr() == jt_addr) + if (!found_jt) { // Acknowledge jumptable detection failure if (!(func.attr & ppu_attr::no_size)) @@ -1469,8 +2165,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con } func.attr += ppu_attr::no_size; - add_block(jt_addr); - block_queue.clear(); + add_block(jt_addr, j); + postpone_analysis = true; } else { @@ -1478,37 +2174,284 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con } } - block.second = _ptr.addr() - block.first; + block_queue[j].size = _ptr.addr() - block_addr; break; } - else if (type & ppu_itype::trap && op.bo) + case ppu_itype::TD: + case ppu_itype::TW: + case ppu_itype::TDI: + case ppu_itype::TWI: { - if (can_trap_continue(op, type)) + if (!op.bo) { - add_block(_ptr.addr()); + // No-op + continue; } - block.second = _ptr.addr() - block.first; + if (can_trap_continue(op, type)) + { + add_block(_ptr.addr(), j); + } + + block_queue[j].size = _ptr.addr() - block_addr; break; } - else if (type == ppu_itype::SC) + case ppu_itype::SC: { - add_block(_ptr.addr()); - block.second = _ptr.addr() - block.first; + add_block(_ptr.addr(), j); + block_queue[j].size = _ptr.addr() - block_addr; break; } - else if (type == ppu_itype::STDU && func.attr & ppu_attr::no_size && (op.opcode == *ptr || *ptr == ppu_instructions::BLR())) + case ppu_itype::STDU: { - // Hack - ppu_log.success("[0x%x] Instruction repetition: 0x%08x", iaddr, op.opcode); - add_block(_ptr.addr()); - block.second = _ptr.addr() - block.first; - break; + if (func.attr & ppu_attr::no_size && (op.opcode == *ptr || *ptr == ppu_instructions::BLR())) + { + // Hack + ppu_log.success("[0x%x] Instruction repetition: 0x%08x", iaddr, op.opcode); + add_block(_ptr.addr(), j); + block_queue[j].size = _ptr.addr() - block_addr; + } + + continue; } + case ppu_itype::ADDI: + case ppu_itype::ADDIC: + case ppu_itype::ADDIS: + { + const s64 to_add = type == ppu_itype::ADDIS ? op.simm16 * 65536 : op.simm16; + + const reg_state_t ra = type == ppu_itype::ADDIC || op.ra ? get_reg(op.ra) : s_reg_const_0; + + if (ra(is_const)) + { + reg_state_t rd{}; + rd.load_const(ra(minv) + to_add); + store_block_reg(j, op.rd, rd); + } + else + { + unmap_reg(op.rd); + } + + continue; + } + case ppu_itype::ORI: + case ppu_itype::ORIS: + { + const u64 to_or = type == ppu_itype::ORIS ? op.uimm16 * 65536 : op.uimm16; + + const reg_state_t rs = get_reg(op.rs); + + if (!to_or && is_reg_mapped(op.rs)) + { + store_block_reg(j, op.ra, get_reg(op.rs)); + } + else if (rs(is_const)) + { + reg_state_t ra{}; + ra.load_const(rs(minv) | to_or); + store_block_reg(j, op.ra, ra); + } + else + { + unmap_reg(op.ra); + } + + continue; + } + case ppu_itype::MTSPR: + { + switch (const u32 n = (op.spr >> 5) | ((op.spr & 0x1f) << 5)) + { + case 0x001: // MTXER + { + break; + } + case 0x008: // MTLR + { + //store_block_reg(j, c_reg_lr, get_reg(op.rs)); + break; + } + case 0x009: // MTCTR + { + store_block_reg(j, c_reg_ctr, get_reg(op.rs)); + break; + } + case 0x100: + { + // get_reg(c_reg_vrsave) = get_reg(op.rs); + // get_reg(c_reg_vrsave).value &= u32{umax}; + break; + } + default: + { + break; + } + } + + continue; + } + case ppu_itype::LWZX: + case ppu_itype::LDX: // TODO: Confirm if LDX can appear in jumptable branching (probably in LV1 applications such as ps2_emu) + { + if (is_reg_mapped(op.ra) || is_reg_mapped(op.rb)) + { + const reg_state_t ra = get_reg(op.ra); + const reg_state_t rb = get_reg(op.rb); + + const bool is_ra = ra(is_const) && (ra(minv) >= start && ra(minv) < segs_end); + const bool is_rb = rb(is_const) && (rb(minv) >= start && rb(minv) < segs_end); + + if (ra(is_const) == rb(is_const)) + { + unmap_reg(op.rd); + } + else + { + // Register possible jumptable offset + auto& rd = get_reg(op.rd); + rd = {}; + rd.is_loaded = true; + + const reg_state_t& const_reg = is_ra ? ra : rb; + const reg_state_t& off_reg = is_ra ? rb : ra; + + rd.ge_than = const_reg(minv); + + if (off_reg.value_range != 0 && off_reg(minv) < segs_end && const_reg(minv) < segs_end - off_reg(minv)) + { + rd.ge_than += off_reg(minv); + + if (off_reg(maxv) - 1 < segs_end - 1 && const_reg(minv) <= segs_end - off_reg(maxv)) + { + rd.value_range = off_reg.value_range; + } + } + } + } + + continue; + } + case ppu_itype::RLWINM: + { + //const u64 mask = ppu_rotate_mask(32 + op.mb32, 32 + op.me32); + + if (!is_reg_mapped(op.rs) && !is_reg_mapped(op.ra)) + { + continue; + } + + if (op.mb32 <= op.me32 && op.sh32 == 31 - op.me32) + { + // SLWI mnemonic (mb32 == 0) or generic form + + reg_state_t rs = get_reg(op.rs); + + if (!rs.shift_left(op.sh32, reg_tag_allocator) || !rs.clear_higher_bits(32 + op.mb32, reg_tag_allocator)) + { + unmap_reg(op.ra); + } + else + { + store_block_reg(j, op.ra, rs); + } + } + else + { + unmap_reg(op.ra); + } + + continue; + } + case ppu_itype::RLDICR: + { + const u32 sh = op.sh64; + const u32 me = op.mbe64; + //const u64 mask = ~0ull << (63 - me); + + if (sh == 63 - me) + { + // SLDI mnemonic + reg_state_t rs = get_reg(op.rs); + + if (!rs.shift_left(op.sh32, reg_tag_allocator)) + { + unmap_reg(op.ra); + } + else + { + store_block_reg(j, op.ra, rs); + } + } + else + { + unmap_reg(op.ra); + } + + continue; + } + case ppu_itype::CMPLI: + case ppu_itype::CMPI: + case ppu_itype::CMP: + case ppu_itype::CMPL: + { + const bool is_wbits = op.l10 == 0; + const bool is_signed = type == ppu_itype::CMPI || type == ppu_itype::CMP; + const bool is_rhs_register = type == ppu_itype::CMP || type == ppu_itype::CMPL; + + reg_state_t rhs{}; + reg_state_t lhs{}; + + lhs = get_reg(op.ra); + + if (is_rhs_register) + { + rhs = get_reg(op.rb); + } + else + { + const u64 compared = is_signed ? op.simm16 : op.uimm16; + rhs.load_const(is_wbits ? (compared & u32{umax}) : compared); + } + + if (is_wbits) + { + lhs.undef_higher_bits(32); + rhs.undef_higher_bits(32); + } + + if (is_signed) + { + // Force the comparison to become unsigned + // const u64 sign_bit = u64{1} << (is_wbits ? 31 : 63); + // lhs.add(sign_bit); + // rhs.add(sign_bit); + } + + const u32 cr_idx = op.crfd; + store_block_reg(j, c_reg_cr0_arg_rhs + cr_idx, rhs); + store_block_reg(j, c_reg_cr0_arg_lhs + cr_idx, lhs); + continue; + } + default: + { + if (type & ppu_itype::store) + { + continue; + } + + // TODO: Tell if instruction modified RD or RA + unmap_reg(op.rd); + unmap_reg(op.ra); + continue; + } + } + + break; } } - if (block_queue.empty() && !was_empty) + if (postpone_analysis) { // Block aborted: abort function, postpone func_queue.emplace_back(func); @@ -1564,11 +2507,10 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con { const u32 target = (op.aa ? 0 : iaddr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); - if (target >= start && target < end && (!op.aa || verify_func(iaddr))) + if (target >= start && target < end && (!op.aa || verify_ref(iaddr))) { if (target < func.addr || target >= func.addr + func.size) { - func.calls.emplace(target); add_func(target, func.toc ? func.toc + func.trampoline : 0, func.addr); } } @@ -1711,9 +2653,24 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con u32 per_instruction_bytes = 0; - for (auto&& [_, func] : as_rvalue(fmap)) + // Iterate by address (fmap may grow) + for (u32 addr_next = start; addr_next != umax;) { - if (func.attr & ppu_attr::no_size && entry) + // Get next iterator + const auto it = fmap.lower_bound(addr_next); + + if (it == fmap.end()) + { + break; + } + + // Save next function address as is as of this moment (ignoring added functions) + const auto it_next = std::next(it); + addr_next = it_next == fmap.end() ? u32{umax} : it_next->first; + + const ppu_function_ext& func = it->second; + + if (func.attr & ppu_attr::no_size && !is_relocatable) { // Disabled for PRX for now const u32 lim = get_limit(func.addr); @@ -1730,6 +2687,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con } per_instruction_bytes += utils::sub_saturate(lim, func.addr); + addr_next = std::max(addr_next, lim); continue; } @@ -1751,7 +2709,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con block.addr = addr; block.size = size; block.toc = func.toc; - ppu_log.trace("Block __0x%x added (func=0x%x, size=0x%x, toc=0x%x)", block.addr, _, block.size, block.toc); + ppu_log.trace("Block __0x%x added (func=0x%x, size=0x%x, toc=0x%x)", block.addr, it->first, block.size, block.toc); if (!entry && !sec_end) { @@ -2032,6 +2990,32 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con break; } + if (is_good) + { + // Special opting-out: range of "instructions" is likely to be embedded floats + // Test this by testing if the lower 23 bits are 0 + vm::cptr _ptr = vm::cast(exp); + auto ptr = get_ptr(_ptr); + + bool ok = false; + + for (u32 it = exp; it < std::min(i_pos + 4, lim); it += 4, advance(_ptr, ptr, 1)) + { + const u32 value_of_supposed_opcode = *ptr; + + if (value_of_supposed_opcode == ppu_instructions::NOP() || (value_of_supposed_opcode << (32 - 23))) + { + ok = true; + break; + } + } + + if (!ok) + { + is_good = false; + } + } + if (i_pos < lim) { i_pos += 4; @@ -2086,8 +3070,10 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con } // Convert map to vector (destructive) - for (auto&& [_, block] : as_rvalue(std::move(fmap))) + for (auto it = fmap.begin(); it != fmap.end(); it = fmap.begin()) { + ppu_function_ext block = std::move(fmap.extract(it).mapped()); + if (block.attr & ppu_attr::no_size && block.size > 4 && !used_fallback) { ppu_log.warning("Block 0x%x will be compiled on per-instruction basis (size=0x%x)", block.addr, block.size); @@ -2098,7 +3084,6 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con i.addr = addr; i.size = 4; i.toc = block.toc; - i.attr = ppu_attr::no_size; } per_instruction_bytes += block.size; @@ -2117,1962 +3102,3 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con ppu_log.notice("Block analysis: %zu blocks (%zu enqueued)", funcs.size(), block_queue.size()); return true; } - -// Temporarily -#ifndef _MSC_VER -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif - -void ppu_acontext::UNK(ppu_opcode_t op) -{ - std::fill_n(gpr, 32, spec_gpr{}); - ppu_log.error("Unknown/Illegal opcode: 0x%08x at 0x%x", op.opcode, cia); -} - -void ppu_acontext::MFVSCR(ppu_opcode_t op) -{ -} - -void ppu_acontext::MTVSCR(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDCUW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDSBS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDSHS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDSWS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDUBM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDUBS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDUHM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDUHS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDUWM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VADDUWS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VAND(ppu_opcode_t op) -{ -} - -void ppu_acontext::VANDC(ppu_opcode_t op) -{ -} - -void ppu_acontext::VAVGSB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VAVGSH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VAVGSW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VAVGUB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VAVGUH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VAVGUW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCFSX(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCFUX(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPBFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPEQFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPEQUB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPEQUH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPEQUW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPGEFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPGTFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPGTSB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPGTSH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPGTSW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPGTUB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPGTUH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCMPGTUW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCTSXS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VCTUXS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VEXPTEFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VLOGEFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMADDFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMAXFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMAXSB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMAXSH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMAXSW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMAXUB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMAXUH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMAXUW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMHADDSHS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMHRADDSHS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMINFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMINSB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMINSH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMINSW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMINUB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMINUH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMINUW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMLADDUHM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMRGHB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMRGHH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMRGHW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMRGLB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMRGLH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMRGLW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMSUMMBM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMSUMSHM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMSUMSHS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMSUMUBM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMSUMUHM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMSUMUHS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMULESB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMULESH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMULEUB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMULEUH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMULOSB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMULOSH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMULOUB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VMULOUH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VNMSUBFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VNOR(ppu_opcode_t op) -{ -} - -void ppu_acontext::VOR(ppu_opcode_t op) -{ -} - -void ppu_acontext::VPERM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VPKPX(ppu_opcode_t op) -{ -} - -void ppu_acontext::VPKSHSS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VPKSHUS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VPKSWSS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VPKSWUS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VPKUHUM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VPKUHUS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VPKUWUM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VPKUWUS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VREFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VRFIM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VRFIN(ppu_opcode_t op) -{ -} - -void ppu_acontext::VRFIP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VRFIZ(ppu_opcode_t op) -{ -} - -void ppu_acontext::VRLB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VRLH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VRLW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VRSQRTEFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSEL(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSL(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSLB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSLDOI(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSLH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSLO(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSLW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSPLTB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSPLTH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSPLTISB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSPLTISH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSPLTISW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSPLTW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSR(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSRAB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSRAH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSRAW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSRB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSRH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSRO(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSRW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBCUW(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBFP(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBSBS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBSHS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBSWS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBUBM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBUBS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBUHM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBUHS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBUWM(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUBUWS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUMSWS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUM2SWS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUM4SBS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUM4SHS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VSUM4UBS(ppu_opcode_t op) -{ -} - -void ppu_acontext::VUPKHPX(ppu_opcode_t op) -{ -} - -void ppu_acontext::VUPKHSB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VUPKHSH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VUPKLPX(ppu_opcode_t op) -{ -} - -void ppu_acontext::VUPKLSB(ppu_opcode_t op) -{ -} - -void ppu_acontext::VUPKLSH(ppu_opcode_t op) -{ -} - -void ppu_acontext::VXOR(ppu_opcode_t op) -{ -} - -void ppu_acontext::TDI(ppu_opcode_t op) -{ -} - -void ppu_acontext::TWI(ppu_opcode_t op) -{ -} - -void ppu_acontext::MULLI(ppu_opcode_t op) -{ - const s64 amin = gpr[op.ra].imin; - const s64 amax = gpr[op.ra].imax; - - // Undef or mixed range (default) - s64 min = 0; - s64 max = -1; - - // Special cases like powers of 2 and their negations are not handled - if (amin <= amax) - { - min = amin * op.simm16; - max = amax * op.simm16; - - // Check overflow - if (min >> 63 != utils::mulh64(amin, op.simm16) || max >> 63 != utils::mulh64(amax, op.simm16)) - { - min = 0; - max = -1; - } - else if (min > max) - { - std::swap(min, max); - } - } - - gpr[op.rd] = spec_gpr::range(min, max, gpr[op.ra].tz() + std::countr_zero(op.simm16)); -} - -void ppu_acontext::SUBFIC(ppu_opcode_t op) -{ - gpr[op.rd] = ~gpr[op.ra] + spec_gpr::fixed(op.simm16) + spec_gpr::fixed(1); -} - -void ppu_acontext::CMPLI(ppu_opcode_t op) -{ -} - -void ppu_acontext::CMPI(ppu_opcode_t op) -{ -} - -void ppu_acontext::ADDIC(ppu_opcode_t op) -{ - gpr[op.rd] = gpr[op.ra] + spec_gpr::fixed(op.simm16); -} - -void ppu_acontext::ADDI(ppu_opcode_t op) -{ - gpr[op.rd] = op.ra ? gpr[op.ra] + spec_gpr::fixed(op.simm16) : spec_gpr::fixed(op.simm16); -} - -void ppu_acontext::ADDIS(ppu_opcode_t op) -{ - gpr[op.rd] = op.ra ? gpr[op.ra] + spec_gpr::fixed(op.simm16 * 65536) : spec_gpr::fixed(op.simm16 * 65536); -} - -void ppu_acontext::BC(ppu_opcode_t op) -{ -} - -void ppu_acontext::SC(ppu_opcode_t op) -{ -} - -void ppu_acontext::B(ppu_opcode_t op) -{ -} - -void ppu_acontext::MCRF(ppu_opcode_t op) -{ -} - -void ppu_acontext::BCLR(ppu_opcode_t op) -{ -} - -void ppu_acontext::CRNOR(ppu_opcode_t op) -{ -} - -void ppu_acontext::CRANDC(ppu_opcode_t op) -{ -} - -void ppu_acontext::ISYNC(ppu_opcode_t op) -{ -} - -void ppu_acontext::CRXOR(ppu_opcode_t op) -{ -} - -void ppu_acontext::CRNAND(ppu_opcode_t op) -{ -} - -void ppu_acontext::CRAND(ppu_opcode_t op) -{ -} - -void ppu_acontext::CREQV(ppu_opcode_t op) -{ -} - -void ppu_acontext::CRORC(ppu_opcode_t op) -{ -} - -void ppu_acontext::CROR(ppu_opcode_t op) -{ -} - -void ppu_acontext::BCCTR(ppu_opcode_t op) -{ -} - -void ppu_acontext::RLWIMI(ppu_opcode_t op) -{ - const u64 mask = ppu_rotate_mask(32 + op.mb32, 32 + op.me32); - - u64 min = gpr[op.rs].bmin; - u64 max = gpr[op.rs].bmax; - - if (op.mb32 <= op.me32) - { - // 32-bit op, including mnemonics: INSLWI, INSRWI (TODO) - min = utils::rol32(static_cast(min), op.sh32) & mask; - max = utils::rol32(static_cast(max), op.sh32) & mask; - } - else - { - // Full 64-bit op with duplication - min = utils::rol64(static_cast(min) | min << 32, op.sh32) & mask; - max = utils::rol64(static_cast(max) | max << 32, op.sh32) & mask; - } - - if (mask != umax) - { - // Insertion - min |= gpr[op.ra].bmin & ~mask; - max |= gpr[op.ra].bmax & ~mask; - } - - gpr[op.rs] = spec_gpr::approx(min, max); -} - -void ppu_acontext::RLWINM(ppu_opcode_t op) -{ - const u64 mask = ppu_rotate_mask(32 + op.mb32, 32 + op.me32); - - u64 min = gpr[op.rs].bmin; - u64 max = gpr[op.rs].bmax; - - if (op.mb32 <= op.me32) - { - if (op.sh32 == 0) - { - // CLRLWI, CLRRWI mnemonics - gpr[op.ra] = gpr[op.ra] & spec_gpr::fixed(mask); - return; - } - else if (op.mb32 == 0 && op.me32 == 31) - { - // ROTLWI, ROTRWI mnemonics - } - else if (op.mb32 == 0 && op.sh32 == 31 - op.me32) - { - // SLWI mnemonic - } - else if (op.me32 == 31 && op.sh32 == 32 - op.mb32) - { - // SRWI mnemonic - } - else if (op.mb32 == 0 && op.sh32 < 31 - op.me32) - { - // EXTLWI and other possible mnemonics - } - else if (op.me32 == 31 && 32 - op.sh32 < op.mb32) - { - // EXTRWI and other possible mnemonics - } - - min = utils::rol32(static_cast(min), op.sh32) & mask; - max = utils::rol32(static_cast(max), op.sh32) & mask; - } - else - { - // Full 64-bit op with duplication - min = utils::rol64(static_cast(min) | min << 32, op.sh32) & mask; - max = utils::rol64(static_cast(max) | max << 32, op.sh32) & mask; - } - - gpr[op.ra] = spec_gpr::approx(min, max); -} - -void ppu_acontext::RLWNM(ppu_opcode_t op) -{ - const u64 mask = ppu_rotate_mask(32 + op.mb32, 32 + op.me32); - - u64 min = gpr[op.rs].bmin; - u64 max = gpr[op.rs].bmax; - - if (op.mb32 <= op.me32) - { - if (op.mb32 == 0 && op.me32 == 31) - { - // ROTLW mnemonic - } - - // TODO - min = 0; - max = mask; - } - else - { - // Full 64-bit op with duplication - min = 0; - max = mask; - } - - gpr[op.ra] = spec_gpr::approx(min, max); -} - -void ppu_acontext::ORI(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] | spec_gpr::fixed(op.uimm16); -} - -void ppu_acontext::ORIS(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] | spec_gpr::fixed(op.uimm16 << 16); -} - -void ppu_acontext::XORI(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] ^ spec_gpr::fixed(op.uimm16); -} - -void ppu_acontext::XORIS(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] ^ spec_gpr::fixed(op.uimm16 << 16); -} - -void ppu_acontext::ANDI(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] & spec_gpr::fixed(op.uimm16); -} - -void ppu_acontext::ANDIS(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] & spec_gpr::fixed(op.uimm16 << 16); -} - -void ppu_acontext::RLDICL(ppu_opcode_t op) -{ - const u32 sh = op.sh64; - const u32 mb = op.mbe64; - const u64 mask = ~0ull >> mb; - - u64 min = gpr[op.rs].bmin; - u64 max = gpr[op.rs].bmax; - - if (64 - sh < mb) - { - // EXTRDI mnemonic - } - else if (64 - sh == mb) - { - // SRDI mnemonic - } - else if (sh == 0) - { - // CLRLDI mnemonic - gpr[op.ra] = gpr[op.rs] & spec_gpr::fixed(mask); - return; - } - - min = utils::rol64(min, sh) & mask; - max = utils::rol64(max, sh) & mask; - gpr[op.ra] = spec_gpr::approx(min, max); -} - -void ppu_acontext::RLDICR(ppu_opcode_t op) -{ - const u32 sh = op.sh64; - const u32 me = op.mbe64; - const u64 mask = ~0ull << (63 - me); - - u64 min = gpr[op.rs].bmin; - u64 max = gpr[op.rs].bmax; - - if (sh < 63 - me) - { - // EXTLDI mnemonic - } - else if (sh == 63 - me) - { - // SLDI mnemonic - } - else if (sh == 0) - { - // CLRRDI mnemonic - gpr[op.ra] = gpr[op.rs] & spec_gpr::fixed(mask); - return; - } - - min = utils::rol64(min, sh) & mask; - max = utils::rol64(max, sh) & mask; - gpr[op.ra] = spec_gpr::approx(min, max); -} - -void ppu_acontext::RLDIC(ppu_opcode_t op) -{ - const u32 sh = op.sh64; - const u32 mb = op.mbe64; - const u64 mask = ppu_rotate_mask(mb, 63 - sh); - - u64 min = gpr[op.rs].bmin; - u64 max = gpr[op.rs].bmax; - - if (mb == 0 && sh == 0) - { - gpr[op.ra] = gpr[op.rs]; - return; - } - else if (mb <= 63 - sh) - { - // CLRLSLDI - //gpr[op.ra] = (gpr[op.rs] & spec_gpr::fixed(ppu_rotate_mask(0, sh + mb))) << spec_gpr::fixed(sh); - return; - } - - min = utils::rol64(min, sh) & mask; - max = utils::rol64(max, sh) & mask; - gpr[op.ra] = spec_gpr::approx(min, max); -} - -void ppu_acontext::RLDIMI(ppu_opcode_t op) -{ - const u32 sh = op.sh64; - const u32 mb = op.mbe64; - const u64 mask = ppu_rotate_mask(mb, 63 - sh); - - u64 min = gpr[op.rs].bmin; - u64 max = gpr[op.rs].bmax; - - if (mb == 0 && sh == 0) - { - // Copy - } - else if (mb <= 63 - sh) - { - // INSRDI mnemonic - } - - min = utils::rol64(min, sh) & mask; - max = utils::rol64(max, sh) & mask; - - if (mask != umax) - { - // Insertion - min |= gpr[op.ra].bmin & ~mask; - max |= gpr[op.ra].bmax & ~mask; - } - - gpr[op.ra] = spec_gpr::approx(min, max); -} - -void ppu_acontext::RLDCL(ppu_opcode_t op) -{ - const u32 mb = op.mbe64; - const u64 mask = ~0ull >> mb; - - u64 min = gpr[op.rs].bmin; - u64 max = gpr[op.rs].bmax; - - // TODO - min = 0; - max = mask; - gpr[op.ra] = spec_gpr::approx(min, max); -} - -void ppu_acontext::RLDCR(ppu_opcode_t op) -{ - const u32 me = op.mbe64; - const u64 mask = ~0ull << (63 - me); - - u64 min = gpr[op.rs].bmin; - u64 max = gpr[op.rs].bmax; - - // TODO - min = 0; - max = mask; - gpr[op.ra] = spec_gpr::approx(min, max); -} - -void ppu_acontext::CMP(ppu_opcode_t op) -{ -} - -void ppu_acontext::TW(ppu_opcode_t op) -{ -} - -void ppu_acontext::LVSL(ppu_opcode_t op) -{ -} - -void ppu_acontext::LVEBX(ppu_opcode_t op) -{ -} - -void ppu_acontext::SUBFC(ppu_opcode_t op) -{ - gpr[op.rd] = ~gpr[op.ra] + gpr[op.rb] + spec_gpr::fixed(1); -} - -void ppu_acontext::ADDC(ppu_opcode_t op) -{ - gpr[op.rd] = gpr[op.ra] + gpr[op.rb]; -} - -void ppu_acontext::MULHDU(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::MULHWU(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::MFOCRF(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LWARX(ppu_opcode_t op) -{ - gpr[op.rd] = spec_gpr::range(0, u32{umax}); -} - -void ppu_acontext::LDX(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LWZX(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::SLW(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::CNTLZW(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::SLD(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::AND(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] & gpr[op.rb]; -} - -void ppu_acontext::CMPL(ppu_opcode_t op) -{ -} - -void ppu_acontext::LVSR(ppu_opcode_t op) -{ -} - -void ppu_acontext::LVEHX(ppu_opcode_t op) -{ -} - -void ppu_acontext::SUBF(ppu_opcode_t op) -{ - gpr[op.rd] = ~gpr[op.ra] + gpr[op.rb] + spec_gpr::fixed(1); -} - -void ppu_acontext::LDUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::DCBST(ppu_opcode_t op) -{ -} - -void ppu_acontext::LWZUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::CNTLZD(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::ANDC(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] & ~gpr[op.rb]; -} - -void ppu_acontext::TD(ppu_opcode_t op) -{ -} - -void ppu_acontext::LVEWX(ppu_opcode_t op) -{ -} - -void ppu_acontext::MULHD(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::MULHW(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LDARX(ppu_opcode_t op) -{ - gpr[op.rd] = {}; -} - -void ppu_acontext::DCBF(ppu_opcode_t op) -{ -} - -void ppu_acontext::LBZX(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LVX(ppu_opcode_t op) -{ -} - -void ppu_acontext::NEG(ppu_opcode_t op) -{ - gpr[op.rd] = ~gpr[op.ra] + spec_gpr::fixed(1); -} - -void ppu_acontext::LBZUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::NOR(ppu_opcode_t op) -{ - gpr[op.ra] = ~(gpr[op.rs] | gpr[op.rb]); -} - -void ppu_acontext::STVEBX(ppu_opcode_t op) -{ -} - -void ppu_acontext::SUBFE(ppu_opcode_t op) -{ - gpr[op.rd] = ~gpr[op.ra] + gpr[op.rb] + spec_gpr::range(0, 1); -} - -void ppu_acontext::ADDE(ppu_opcode_t op) -{ - gpr[op.rd] = gpr[op.ra] + gpr[op.rb] + spec_gpr::range(0, 1); -} - -void ppu_acontext::MTOCRF(ppu_opcode_t op) -{ -} - -void ppu_acontext::STDX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STWCX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STWX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STVEHX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STDUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::STWUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::STVEWX(ppu_opcode_t op) -{ -} - -void ppu_acontext::SUBFZE(ppu_opcode_t op) -{ - gpr[op.rd] = ~gpr[op.ra] + spec_gpr::range(0, 1); -} - -void ppu_acontext::ADDZE(ppu_opcode_t op) -{ - gpr[op.rd] = gpr[op.ra] + spec_gpr::range(0, 1); -} - -void ppu_acontext::STDCX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STBX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STVX(ppu_opcode_t op) -{ -} - -void ppu_acontext::SUBFME(ppu_opcode_t op) -{ - gpr[op.rd] = ~gpr[op.ra] + spec_gpr::fixed(-1) + spec_gpr::range(0, 1); -} - -void ppu_acontext::MULLD(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::ADDME(ppu_opcode_t op) -{ - gpr[op.rd] = gpr[op.ra] + spec_gpr::fixed(-1) + spec_gpr::range(0, 1); -} - -void ppu_acontext::MULLW(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::DCBTST(ppu_opcode_t op) -{ -} - -void ppu_acontext::STBUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::ADD(ppu_opcode_t op) -{ - gpr[op.rd] = gpr[op.ra] + gpr[op.rd]; -} - -void ppu_acontext::DCBT(ppu_opcode_t op) -{ -} - -void ppu_acontext::LHZX(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::EQV(ppu_opcode_t op) -{ - gpr[op.ra] = ~(gpr[op.rs] ^ gpr[op.rb]); -} - -void ppu_acontext::ECIWX(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LHZUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::XOR(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] ^ gpr[op.rb]; -} - -void ppu_acontext::MFSPR(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LWAX(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::DST(ppu_opcode_t op) -{ -} - -void ppu_acontext::LHAX(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LVXL(ppu_opcode_t op) -{ -} - -void ppu_acontext::MFTB(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LWAUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::DSTST(ppu_opcode_t op) -{ -} - -void ppu_acontext::LHAUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::STHX(ppu_opcode_t op) -{ -} - -void ppu_acontext::ORC(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] | ~gpr[op.rb]; -} - -void ppu_acontext::ECOWX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STHUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::OR(ppu_opcode_t op) -{ - gpr[op.ra] = gpr[op.rs] | gpr[op.rb]; -} - -void ppu_acontext::DIVDU(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::DIVWU(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::MTSPR(ppu_opcode_t op) -{ -} - -void ppu_acontext::DCBI(ppu_opcode_t op) -{ -} - -void ppu_acontext::NAND(ppu_opcode_t op) -{ - gpr[op.ra] = ~(gpr[op.rs] & gpr[op.rb]); -} - -void ppu_acontext::STVXL(ppu_opcode_t op) -{ -} - -void ppu_acontext::DIVD(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::DIVW(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LVLX(ppu_opcode_t op) -{ -} - -void ppu_acontext::LDBRX(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LSWX(ppu_opcode_t op) -{ -} - -void ppu_acontext::LWBRX(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LFSX(ppu_opcode_t op) -{ -} - -void ppu_acontext::SRW(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::SRD(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::LVRX(ppu_opcode_t op) -{ -} - -void ppu_acontext::LSWI(ppu_opcode_t op) -{ - std::fill_n(gpr, 32, spec_gpr{}); -} - -void ppu_acontext::LFSUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::SYNC(ppu_opcode_t op) -{ -} - -void ppu_acontext::LFDX(ppu_opcode_t op) -{ -} - -void ppu_acontext::LFDUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::STVLX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STDBRX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STSWX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STWBRX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STFSX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STVRX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STFSUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::STSWI(ppu_opcode_t op) -{ -} - -void ppu_acontext::STFDX(ppu_opcode_t op) -{ -} - -void ppu_acontext::STFDUX(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::LVLXL(ppu_opcode_t op) -{ -} - -void ppu_acontext::LHBRX(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::SRAW(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::SRAD(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::LVRXL(ppu_opcode_t op) -{ -} - -void ppu_acontext::DSS(ppu_opcode_t op) -{ -} - -void ppu_acontext::SRAWI(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::SRADI(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::EIEIO(ppu_opcode_t op) -{ -} - -void ppu_acontext::STVLXL(ppu_opcode_t op) -{ -} - -void ppu_acontext::STHBRX(ppu_opcode_t op) -{ -} - -void ppu_acontext::EXTSH(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::STVRXL(ppu_opcode_t op) -{ -} - -void ppu_acontext::EXTSB(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::STFIWX(ppu_opcode_t op) -{ -} - -void ppu_acontext::EXTSW(ppu_opcode_t op) -{ - gpr[op.ra].set_undef(); -} - -void ppu_acontext::ICBI(ppu_opcode_t op) -{ -} - -void ppu_acontext::DCBZ(ppu_opcode_t op) -{ -} - -void ppu_acontext::LWZ(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LWZU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::LBZ(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LBZU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::STW(ppu_opcode_t op) -{ -} - -void ppu_acontext::STWU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::STB(ppu_opcode_t op) -{ -} - -void ppu_acontext::STBU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::LHZ(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LHZU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::LHA(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LHAU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::STH(ppu_opcode_t op) -{ -} - -void ppu_acontext::STHU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::LMW(ppu_opcode_t op) -{ - std::fill_n(gpr, 32, spec_gpr{}); -} - -void ppu_acontext::STMW(ppu_opcode_t op) -{ -} - -void ppu_acontext::LFS(ppu_opcode_t op) -{ -} - -void ppu_acontext::LFSU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::LFD(ppu_opcode_t op) -{ -} - -void ppu_acontext::LFDU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::STFS(ppu_opcode_t op) -{ -} - -void ppu_acontext::STFSU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::STFD(ppu_opcode_t op) -{ -} - -void ppu_acontext::STFDU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::LD(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::LDU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.rd].set_undef(); - gpr[op.ra] = addr; -} - -void ppu_acontext::LWA(ppu_opcode_t op) -{ - gpr[op.rd].set_undef(); -} - -void ppu_acontext::STD(ppu_opcode_t op) -{ -} - -void ppu_acontext::STDU(ppu_opcode_t op) -{ - const auto addr = gpr[op.ra] + gpr[op.rb]; - gpr[op.ra] = addr; -} - -void ppu_acontext::FDIVS(ppu_opcode_t op) -{ -} - -void ppu_acontext::FSUBS(ppu_opcode_t op) -{ -} - -void ppu_acontext::FADDS(ppu_opcode_t op) -{ -} - -void ppu_acontext::FSQRTS(ppu_opcode_t op) -{ -} - -void ppu_acontext::FRES(ppu_opcode_t op) -{ -} - -void ppu_acontext::FMULS(ppu_opcode_t op) -{ -} - -void ppu_acontext::FMADDS(ppu_opcode_t op) -{ -} - -void ppu_acontext::FMSUBS(ppu_opcode_t op) -{ -} - -void ppu_acontext::FNMSUBS(ppu_opcode_t op) -{ -} - -void ppu_acontext::FNMADDS(ppu_opcode_t op) -{ -} - -void ppu_acontext::MTFSB1(ppu_opcode_t op) -{ -} - -void ppu_acontext::MCRFS(ppu_opcode_t op) -{ -} - -void ppu_acontext::MTFSB0(ppu_opcode_t op) -{ -} - -void ppu_acontext::MTFSFI(ppu_opcode_t op) -{ -} - -void ppu_acontext::MFFS(ppu_opcode_t op) -{ -} - -void ppu_acontext::MTFSF(ppu_opcode_t op) -{ -} - -void ppu_acontext::FCMPU(ppu_opcode_t op) -{ -} - -void ppu_acontext::FRSP(ppu_opcode_t op) -{ -} - -void ppu_acontext::FCTIW(ppu_opcode_t op) -{ -} - -void ppu_acontext::FCTIWZ(ppu_opcode_t op) -{ -} - -void ppu_acontext::FDIV(ppu_opcode_t op) -{ -} - -void ppu_acontext::FSUB(ppu_opcode_t op) -{ -} - -void ppu_acontext::FADD(ppu_opcode_t op) -{ -} - -void ppu_acontext::FSQRT(ppu_opcode_t op) -{ -} - -void ppu_acontext::FSEL(ppu_opcode_t op) -{ -} - -void ppu_acontext::FMUL(ppu_opcode_t op) -{ -} - -void ppu_acontext::FRSQRTE(ppu_opcode_t op) -{ -} - -void ppu_acontext::FMSUB(ppu_opcode_t op) -{ -} - -void ppu_acontext::FMADD(ppu_opcode_t op) -{ -} - -void ppu_acontext::FNMSUB(ppu_opcode_t op) -{ -} - -void ppu_acontext::FNMADD(ppu_opcode_t op) -{ -} - -void ppu_acontext::FCMPO(ppu_opcode_t op) -{ -} - -void ppu_acontext::FNEG(ppu_opcode_t op) -{ -} - -void ppu_acontext::FMR(ppu_opcode_t op) -{ -} - -void ppu_acontext::FNABS(ppu_opcode_t op) -{ -} - -void ppu_acontext::FABS(ppu_opcode_t op) -{ -} - -void ppu_acontext::FCTID(ppu_opcode_t op) -{ -} - -void ppu_acontext::FCTIDZ(ppu_opcode_t op) -{ -} - -void ppu_acontext::FCFID(ppu_opcode_t op) -{ -} - -#include - -const bool s_tes = []() -{ - return true; - - std::mt19937_64 rnd{123}; - - for (u32 i = 0; i < 10000; i++) - { - ppu_acontext::spec_gpr r1, r2, r3; - r1 = ppu_acontext::spec_gpr::approx(rnd(), rnd()); - r2 = ppu_acontext::spec_gpr::range(rnd(), rnd()); - r3 = r1 | r2; - - for (u32 j = 0; j < 10000; j++) - { - u64 v1 = rnd(), v2 = rnd(); - v1 &= r1.mask(); - v1 |= r1.ones(); - if (!r2.test(v2)) - { - v2 = r2.imin; - } - - if (r1.test(v1) && r2.test(v2)) - { - if (!r3.test(v1 | v2)) - { - auto exp = ppu_acontext::spec_gpr::approx(r1.ones() & r2.ones(), r1.mask() & r2.mask()); - - ppu_log.error("ppu_acontext failure:" - "\n\tr1 = 0x%016x..0x%016x, 0x%016x:0x%016x" - "\n\tr2 = 0x%016x..0x%016x, 0x%016x:0x%016x" - "\n\tr3 = 0x%016x..0x%016x, 0x%016x:0x%016x" - "\n\tex = 0x%016x..0x%016x" - "\n\tv1 = 0x%016x, v2 = 0x%016x, v3 = 0x%016x", - r1.imin, r1.imax, r1.bmin, r1.bmax, r2.imin, r2.imax, r2.bmin, r2.bmax, r3.imin, r3.imax, r3.bmin, r3.bmax, exp.imin, exp.imax, v1, v2, v1 | v2); - break; - } - } - } - } - - ppu_acontext::spec_gpr r1; - r1 = ppu_acontext::spec_gpr::range(0x13311, 0x1fe22); - r1 = r1 ^ ppu_acontext::spec_gpr::approx(0x000, 0xf00); - ppu_log.success("0x%x..0x%x", r1.imin, r1.imax); - - return true; -}(); diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index 0b225bc821..5d9053847d 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -2,10 +2,9 @@ #include #include -#include #include +#include #include "util/types.hpp" -#include "util/endian.hpp" #include "util/asm.hpp" #include "util/to_endian.hpp" @@ -13,9 +12,8 @@ #include "PPUOpcodes.h" // PPU Function Attributes -enum class ppu_attr : u32 +enum class ppu_attr : u8 { - known_addr, known_size, no_return, no_size, @@ -30,15 +28,52 @@ struct ppu_function u32 addr = 0; u32 toc = 0; u32 size = 0; - bs_t attr{}; - - u32 stack_frame = 0; - u32 trampoline = 0; std::map blocks{}; // Basic blocks: addr -> size - std::set calls{}; // Set of called functions - std::set callers{}; - std::string name{}; // Function name + + struct iterator + { + const ppu_function* _this; + typename std::map::const_iterator it; + usz index = 0; + + std::pair operator*() const + { + return _this->blocks.empty() ? std::pair(_this->addr, _this->size) : *it; + } + + iterator& operator++() + { + index++; + + if (it != _this->blocks.end()) + { + it++; + } + + return *this; + } + + bool operator==(const iterator& rhs) const noexcept + { + return it == rhs.it || (rhs.index == index && _this->blocks.empty()); + } + + bool operator!=(const iterator& rhs) const noexcept + { + return !operator==(rhs); + } + }; + + iterator begin() const + { + return iterator{this, blocks.begin()}; + } + + iterator end() const + { + return iterator{this, blocks.end(), 1}; + } }; // PPU Relocation Information @@ -87,18 +122,57 @@ struct ppu_module : public Type ppu_module& operator=(ppu_module&&) noexcept = default; - uchar sha1[20]{}; - std::string name{}; - std::string path{}; + uchar sha1[20]{}; // Hash + std::string name{}; // Filename + std::string path{}; // Filepath s64 offset = 0; // Offset of file - std::string cache{}; - std::vector relocs{}; - std::vector segs{}; - std::vector secs{}; - std::vector funcs{}; - std::vector applied_patches; - std::deque> allocations; - std::map addr_to_seg_index; + mutable bs_t attr{}; // Shared module attributes + std::string cache{}; // Cache file path + std::vector relocs{}; // Relocations + std::vector segs{}; // Segments + std::vector secs{}; // Segment sections + std::vector funcs{}; // Function list + std::vector applied_patches; // Patch addresses + std::deque> allocations; // Segment memory allocations + std::map addr_to_seg_index; // address->segment ordered translator map + ppu_module* parent = nullptr; // For compilation: refers to original structure (is whole, not partitioned) + std::pair local_bounds{0, u32{umax}}; // Module addresses range + std::shared_ptr> jit_bounds; // JIT instance modules addresses range + bool is_relocatable = false; // Is code relocatable(?) + + template + auto as_span(T&& arg, bool bound_local, bool bound_jit) const + { + using unref = std::remove_reference_t; + using type = std::conditional_t, std::add_const_t, typename unref::value_type>; + + if (bound_local || bound_jit) + { + // Return span bound to specified bounds + const auto [min_addr, max_addr] = bound_jit ? *jit_bounds : local_bounds; + constexpr auto compare = [](const type& a, u32 addr) { return a.addr < addr; }; + const auto end = arg.data() + arg.size(); + const auto start = std::lower_bound(arg.data(), end, min_addr, compare); + return std::span{ start, std::lower_bound(start, end, max_addr, compare) }; + } + + return std::span(arg.data(), arg.size()); + } + + auto get_funcs(bool bound_local = true, bool bound_jit = false) + { + return as_span(parent ? parent->funcs : funcs, bound_local, bound_jit); + } + + auto get_funcs(bool bound_local = true, bool bound_jit = false) const + { + return as_span(parent ? parent->funcs : funcs, bound_local, bound_jit); + } + + auto get_relocs(bool bound_local = false) const + { + return as_span(parent ? parent->relocs : relocs, bound_local, false); + } // Copy info without functions void copy_part(const ppu_module& info) @@ -106,11 +180,13 @@ struct ppu_module : public Type std::memcpy(sha1, info.sha1, sizeof(sha1)); name = info.name; path = info.path; - relocs = info.relocs; segs = info.segs; - secs = info.secs; allocations = info.allocations; addr_to_seg_index = info.addr_to_seg_index; + parent = const_cast(&info); + attr = info.attr; + is_relocatable = info.is_relocatable; + local_bounds = {u32{umax}, 0}; // Initially empty range } bool analyse(u32 lib_toc, u32 entry, u32 end, const std::vector& applied, const std::vector& exported_funcs = std::vector{}, std::function check_aborted = {}); @@ -263,7 +339,10 @@ struct ppu_pattern_matrix struct ppu_itype { static constexpr struct branch_tag{} branch{}; // Branch Instructions - static constexpr struct trap_tag{} trap{}; // Branch Instructions + static constexpr struct trap_tag{} trap{}; // Trap Instructions + static constexpr struct store_tag{} store{}; // Memory store Instructions + static constexpr struct load_tag{} load{}; // Memory load Instructions (TODO: Fill it) + static constexpr struct memory_tag{} memory{}; // Memory Instructions enum class type { @@ -428,8 +507,6 @@ struct ppu_itype VXOR, MULLI, SUBFIC, - CMPLI, - CMPI, ADDIC, ADDI, ADDIS, @@ -459,7 +536,6 @@ struct ppu_itype RLDIMI, RLDCL, RLDCR, - CMP, LVSL, LVEBX, SUBFC, @@ -476,13 +552,11 @@ struct ppu_itype CNTLZW, SLD, AND, - CMPL, LVSR, LVEHX, SUBF, SUBFO, LDUX, - DCBST, LWZUX, CNTLZD, ANDC, @@ -497,26 +571,15 @@ struct ppu_itype NEGO, LBZUX, NOR, - STVEBX, SUBFE, SUBFEO, ADDE, ADDEO, MTOCRF, - STDX, - STWCX, - STWX, - STVEHX, - STDUX, - STWUX, - STVEWX, SUBFZE, SUBFZEO, ADDZE, ADDZEO, - STDCX, - STBX, - STVX, SUBFME, SUBFMEO, MULLD, @@ -525,11 +588,8 @@ struct ppu_itype ADDMEO, MULLW, MULLWO, - DCBTST, - STBUX, ADD, ADDO, - DCBT, LHZX, EQV, ECIWX, @@ -537,26 +597,20 @@ struct ppu_itype XOR, MFSPR, LWAX, - DST, LHAX, LVXL, MFTB, LWAUX, - DSTST, LHAUX, - STHX, ORC, ECOWX, - STHUX, OR, DIVDU, DIVDUO, DIVWU, DIVWUO, MTSPR, - DCBI, NAND, - STVXL, DIVD, DIVDO, DIVW, @@ -571,66 +625,34 @@ struct ppu_itype LVRX, LSWI, LFSUX, - SYNC, LFDX, LFDUX, - STVLX, - STDBRX, - STSWX, - STWBRX, - STFSX, - STVRX, - STFSUX, - STSWI, - STFDX, - STFDUX, LVLXL, LHBRX, SRAW, SRAD, LVRXL, - DSS, SRAWI, SRADI, - EIEIO, - STVLXL, - STHBRX, EXTSH, - STVRXL, EXTSB, - STFIWX, EXTSW, - ICBI, - DCBZ, LWZ, LWZU, LBZ, LBZU, - STW, - STWU, - STB, - STBU, LHZ, LHZU, LHA, LHAU, - STH, - STHU, LMW, - STMW, LFS, LFSU, LFD, LFDU, - STFS, - STFSU, - STFD, - STFDU, LD, LDU, LWA, - STD, - STDU, FDIVS, FSUBS, FADDS, @@ -777,6 +799,65 @@ struct ppu_itype FCTIDZ_, FCFID_, + CMPLI, + CMPI, + CMP, + CMPL, + + STMW, // store_tag first + STSWX, + STSWI, + STVXL, + STVLX, + STVRX, + STVEBX, + STVEHX, + STVEWX, + STVX, + STVLXL, + STVRXL, + STDX, + STDUX, + STDCX, + STDBRX, + STD, + STDU, + STFDUX, + STFDX, + STFDU, + STFD, + STFS, + STFSU, + STFSX, + STFSUX, + STFIWX, + STWCX, + STWX, + STWUX, + STWBRX, + STWU, + STW, + STHBRX, + STHX, + STHUX, + STH, + STBX, + STBU, + STB, + STHU, + STBUX, + DCBZ, + DCBI, // Perceive memory barrier or flag instructions as stores + DCBTST, + DCBT, + DCBST, + DST, + DSS, + ICBI, + SYNC, + EIEIO, + DSTST, // store_tag last + B, // branch_tag first BC, BCLR, @@ -805,6 +886,11 @@ struct ppu_itype { return value >= TD && value <= TWI; } + + friend constexpr bool operator &(type value, store_tag) + { + return value >= STMW && value <= DSTST; + } }; struct ppu_iname @@ -1331,738 +1417,3 @@ struct ppu_iname #undef NAME #undef NAME_ }; - -// PPU Analyser Context -struct ppu_acontext -{ - // General-purpose register range - struct spec_gpr - { - // Integral range: normalized undef = (0;UINT64_MAX), unnormalized undefs are possible (when max = min - 1) - // Bit range: constant 0 = (0;0), constant 1 = (1;1), normalized undef = (0;1), unnormalized undef = (1;0) - - u64 imin = 0ull; // Integral range begin - u64 imax = ~0ull; // Integral range end - u64 bmin = 0ull; // Bit range begin - u64 bmax = ~0ull; // Bit range end - - void set_undef() - { - imin = 0; - imax = -1; - bmin = 0; - bmax = -1; - } - - // (Number of possible values - 1), 0 = const - u64 div() const - { - return imax - imin; - } - - // Return zero bits for zeros, ones for ones or undefs - u64 mask() const - { - return bmin | bmax; - } - - // Return one bits for ones, zeros for zeros or undefs - u64 ones() const - { - return bmin & bmax; - } - - // Return one bits for undefs - u64 undefs() const - { - return bmin ^ bmax; - } - - // Return number of trailing zero bits - u64 tz() const - { - return std::countr_zero(mask()); - } - - // Range NOT - spec_gpr operator ~() const - { - spec_gpr r; - r.imin = ~imax; - r.imax = ~imin; - r.bmin = ~bmax; - r.bmax = ~bmin; - return r; - } - - // Range ADD - spec_gpr operator +(const spec_gpr& rhs) const - { - spec_gpr r{}; - - const u64 adiv = div(); - const u64 bdiv = rhs.div(); - - // Check overflow, generate normalized range - if (adiv != umax && bdiv != umax && adiv <= adiv + bdiv) - { - r = range(imin + rhs.imin, imax + rhs.imax); - } - - // Carry for bitrange computation - u64 cmin = 0; - u64 cmax = 0; - - const u64 amask = mask(); - const u64 bmask = rhs.mask(); - const u64 aones = ones(); - const u64 bones = rhs.ones(); - - for (u32 i = 0; i < 64; i++) - { - cmin += ((amask >> i) & 1) + ((bmask >> i) & 1); - cmax += ((aones >> i) & 1) + ((bones >> i) & 1); - - // Discover some constant bits - if (cmin == cmax) - { - r.bmin |= (cmin & 1) << i; - r.bmax &= ~((~cmin & 1) << i); - } - - cmin >>= 1; - cmax >>= 1; - } - - return r; - } - - // Range AND - spec_gpr operator &(const spec_gpr& rhs) const - { - // Ignore inverted ranges (TODO) - if (imin > imax || rhs.imin > rhs.imax) - { - return approx(ones() & rhs.ones(), mask() & rhs.mask()); - } - - // Const (TODO: remove when unnecessary) - if (imin == imax && rhs.imin == rhs.imax) - { - return fixed(imin & rhs.imin); - } - - // Swap (TODO: remove when unnecessary) - if (imin == imax || rhs.undefs() > undefs()) - { - return rhs & *this; - } - - // Copy and attempt to partially preserve integral range - spec_gpr r = *this; - - for (u32 i = 63; ~i; i--) - { - const u64 m = 1ull << i; - - if (!(rhs.mask() & m)) - { - if (r.undefs() & m) - { - // undef -> 0 - r.imin &= ~(m - 1); - r.imax |= (m - 1); - r.imin &= ~m; - r.imax &= ~m; - } - else if (r.ones() & m) - { - // 1 -> 0 - if ((r.imin ^ r.imax) > (m - 1)) - { - r.imin &= ~(m - 1); - r.imax |= (m - 1); - } - - r.imin &= ~m; - r.imax &= ~m; - } - } - else if (rhs.undefs() & m) - { - // -> undef - r.imin &= ~(m - 1); - r.imax |= (m - 1); - r.imin &= ~m; - r.imax |= m; - } - } - - r.bmin = ones() & rhs.ones(); - r.bmax = mask() & rhs.mask(); - return r; - } - - // Range OR - spec_gpr operator |(const spec_gpr& rhs) const - { - // Ignore inverted ranges (TODO) - if (imin > imax || rhs.imin > rhs.imax) - { - return approx(ones() | rhs.ones(), mask() | rhs.mask()); - } - - // Const (TODO: remove when unnecessary) - if (imin == imax && rhs.imin == rhs.imax) - { - return fixed(imin | rhs.imin); - } - - // Swap (TODO: remove when unnecessary) - if (imin == imax || rhs.undefs() > undefs()) - { - return rhs | *this; - } - - // Copy and attempt to partially preserve integral range - spec_gpr r = *this; - - for (u32 i = 63; ~i; i--) - { - const u64 m = 1ull << i; - - if (rhs.ones() & m) - { - if (r.undefs() & m) - { - // undef -> 1 - r.imin &= ~(m - 1); - r.imax |= (m - 1); - r.imin |= m; - r.imax |= m; - } - else if (!(r.mask() & m)) - { - // 0 -> 1 - if ((r.imin ^ r.imax) > (m - 1)) - { - r.imin &= ~(m - 1); - r.imax |= (m - 1); - } - - r.imin |= m; - r.imax |= m; - } - } - else if (rhs.undefs() & m) - { - // -> undef - r.imin &= ~(m - 1); - r.imax |= (m - 1); - r.imin &= ~m; - r.imax |= m; - } - } - - r.bmin = ones() | rhs.ones(); - r.bmax = mask() | rhs.mask(); - return r; - } - - // Range XOR - spec_gpr operator ^(const spec_gpr& rhs) const - { - return (~*this & rhs) | (*this & ~rhs); - } - - // Check whether the value is in range - bool test(u64 value) const - { - if (imin <= imax) - { - if (value < imin || value > imax) - { - return false; - } - } - else - { - if (value < imin && value > imax) - { - return false; - } - } - - if ((value & mask()) != value) - { - return false; - } - - if ((value | ones()) != value) - { - return false; - } - - return true; - } - - // Constant value - static spec_gpr fixed(u64 value) - { - spec_gpr r; - r.imin = value; - r.imax = value; - r.bmin = value; - r.bmax = value; - return r; - } - - // Range (tz = number of constant trailing zeros) - static spec_gpr range(u64 min, u64 max, u64 tz = 0) - { - const u64 mask = tz < 64 ? ~0ull << tz : 0ull; - - spec_gpr r; - r.bmin = 0; - r.bmax = mask; - - // Normalize min/max for tz (TODO) - if (min < max) - { - // Inverted constant MSB mask - const u64 mix = ~0ull >> std::countl_zero(min ^ max); - r.bmin |= min & ~mix; - r.bmax &= max | mix; - - r.imin = (min + ~mask) & mask; - r.imax = max & mask; - ensure(r.imin <= r.imax); // "Impossible range" - } - else - { - r.imin = min & mask; - r.imax = (max + ~mask) & mask; - ensure(r.imin >= r.imax); // "Impossible range" - } - - // Fix const values - if (r.imin == r.imax) - { - r.bmin = r.imin; - r.bmax = r.imax; - } - - return r; - } - - // Make from bitrange (normalize, approximate range values) - static spec_gpr approx(u64 bmin, u64 bmax) - { - spec_gpr r; - r.imin = bmin & ~(bmin ^ bmax); - r.imax = bmax | (bmin ^ bmax); - r.bmin = bmin & ~(bmin ^ bmax); - r.bmax = bmax | (bmin ^ bmax); - return r; - } - } gpr[32]{}; - - // Vector registers (draft) - struct spec_vec - { - u8 imin8[16]{}; - u8 imax8[16]{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}; - u16 imin16[8]{}; - u16 imax16[8]{0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}; - u32 imin32[4]{}; - u32 imax32[4]{0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu}; - u64 bmin64[2]{}; - u64 bmax64[2]{0xffffffffffffffffull, 0xffffffffffffffffull}; - }; - - // Info - u32 cia; - - // Analyser step - void UNK(ppu_opcode_t); - - void MFVSCR(ppu_opcode_t); - void MTVSCR(ppu_opcode_t); - void VADDCUW(ppu_opcode_t); - void VADDFP(ppu_opcode_t); - void VADDSBS(ppu_opcode_t); - void VADDSHS(ppu_opcode_t); - void VADDSWS(ppu_opcode_t); - void VADDUBM(ppu_opcode_t); - void VADDUBS(ppu_opcode_t); - void VADDUHM(ppu_opcode_t); - void VADDUHS(ppu_opcode_t); - void VADDUWM(ppu_opcode_t); - void VADDUWS(ppu_opcode_t); - void VAND(ppu_opcode_t); - void VANDC(ppu_opcode_t); - void VAVGSB(ppu_opcode_t); - void VAVGSH(ppu_opcode_t); - void VAVGSW(ppu_opcode_t); - void VAVGUB(ppu_opcode_t); - void VAVGUH(ppu_opcode_t); - void VAVGUW(ppu_opcode_t); - void VCFSX(ppu_opcode_t); - void VCFUX(ppu_opcode_t); - void VCMPBFP(ppu_opcode_t); - void VCMPEQFP(ppu_opcode_t); - void VCMPEQUB(ppu_opcode_t); - void VCMPEQUH(ppu_opcode_t); - void VCMPEQUW(ppu_opcode_t); - void VCMPGEFP(ppu_opcode_t); - void VCMPGTFP(ppu_opcode_t); - void VCMPGTSB(ppu_opcode_t); - void VCMPGTSH(ppu_opcode_t); - void VCMPGTSW(ppu_opcode_t); - void VCMPGTUB(ppu_opcode_t); - void VCMPGTUH(ppu_opcode_t); - void VCMPGTUW(ppu_opcode_t); - void VCTSXS(ppu_opcode_t); - void VCTUXS(ppu_opcode_t); - void VEXPTEFP(ppu_opcode_t); - void VLOGEFP(ppu_opcode_t); - void VMADDFP(ppu_opcode_t); - void VMAXFP(ppu_opcode_t); - void VMAXSB(ppu_opcode_t); - void VMAXSH(ppu_opcode_t); - void VMAXSW(ppu_opcode_t); - void VMAXUB(ppu_opcode_t); - void VMAXUH(ppu_opcode_t); - void VMAXUW(ppu_opcode_t); - void VMHADDSHS(ppu_opcode_t); - void VMHRADDSHS(ppu_opcode_t); - void VMINFP(ppu_opcode_t); - void VMINSB(ppu_opcode_t); - void VMINSH(ppu_opcode_t); - void VMINSW(ppu_opcode_t); - void VMINUB(ppu_opcode_t); - void VMINUH(ppu_opcode_t); - void VMINUW(ppu_opcode_t); - void VMLADDUHM(ppu_opcode_t); - void VMRGHB(ppu_opcode_t); - void VMRGHH(ppu_opcode_t); - void VMRGHW(ppu_opcode_t); - void VMRGLB(ppu_opcode_t); - void VMRGLH(ppu_opcode_t); - void VMRGLW(ppu_opcode_t); - void VMSUMMBM(ppu_opcode_t); - void VMSUMSHM(ppu_opcode_t); - void VMSUMSHS(ppu_opcode_t); - void VMSUMUBM(ppu_opcode_t); - void VMSUMUHM(ppu_opcode_t); - void VMSUMUHS(ppu_opcode_t); - void VMULESB(ppu_opcode_t); - void VMULESH(ppu_opcode_t); - void VMULEUB(ppu_opcode_t); - void VMULEUH(ppu_opcode_t); - void VMULOSB(ppu_opcode_t); - void VMULOSH(ppu_opcode_t); - void VMULOUB(ppu_opcode_t); - void VMULOUH(ppu_opcode_t); - void VNMSUBFP(ppu_opcode_t); - void VNOR(ppu_opcode_t); - void VOR(ppu_opcode_t); - void VPERM(ppu_opcode_t); - void VPKPX(ppu_opcode_t); - void VPKSHSS(ppu_opcode_t); - void VPKSHUS(ppu_opcode_t); - void VPKSWSS(ppu_opcode_t); - void VPKSWUS(ppu_opcode_t); - void VPKUHUM(ppu_opcode_t); - void VPKUHUS(ppu_opcode_t); - void VPKUWUM(ppu_opcode_t); - void VPKUWUS(ppu_opcode_t); - void VREFP(ppu_opcode_t); - void VRFIM(ppu_opcode_t); - void VRFIN(ppu_opcode_t); - void VRFIP(ppu_opcode_t); - void VRFIZ(ppu_opcode_t); - void VRLB(ppu_opcode_t); - void VRLH(ppu_opcode_t); - void VRLW(ppu_opcode_t); - void VRSQRTEFP(ppu_opcode_t); - void VSEL(ppu_opcode_t); - void VSL(ppu_opcode_t); - void VSLB(ppu_opcode_t); - void VSLDOI(ppu_opcode_t); - void VSLH(ppu_opcode_t); - void VSLO(ppu_opcode_t); - void VSLW(ppu_opcode_t); - void VSPLTB(ppu_opcode_t); - void VSPLTH(ppu_opcode_t); - void VSPLTISB(ppu_opcode_t); - void VSPLTISH(ppu_opcode_t); - void VSPLTISW(ppu_opcode_t); - void VSPLTW(ppu_opcode_t); - void VSR(ppu_opcode_t); - void VSRAB(ppu_opcode_t); - void VSRAH(ppu_opcode_t); - void VSRAW(ppu_opcode_t); - void VSRB(ppu_opcode_t); - void VSRH(ppu_opcode_t); - void VSRO(ppu_opcode_t); - void VSRW(ppu_opcode_t); - void VSUBCUW(ppu_opcode_t); - void VSUBFP(ppu_opcode_t); - void VSUBSBS(ppu_opcode_t); - void VSUBSHS(ppu_opcode_t); - void VSUBSWS(ppu_opcode_t); - void VSUBUBM(ppu_opcode_t); - void VSUBUBS(ppu_opcode_t); - void VSUBUHM(ppu_opcode_t); - void VSUBUHS(ppu_opcode_t); - void VSUBUWM(ppu_opcode_t); - void VSUBUWS(ppu_opcode_t); - void VSUMSWS(ppu_opcode_t); - void VSUM2SWS(ppu_opcode_t); - void VSUM4SBS(ppu_opcode_t); - void VSUM4SHS(ppu_opcode_t); - void VSUM4UBS(ppu_opcode_t); - void VUPKHPX(ppu_opcode_t); - void VUPKHSB(ppu_opcode_t); - void VUPKHSH(ppu_opcode_t); - void VUPKLPX(ppu_opcode_t); - void VUPKLSB(ppu_opcode_t); - void VUPKLSH(ppu_opcode_t); - void VXOR(ppu_opcode_t); - void TDI(ppu_opcode_t); - void TWI(ppu_opcode_t); - void MULLI(ppu_opcode_t); - void SUBFIC(ppu_opcode_t); - void CMPLI(ppu_opcode_t); - void CMPI(ppu_opcode_t); - void ADDIC(ppu_opcode_t); - void ADDI(ppu_opcode_t); - void ADDIS(ppu_opcode_t); - void BC(ppu_opcode_t); - void SC(ppu_opcode_t); - void B(ppu_opcode_t); - void MCRF(ppu_opcode_t); - void BCLR(ppu_opcode_t); - void CRNOR(ppu_opcode_t); - void CRANDC(ppu_opcode_t); - void ISYNC(ppu_opcode_t); - void CRXOR(ppu_opcode_t); - void CRNAND(ppu_opcode_t); - void CRAND(ppu_opcode_t); - void CREQV(ppu_opcode_t); - void CRORC(ppu_opcode_t); - void CROR(ppu_opcode_t); - void BCCTR(ppu_opcode_t); - void RLWIMI(ppu_opcode_t); - void RLWINM(ppu_opcode_t); - void RLWNM(ppu_opcode_t); - void ORI(ppu_opcode_t); - void ORIS(ppu_opcode_t); - void XORI(ppu_opcode_t); - void XORIS(ppu_opcode_t); - void ANDI(ppu_opcode_t); - void ANDIS(ppu_opcode_t); - void RLDICL(ppu_opcode_t); - void RLDICR(ppu_opcode_t); - void RLDIC(ppu_opcode_t); - void RLDIMI(ppu_opcode_t); - void RLDCL(ppu_opcode_t); - void RLDCR(ppu_opcode_t); - void CMP(ppu_opcode_t); - void TW(ppu_opcode_t); - void LVSL(ppu_opcode_t); - void LVEBX(ppu_opcode_t); - void SUBFC(ppu_opcode_t); - void ADDC(ppu_opcode_t); - void MULHDU(ppu_opcode_t); - void MULHWU(ppu_opcode_t); - void MFOCRF(ppu_opcode_t); - void LWARX(ppu_opcode_t); - void LDX(ppu_opcode_t); - void LWZX(ppu_opcode_t); - void SLW(ppu_opcode_t); - void CNTLZW(ppu_opcode_t); - void SLD(ppu_opcode_t); - void AND(ppu_opcode_t); - void CMPL(ppu_opcode_t); - void LVSR(ppu_opcode_t); - void LVEHX(ppu_opcode_t); - void SUBF(ppu_opcode_t); - void LDUX(ppu_opcode_t); - void DCBST(ppu_opcode_t); - void LWZUX(ppu_opcode_t); - void CNTLZD(ppu_opcode_t); - void ANDC(ppu_opcode_t); - void TD(ppu_opcode_t); - void LVEWX(ppu_opcode_t); - void MULHD(ppu_opcode_t); - void MULHW(ppu_opcode_t); - void LDARX(ppu_opcode_t); - void DCBF(ppu_opcode_t); - void LBZX(ppu_opcode_t); - void LVX(ppu_opcode_t); - void NEG(ppu_opcode_t); - void LBZUX(ppu_opcode_t); - void NOR(ppu_opcode_t); - void STVEBX(ppu_opcode_t); - void SUBFE(ppu_opcode_t); - void ADDE(ppu_opcode_t); - void MTOCRF(ppu_opcode_t); - void STDX(ppu_opcode_t); - void STWCX(ppu_opcode_t); - void STWX(ppu_opcode_t); - void STVEHX(ppu_opcode_t); - void STDUX(ppu_opcode_t); - void STWUX(ppu_opcode_t); - void STVEWX(ppu_opcode_t); - void SUBFZE(ppu_opcode_t); - void ADDZE(ppu_opcode_t); - void STDCX(ppu_opcode_t); - void STBX(ppu_opcode_t); - void STVX(ppu_opcode_t); - void SUBFME(ppu_opcode_t); - void MULLD(ppu_opcode_t); - void ADDME(ppu_opcode_t); - void MULLW(ppu_opcode_t); - void DCBTST(ppu_opcode_t); - void STBUX(ppu_opcode_t); - void ADD(ppu_opcode_t); - void DCBT(ppu_opcode_t); - void LHZX(ppu_opcode_t); - void EQV(ppu_opcode_t); - void ECIWX(ppu_opcode_t); - void LHZUX(ppu_opcode_t); - void XOR(ppu_opcode_t); - void MFSPR(ppu_opcode_t); - void LWAX(ppu_opcode_t); - void DST(ppu_opcode_t); - void LHAX(ppu_opcode_t); - void LVXL(ppu_opcode_t); - void MFTB(ppu_opcode_t); - void LWAUX(ppu_opcode_t); - void DSTST(ppu_opcode_t); - void LHAUX(ppu_opcode_t); - void STHX(ppu_opcode_t); - void ORC(ppu_opcode_t); - void ECOWX(ppu_opcode_t); - void STHUX(ppu_opcode_t); - void OR(ppu_opcode_t); - void DIVDU(ppu_opcode_t); - void DIVWU(ppu_opcode_t); - void MTSPR(ppu_opcode_t); - void DCBI(ppu_opcode_t); - void NAND(ppu_opcode_t); - void STVXL(ppu_opcode_t); - void DIVD(ppu_opcode_t); - void DIVW(ppu_opcode_t); - void LVLX(ppu_opcode_t); - void LDBRX(ppu_opcode_t); - void LSWX(ppu_opcode_t); - void LWBRX(ppu_opcode_t); - void LFSX(ppu_opcode_t); - void SRW(ppu_opcode_t); - void SRD(ppu_opcode_t); - void LVRX(ppu_opcode_t); - void LSWI(ppu_opcode_t); - void LFSUX(ppu_opcode_t); - void SYNC(ppu_opcode_t); - void LFDX(ppu_opcode_t); - void LFDUX(ppu_opcode_t); - void STVLX(ppu_opcode_t); - void STDBRX(ppu_opcode_t); - void STSWX(ppu_opcode_t); - void STWBRX(ppu_opcode_t); - void STFSX(ppu_opcode_t); - void STVRX(ppu_opcode_t); - void STFSUX(ppu_opcode_t); - void STSWI(ppu_opcode_t); - void STFDX(ppu_opcode_t); - void STFDUX(ppu_opcode_t); - void LVLXL(ppu_opcode_t); - void LHBRX(ppu_opcode_t); - void SRAW(ppu_opcode_t); - void SRAD(ppu_opcode_t); - void LVRXL(ppu_opcode_t); - void DSS(ppu_opcode_t); - void SRAWI(ppu_opcode_t); - void SRADI(ppu_opcode_t); - void EIEIO(ppu_opcode_t); - void STVLXL(ppu_opcode_t); - void STHBRX(ppu_opcode_t); - void EXTSH(ppu_opcode_t); - void STVRXL(ppu_opcode_t); - void EXTSB(ppu_opcode_t); - void STFIWX(ppu_opcode_t); - void EXTSW(ppu_opcode_t); - void ICBI(ppu_opcode_t); - void DCBZ(ppu_opcode_t); - void LWZ(ppu_opcode_t); - void LWZU(ppu_opcode_t); - void LBZ(ppu_opcode_t); - void LBZU(ppu_opcode_t); - void STW(ppu_opcode_t); - void STWU(ppu_opcode_t); - void STB(ppu_opcode_t); - void STBU(ppu_opcode_t); - void LHZ(ppu_opcode_t); - void LHZU(ppu_opcode_t); - void LHA(ppu_opcode_t); - void LHAU(ppu_opcode_t); - void STH(ppu_opcode_t); - void STHU(ppu_opcode_t); - void LMW(ppu_opcode_t); - void STMW(ppu_opcode_t); - void LFS(ppu_opcode_t); - void LFSU(ppu_opcode_t); - void LFD(ppu_opcode_t); - void LFDU(ppu_opcode_t); - void STFS(ppu_opcode_t); - void STFSU(ppu_opcode_t); - void STFD(ppu_opcode_t); - void STFDU(ppu_opcode_t); - void LD(ppu_opcode_t); - void LDU(ppu_opcode_t); - void LWA(ppu_opcode_t); - void STD(ppu_opcode_t); - void STDU(ppu_opcode_t); - void FDIVS(ppu_opcode_t); - void FSUBS(ppu_opcode_t); - void FADDS(ppu_opcode_t); - void FSQRTS(ppu_opcode_t); - void FRES(ppu_opcode_t); - void FMULS(ppu_opcode_t); - void FMADDS(ppu_opcode_t); - void FMSUBS(ppu_opcode_t); - void FNMSUBS(ppu_opcode_t); - void FNMADDS(ppu_opcode_t); - void MTFSB1(ppu_opcode_t); - void MCRFS(ppu_opcode_t); - void MTFSB0(ppu_opcode_t); - void MTFSFI(ppu_opcode_t); - void MFFS(ppu_opcode_t); - void MTFSF(ppu_opcode_t); - void FCMPU(ppu_opcode_t); - void FRSP(ppu_opcode_t); - void FCTIW(ppu_opcode_t); - void FCTIWZ(ppu_opcode_t); - void FDIV(ppu_opcode_t); - void FSUB(ppu_opcode_t); - void FADD(ppu_opcode_t); - void FSQRT(ppu_opcode_t); - void FSEL(ppu_opcode_t); - void FMUL(ppu_opcode_t); - void FRSQRTE(ppu_opcode_t); - void FMSUB(ppu_opcode_t); - void FMADD(ppu_opcode_t); - void FNMSUB(ppu_opcode_t); - void FNMADD(ppu_opcode_t); - void FCMPO(ppu_opcode_t); - void FNEG(ppu_opcode_t); - void FMR(ppu_opcode_t); - void FNABS(ppu_opcode_t); - void FABS(ppu_opcode_t); - void FCTID(ppu_opcode_t); - void FCTIDZ(ppu_opcode_t); - void FCFID(ppu_opcode_t); -}; diff --git a/rpcs3/Emu/Cell/PPUDisAsm.cpp b/rpcs3/Emu/Cell/PPUDisAsm.cpp index 9c9fa0e1fa..569b60b612 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/PPUDisAsm.cpp @@ -132,22 +132,7 @@ std::pair PPUDisAsm::try_get_const_op_gpr_value(u32 re switch (type) { case ppu_itype::ADDI: - { - if (op.rd != reg) - { - // Destination register is not relevant to us - break; - } - - u64 reg_ra = 0; - - if (op.ra) - { - GET_CONST_REG(reg_ra, op.ra); - } - - return { form, reg_ra + op.simm16 }; - } + case ppu_itype::ADDIC: case ppu_itype::ADDIS: { if (op.rd != reg) @@ -157,12 +142,12 @@ std::pair PPUDisAsm::try_get_const_op_gpr_value(u32 re u64 reg_ra = 0; - if (op.ra) + if (op.ra || type == ppu_itype::ADDIC) { GET_CONST_REG(reg_ra, op.ra); } - return { form, reg_ra + op.simm16 * 65536 }; + return { form, reg_ra + (type == ppu_itype::ADDIS ? op.simm16 * 65536 : op.simm16) }; } case ppu_itype::ORI: { @@ -1286,6 +1271,12 @@ void PPUDisAsm::CMPI(ppu_opcode_t op) void PPUDisAsm::ADDIC(ppu_opcode_t op) { DisAsm_R2_IMM(op.main & 1 ? "addic." : "addic", op.rd, op.ra, op.simm16); + + if (auto [is_const, value] = try_get_const_gpr_value(op.ra); is_const) + { + // Comment constant formation + comment_constant(last_opcode, value + op.simm16); + } } void PPUDisAsm::ADDI(ppu_opcode_t op) diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 6a7ce4312b..d95737ba50 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -15,6 +15,7 @@ #include "Emu/Cell/PPUOpcodes.h" #include "Emu/Cell/SPUThread.h" #include "Emu/Cell/PPUAnalyser.h" +#include "Emu/Cell/timers.hpp" #include "Emu/Cell/lv2/sys_process.h" #include "Emu/Cell/lv2/sys_prx.h" @@ -1951,6 +1952,7 @@ shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_load, c } prx->applied_patches = applied; + prx->is_relocatable = true; prx->analyse(toc, 0, end, applied, exported_funcs); if (!ar && !virtual_load) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 8da9cca8f0..65467b168b 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -12,9 +12,9 @@ #include "Emu/Memory/vm_locking.h" #include "Emu/RSX/Core/RSXReservationLock.hpp" #include "Emu/VFS.h" -#include "Emu/vfs_config.h" #include "Emu/system_progress.hpp" #include "Emu/system_utils.hpp" +#include "Emu/System.h" #include "PPUThread.h" #include "PPUInterpreter.h" #include "PPUAnalyser.h" @@ -40,19 +40,10 @@ #endif #include #include -#if LLVM_VERSION_MAJOR < 17 -#include -#include -#include -#include -#include -#include -#else #include #include #include #include -#endif #ifdef _MSC_VER #pragma warning(pop) #else @@ -66,6 +57,7 @@ #include #include #include +#include #include "util/asm.hpp" #include "util/vm.hpp" @@ -73,6 +65,8 @@ #include "util/simd.hpp" #include "util/sysinfo.hpp" +#include "Utilities/sema.h" + #ifdef __APPLE__ #include #endif @@ -176,7 +170,7 @@ bool serialize(utils::serial& ar, typename ppu_thread::cr_b extern void ppu_initialize(); extern void ppu_finalize(const ppu_module& info, bool force_mem_release = false); extern bool ppu_initialize(const ppu_module& info, bool check_only = false, u64 file_size = 0); -static void ppu_initialize2(class jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name, const ppu_module& whole_module); +static void ppu_initialize2(class jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name); extern bool ppu_load_exec(const ppu_exec_object&, bool virtual_load, const std::string&, utils::serial* = nullptr); extern std::pair, CellError> ppu_load_overlay(const ppu_exec_object&, bool virtual_load, const std::string& path, s64 file_offset, utils::serial* = nullptr); extern void ppu_unload_prx(const lv2_prx&); @@ -342,11 +336,10 @@ const auto ppu_gateway = build_function_asm("ppu_gateway", // Load offset value c.mov(cia_addr_reg, Imm(static_cast(::offset32(&ppu_thread::cia)))); // Load cia - c.ldr(a64::w15, arm::Mem(ppu_t_base, cia_addr_reg)); + c.ldr(pc.w(), arm::Mem(ppu_t_base, cia_addr_reg)); + // Multiply by 2 to index into ptr table - const arm::GpX index_shift = a64::x12; - c.mov(index_shift, Imm(2)); - c.mul(pc, pc, index_shift); + c.add(pc, pc, pc); // Load call target const arm::GpX call_target = a64::x13; @@ -355,7 +348,7 @@ const auto ppu_gateway = build_function_asm("ppu_gateway", const arm::GpX reg_hp = a64::x21; c.mov(reg_hp, call_target); c.lsr(reg_hp, reg_hp, 48); - c.lsl(a64::w21, a64::w21, 13); + c.lsl(reg_hp.w(), reg_hp.w(), 13); // Zero top 16 bits of call target c.lsl(call_target, call_target, Imm(16)); @@ -449,7 +442,29 @@ const auto ppu_recompiler_fallback_ghc = build_function_asm("", [](native_asm& c, auto& args) +{ + using namespace asmjit; + + Label fallback_fn = c.newLabel(); + Label escape_fn = c.newLabel(); + + // This is called as GHC so the first arg is in x20. + // Fix up the arg registers and call the real function. + c.mov(args[0], a64::x20); + c.ldr(a64::x13, arm::ptr(fallback_fn)); + c.blr(a64::x13); + + // There is no call-stack to return to in arm64 GHC. Escape to host. + c.mov(a64::x0, a64::x20); + c.ldr(a64::x13, arm::ptr(escape_fn)); + c.br(a64::x13); + + c.bind(fallback_fn); + c.embedUInt64(reinterpret_cast(ppu_recompiler_fallback)); + c.bind(escape_fn); + c.embedUInt64(reinterpret_cast(ppu_escape)); +}); #endif // Get pointer to executable cache @@ -2447,11 +2462,6 @@ void ppu_thread::serialize_common(utils::serial& ar) fmt::throw_exception("Failed to serialize PPU thread ID=0x%x (cia=0x%x, ar=%s)", this->id, cia, ar); } - if (ar.is_writing()) - { - ppu_log.notice("Saving PPU Thread [0x%x: %s]: cia=0x%x, state=%s", id, *ppu_tname.load(), cia, +state); - } - ar(optional_savestate_state, vr); if (!ar.is_writing()) @@ -2523,7 +2533,9 @@ ppu_thread::ppu_thread(utils::serial& ar) } }; - switch (const u32 status = ar.pop()) + const u32 status = ar.pop(); + + switch (status) { case PPU_THREAD_STATUS_IDLE: { @@ -2654,11 +2666,14 @@ ppu_thread::ppu_thread(utils::serial& ar) ppu_tname = make_single(ar.pop()); - ppu_log.notice("Loading PPU Thread [0x%x: %s]: cia=0x%x, state=%s", id, *ppu_tname.load(), cia, +state); + ppu_log.notice("Loading PPU Thread [0x%x: %s]: cia=0x%x, state=%s, status=%s", id, *ppu_tname.load(), cia, +state, ppu_thread_status{status}); } void ppu_thread::save(utils::serial& ar) { + // For debugging purposes, load this as soon as this function enters + const bs_t state_flags = state; + USING_SERIALIZATION_VERSION(ppu); const u64 entry = std::bit_cast(entry_func); @@ -2708,6 +2723,15 @@ void ppu_thread::save(utils::serial& ar) } ar(*ppu_tname.load()); + + if (current_module && current_module[0]) + { + ppu_log.notice("Saving PPU Thread [0x%x: %s]: cia=0x%x, state=%s, statu=%s (at function: %s)", id, *ppu_tname.load(), cia, state_flags, ppu_thread_status{status}, last_function); + } + else + { + ppu_log.notice("Saving PPU Thread [0x%x: %s]: cia=0x%x, state=%s, statu=%s", id, *ppu_tname.load(), cia, state_flags, ppu_thread_status{status}); + } } ppu_thread::thread_name_t::operator std::string() const @@ -3665,6 +3689,9 @@ struct jit_core_allocator // Initialize global semaphore with the max number of threads ::semaphore<0x7fff> sem{std::max(thread_count, 1)}; + // Mutex for special extra-large modules to compile alone + shared_mutex shared_mtx; + static s16 limit() { return static_cast(std::min(0x7fff, utils::get_thread_count())); @@ -3677,8 +3704,8 @@ namespace // Compiled PPU module info struct jit_module { - void(*symbol_resolver)(u8*, u64) = nullptr; - std::shared_ptr pjit; + std::vector symbol_resolvers; + std::vector> pjit; bool init = false; }; @@ -3729,9 +3756,29 @@ namespace } to_destroy.pjit = std::move(found->second.pjit); + to_destroy.symbol_resolvers = std::move(found->second.symbol_resolvers); bucket.map.erase(found); } + + jit_module_manager& operator=(thread_state s) noexcept + { + if (s == thread_state::destroying_context) + { + for (auto& buck : buckets) + { + for (auto& mod : buck.map) + { + for (auto& jit : mod.second.pjit) + { + *jit = s; + } + } + } + } + + return *this; + } }; } #endif @@ -4445,7 +4492,7 @@ extern void ppu_initialize() idm::select([&](u32, lv2_prx& _module) { - if (_module.funcs.empty()) + if (_module.get_funcs().empty()) { return; } @@ -4556,7 +4603,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s auto& ppu_toc = toc_manager.toc_map; - for (const auto& func : info.funcs) + for (const auto& func : info.get_funcs()) { if (func.size && func.blocks.empty()) { @@ -4659,13 +4706,16 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s jit_module& jit_mod = g_fxo->get().get(cache_path + "_" + std::to_string(std::bit_cast(info.segs[0].ptr))); // Compiler instance (deferred initialization) - std::shared_ptr& jit = jit_mod.pjit; + std::vector>& jits = jit_mod.pjit; // Split module into fragments <= 1 MiB usz fpos = 0; + // Modules counted so far + usz module_counter = 0; + // Difference between function name and current location - const u32 reloc = info.relocs.empty() ? 0 : ::at32(info.segs, 0).addr; + const u32 reloc = info.is_relocatable ? ::at32(info.segs, 0).addr : 0; // Info sent to threads std::vector>> workload; @@ -4684,14 +4734,14 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s const cpu_thread* cpu = cpu_thread::get_current(); - for (auto& func : info.funcs) + for (auto& func : info.get_funcs()) { if (func.size == 0) { continue; } - for (const auto& [addr, size] : func.blocks) + for (const auto [addr, size] : func) { if (size == 0) { @@ -4722,28 +4772,265 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s } } - u32 total_compile = 0; + // Limit how many modules are per JIt instance + // Advantage to lower the limit: + // 1. Lowering contoniues memory requirements for allocations + // Its disadvantage: + // 1. B instruction can wander up to 16MB relatively to its range, + // each additional split of JIT instance results in a downgraded version of around (100% / N-1th) - (100% / Nth) percent of instructions + // where N is the total amunt of JIT instances + // Subject to change + constexpr u32 c_moudles_per_jit = 100; - while (!jit_mod.init && fpos < info.funcs.size()) + std::shared_ptr> local_jit_bounds = std::make_shared>(u32{umax}, 0); + + const auto shared_runtime = make_shared(); + const auto shared_map = make_shared>(); + const auto full_sample = make_shared(0); + const auto shared_mtx = make_shared(); + + auto symbols_cement = [runtime = shared_runtime, reloc, seg0 = info.segs[0].addr, bound = info.segs[0].addr + info.segs[0].size - reloc, func_map = shared_map, shared_mtx, full_sample](const std::string& name) -> u64 { - // Initialize compiler instance - if (!jit && is_being_used_in_emulation) + u32 func_addr = umax; + + if (name.starts_with("__0x")) { - jit = std::make_shared(s_link_table, g_cfg.core.llvm_cpu); + u32 addr = umax; + auto res = std::from_chars(name.c_str() + 4, name.c_str() + name.size(), addr, 16); + + if (res.ec == std::errc() && res.ptr == name.c_str() + name.size() && addr < bound) + { + func_addr = addr + reloc; + } } - // Copy module information (TODO: optimize) + if (func_addr == umax) + { + return {}; + } + + reader_lock rlock(*shared_mtx); + + if (auto it = func_map->find(func_addr); it != func_map->end()) + { + return it->second; + } + + rlock.upgrade(); + + u64& code_ptr = (*func_map)[func_addr]; + + if (code_ptr) + { + return +code_ptr; + } + + constexpr auto abs_diff = [](u64 a, u64 b) { return a <= b ? b - a : a - b; }; + + auto write_le = [](u8*& code, auto value) + { + write_to_ptr>>(code, value); + code += sizeof(value); + }; + +#if defined(ARCH_X64) + // Try to make the code fit in 16 bytes, may fail and fallback + if (*full_sample && (*full_sample <= s32{smax} || abs_diff(*full_sample, reinterpret_cast(jit_runtime::peek(true))) <= s32{smax})) + { + u8* code = jit_runtime::alloc(16, 8, true); + code_ptr = reinterpret_cast(code); + + // mov edx, func_addr + *code++ = 0xba; + write_le(code, func_addr - seg0); + + const u64 diff_for_jump = abs_diff(reinterpret_cast(code + 5), *full_sample); + + if (diff_for_jump <= s32{smax}) + { + // jmp (rel32) full_sample + *code++ = 0xe9; + + write_le(code, static_cast(*full_sample - reinterpret_cast(code + 4))); + return code_ptr; + } + else if (*full_sample <= s32{smax}) + { + // mov eax, full_sample + *code++ = 0xb8; + + write_le(code, static_cast(*full_sample)); + + // jmp rax + *code++ = 0xff; + *code++ = 0xea; + return code_ptr; + } + else // fallback (requiring more than 16 bytes) + { + // movabs rax, full_sample + // *code++ = 0x48; + // *code++ = 0xb8; + + // write_le(code, *full_sample); + + // // jmp rax + // *code++ = 0xff; + // *code++ = 0xea; + // return code_ptr; + ppu_log.error("JIT symbol trampoline failed."); + } + } +#elif 0 + // Try to make the code fit in 16 bytes, may fail and fallback + if (*full_sample && abs_diff(*full_sample, reinterpret_cast(jit_runtime::peek(true) + 3 * 4)) < (128u << 20)) + { +#ifdef __APPLE__ + pthread_jit_write_protect_np(false); +#endif + u8* code = jit_runtime::alloc(12, 4, true); + code_ptr = reinterpret_cast(code); + + union arm_op + { + u32 op; + bf_t b_target; + bf_t mov_imm16; + }; + + const u64 diff_for_jump = abs_diff(reinterpret_cast(code + 3 * 4), *full_sample); + + if (diff_for_jump < (128u << 20)) + { + // MOVZ w15, func_addr + arm_op mov_pcl{0x5280000F}; + mov_pcl.mov_imm16 = func_addr & 0xffff; + + write_le(code, mov_pcl.op); + + // MOVK w15, func_addr >> 16, LSL #16 + arm_op mov_pch{0x72A0000F}; + mov_pch.mov_imm16 = func_addr >> 16; + + write_le(code, mov_pch.op); + + const s64 branch_offset = (*full_sample - reinterpret_cast(code + 4)); + + // B full_sample + arm_op b_sample{0x14000000}; + b_sample.b_target = static_cast(branch_offset / 4); + + write_le(code, b_sample.op); + return code_ptr; + } + else // fallback + { + ppu_log.error("JIT symbol trampoline failed."); + } + } +#endif + + using namespace asmjit; + + usz code_size_until_jump = umax; + + auto func = build_function_asm(name, [&](native_asm& c, auto& /*args*/) + { +#if defined(ARCH_X64) + c.mov(x86::edx, func_addr - seg0); // Load PC + + const auto buf_start = reinterpret_cast(c.bufferData()); + const auto buf_end = reinterpret_cast(c.bufferPtr()); + + code_size_until_jump = buf_end - buf_start; + + c.add(x86::edx, seg0); + c.mov(x86::rax, x86::qword_ptr(reinterpret_cast(&vm::g_exec_addr))); + c.mov(x86::dword_ptr(x86::rbp, ::offset32(&ppu_thread::cia)), x86::edx); + + c.mov(x86::rax, x86::qword_ptr(x86::rax, x86::rdx, 1, 0)); // Load call target + c.mov(x86::rdx, x86::rax); + c.shl(x86::rax, 16); + c.shr(x86::rax, 16); + c.shr(x86::rdx, 48); + c.shl(x86::edx, 13); + c.mov(x86::r12d, x86::edx); // Load relocation base + c.jmp(x86::rax); +#else + // Load REG_Base - use absolute jump target to bypass rel jmp range limits + // X19 contains vm::g_exec_addr + const arm::GpX exec_addr = a64::x19; + + // X20 contains ppu_thread* + const arm::GpX ppu_t_base = a64::x20; + + // Load PC + const arm::GpX pc = a64::x15; + const arm::GpX cia_addr_reg = a64::x11; + + // Load CIA + c.mov(pc.w(), func_addr); + + const auto buf_start = reinterpret_cast(c.bufferData()); + const auto buf_end = reinterpret_cast(c.bufferPtr()); + + code_size_until_jump = buf_end - buf_start; + + // Load offset value + c.mov(cia_addr_reg, static_cast(::offset32(&ppu_thread::cia))); + + // Update CIA + c.str(pc.w(), arm::Mem(ppu_t_base, cia_addr_reg)); + + // Multiply by 2 to index into ptr table + c.add(pc, pc, pc); + + // Load call target + const arm::GpX call_target = a64::x13; + c.ldr(call_target, arm::Mem(exec_addr, pc)); + + // Compute REG_Hp + const arm::GpX reg_hp = a64::x21; + c.mov(reg_hp, call_target); + c.lsr(reg_hp, reg_hp, 48); + c.lsl(reg_hp.w(), reg_hp.w(), 13); + + // Zero top 16 bits of call target + c.lsl(call_target, call_target, 16); + c.lsr(call_target, call_target, 16); + + // Execute LLE call + c.br(call_target); +#endif + }, runtime.get(), true); + + // Full sample may exist already, but is very far away + // So in this case, a new sample is written + ensure(code_size_until_jump != umax); + *full_sample = reinterpret_cast(func) + code_size_until_jump; + + code_ptr = reinterpret_cast(func); + return code_ptr; + }; + + if (has_mfvscr && g_cfg.core.ppu_set_sat_bit) + { + info.attr += ppu_attr::has_mfvscr; + } + + while (!jit_mod.init && fpos < info.get_funcs().size()) + { + // Copy module information ppu_module part; part.copy_part(info); - part.funcs.reserve(16000); // Overall block size in bytes usz bsize = 0; usz bcount = 0; - while (fpos < info.funcs.size()) + while (fpos < info.get_funcs().size()) { - auto& func = info.funcs[fpos]; + auto& func = info.get_funcs()[fpos]; if (!func.size) { @@ -4767,9 +5054,9 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s { auto far_jump = ensure(g_fxo->get().gen_jump(source)); - if (source == func.addr && jit) + if (source == func.addr) { - jit->update_global_mapping(fmt::format("__0x%x", func.addr - reloc), reinterpret_cast(far_jump)); + (*shared_map)[func.addr] = reinterpret_cast(far_jump); } ppu_register_function_at(source, 4, far_jump); @@ -4783,22 +5070,11 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s } } - // Copy block or function entry - ppu_function& entry = part.funcs.emplace_back(func); + local_jit_bounds->first = std::min(local_jit_bounds->first, func.addr); + local_jit_bounds->second = std::max(local_jit_bounds->second, func.addr + func.size); - // Fixup some information - entry.name = fmt::format("__0x%x", entry.addr - reloc); - - if (has_mfvscr && g_cfg.core.ppu_set_sat_bit) - { - // TODO - entry.attr += ppu_attr::has_mfvscr; - } - - if (entry.blocks.empty()) - { - entry.blocks.emplace(func.addr, func.size); - } + part.local_bounds.first = std::min(part.local_bounds.first, func.addr); + part.local_bounds.second = std::max(part.local_bounds.second, func.addr + func.size); bsize += func.size; @@ -4815,7 +5091,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s int has_dcbz = !!g_cfg.core.accurate_cache_line_stores; - for (const auto& func : part.funcs) + for (const auto& func : part.get_funcs()) { if (func.size == 0) { @@ -4827,7 +5103,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s sha1_update(&ctx, reinterpret_cast(&addr), sizeof(addr)); sha1_update(&ctx, reinterpret_cast(&size), sizeof(size)); - for (const auto& block : func.blocks) + for (const auto block : func) { if (block.second == 0 || reloc) { @@ -4898,7 +5174,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s sha1_update(&ctx, ensure(info.get_ptr(func.addr)), func.size); } - if (!workload.empty() && fpos >= info.funcs.size()) + if (fpos >= info.get_funcs().size() || module_counter % c_moudles_per_jit == c_moudles_per_jit - 1) { // Hash the entire function grouped addresses for the integrity of the symbol resolver function // Potentially occuring during patches @@ -4906,7 +5182,13 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s std::vector> addrs; - for (const ppu_function& func : info.funcs) + constexpr auto compare = [](const ppu_function& a, u32 addr) { return a.addr < addr; }; + + const auto start = std::lower_bound(info.funcs.begin(), info.funcs.end(), local_jit_bounds->first, compare); + + std::span span_range{ start, std::lower_bound(start, info.funcs.end(), local_jit_bounds->second, compare) }; + + for (const ppu_function& func : span_range) { if (func.size == 0) { @@ -4919,7 +5201,13 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s // Hash its size too addrs.emplace_back(::size32(addrs)); - sha1_update(&ctx, reinterpret_cast(addrs.data()), addrs.size() * sizeof(be_t)); + if (module_counter != 0) + { + sha1_update(&ctx, reinterpret_cast(addrs.data()), addrs.size() * sizeof(be_t)); + } + + part.jit_bounds = std::move(local_jit_bounds); + local_jit_bounds = std::make_shared>(u32{umax}, 0); } if (false) @@ -4974,7 +5262,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s settings += ppu_settings::accurate_vnan, settings -= ppu_settings::fixup_vnan, fmt::throw_exception("VNAN Not implemented"); if (g_cfg.core.ppu_use_nj_bit) settings += ppu_settings::accurate_nj_mode, settings -= ppu_settings::fixup_nj_denormals, fmt::throw_exception("NJ Not implemented"); - if (fpos >= info.funcs.size()) + if (fpos >= info.get_funcs().size() || module_counter % c_moudles_per_jit == c_moudles_per_jit - 1) settings += ppu_settings::contains_symbol_resolver; // Avoid invalidating all modules for this purpose // Write version, hash, CPU, settings @@ -4986,23 +5274,20 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s break; } + module_counter++; + if (!check_only) { - total_compile++; - link_workload.emplace_back(obj_name, false); } // Check object file if (jit_compiler::check(cache_path + obj_name)) { - if (!jit && !check_only) + if (!is_being_used_in_emulation && !check_only) { ppu_log.success("LLVM: Module exists: %s", obj_name); - - // Done already, revert total amount increase - // Avoid incrementing "pdone" instead because it creates false appreciation for both the progress dialog and the user - total_compile--; + link_workload.pop_back(); } continue; @@ -5028,12 +5313,6 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s return false; } - // Update progress dialog - if (total_compile) - { - g_progr_ptotal += total_compile; - } - if (g_progr_ftotal_bits && file_size) { g_progr_fknown_bits += file_size; @@ -5042,14 +5321,12 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s // Create worker threads for compilation if (!workload.empty()) { + // Update progress dialog + g_progr_ptotal += ::size32(workload); + *progress_dialog = get_localized_string(localized_string_id::PROGRESS_DIALOG_COMPILING_PPU_MODULES); - u32 thread_count = rpcs3::utils::get_max_threads(); - - if (workload.size() < thread_count) - { - thread_count = ::size32(workload); - } + const u32 thread_count = std::min(::size32(workload), rpcs3::utils::get_max_threads()); struct thread_index_allocator { @@ -5113,11 +5390,26 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s // Keep allocating workload const auto& [obj_name, part] = std::as_const(workload)[i]; + std::shared_lock rlock(g_fxo->get().shared_mtx, std::defer_lock); + std::unique_lock lock(g_fxo->get().shared_mtx, std::defer_lock); + + if (false && part.jit_bounds && part.parent->funcs.size() >= 0x8000) + { + // Make a large symbol-resolving function compile alone because it has massive memory requirements + lock.lock(); + } + else + { + rlock.lock(); + } + ppu_log.warning("LLVM: Compiling module %s%s", cache_path, obj_name); - // Use another JIT instance - jit_compiler jit2({}, g_cfg.core.llvm_cpu, 0x1); - ppu_initialize2(jit2, part, cache_path, obj_name, i == workload.size() - 1 ? main_module : part); + { + // Use another JIT instance + jit_compiler jit2({}, g_cfg.core.llvm_cpu, 0x1); + ppu_initialize2(jit2, part, cache_path, obj_name); + } ppu_log.success("LLVM: Compiled module %s", obj_name); } @@ -5145,6 +5437,22 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s g_watchdog_hold_ctr--; } + // Initialize compiler instance + while (jits.size() < utils::aligned_div(module_counter, c_moudles_per_jit) && is_being_used_in_emulation) + { + jits.emplace_back(std::make_shared(s_link_table, g_cfg.core.llvm_cpu, 0, symbols_cement)); + + for (const auto& [addr, func] : *shared_map) + { + jits.back()->update_global_mapping(fmt::format("__0x%x", addr - reloc), func); + } + } + + if (jit_mod.symbol_resolvers.empty() && is_being_used_in_emulation) + { + jit_mod.symbol_resolvers.resize(jits.size()); + } + bool failed_to_load = false; { if (!is_being_used_in_emulation || (cpu ? cpu->state.all_of(cpu_flag::exit) : Emu.IsStopped())) @@ -5152,39 +5460,45 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s return compiled_new; } - if (workload.size() < link_workload.size()) - { - // Only show this message if this task is relevant - *progress_dialog = get_localized_string(localized_string_id::PROGRESS_DIALOG_LINKING_PPU_MODULES); - } + *progress_dialog = get_localized_string(localized_string_id::PROGRESS_DIALOG_LINKING_PPU_MODULES); + + // Because linking is faster than compiling, consider each module linkages as a single module compilation in time + const bool divide_by_twenty = !workload.empty(); + const usz increment_link_count_at = (divide_by_twenty ? 20 : 1); + + g_progr_ptotal += static_cast(utils::aligned_div(link_workload.size(), increment_link_count_at)); + + usz mod_index = umax; for (const auto& [obj_name, is_compiled] : link_workload) { + mod_index++; + if (cpu ? cpu->state.all_of(cpu_flag::exit) : Emu.IsStopped()) { break; } - if (!failed_to_load && !jit->add(cache_path + obj_name)) + if (!failed_to_load && !jits[mod_index / c_moudles_per_jit]->add(cache_path + obj_name)) { ppu_log.error("LLVM: Failed to load module %s", obj_name); failed_to_load = true; } + if (mod_index % increment_link_count_at == (link_workload.size() - 1) % increment_link_count_at) + { + // Incremenet 'pdone' Nth times where N is link workload size ceil-divided by increment_link_count_at + g_progr_pdone++; + } + if (failed_to_load) { - if (!is_compiled) - { - g_progr_pdone++; - } - continue; } if (!is_compiled) { ppu_log.success("LLVM: Loaded module %s", obj_name); - g_progr_pdone++; } } } @@ -5205,10 +5519,10 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s progress_dialog = get_localized_string(localized_string_id::PROGRESS_DIALOG_APPLYING_PPU_CODE); - if (!jit) + if (jits.empty()) { // No functions - nothing to do - ensure(info.funcs.empty()); + ensure(info.get_funcs().empty()); return compiled_new; } @@ -5216,25 +5530,27 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s if (is_first) { - jit->fin(); - } - - if (is_first) - { - jit_mod.symbol_resolver = reinterpret_cast(jit->get("__resolve_symbols")); - ensure(jit_mod.symbol_resolver); - } - else - { - ensure(jit_mod.symbol_resolver); + for (auto& jit : jits) + { + jit->fin(); + } } #ifdef __APPLE__ // Symbol resolver is in JIT mem, so we must enable execution pthread_jit_write_protect_np(true); #endif + { + usz index = umax; - jit_mod.symbol_resolver(vm::g_exec_addr, info.segs[0].addr); + for (auto& sim : jit_mod.symbol_resolvers) + { + index++; + + sim = ensure(!is_first ? sim : reinterpret_cast(jits[index]->get("__resolve_symbols"))); + sim(vm::g_exec_addr, info.segs[0].addr); + } + } #ifdef __APPLE__ // Symbol resolver is in JIT mem, so we must enable execution @@ -5242,7 +5558,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s #endif // Find a BLR-only function in order to copy it to all BLRs (some games need it) - for (const auto& func : info.funcs) + for (const auto& func : info.get_funcs()) { if (func.size == 4 && *info.get_ptr(func.addr) == ppu_instructions::BLR()) { @@ -5281,7 +5597,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s #endif } -static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name, const ppu_module& whole_module) +static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name) { #ifdef LLVM_AVAILABLE using namespace llvm; @@ -5307,12 +5623,15 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module translator.get_type(), // r2 }, false); + // Difference between function name and current location + const u32 reloc = module_part.is_relocatable ? ::at32(module_part.segs, 0).addr : 0; + // Initialize function list - for (const auto& func : module_part.funcs) + for (const auto& func : module_part.get_funcs()) { if (func.size) { - const auto f = cast(_module->getOrInsertFunction(func.name, _func).getCallee()); + const auto f = cast(_module->getOrInsertFunction(fmt::format("__0x%x", func.addr - reloc), _func).getCallee()); f->setCallingConv(CallingConv::GHC); f->addParamAttr(1, llvm::Attribute::NoAlias); f->addFnAttr(Attribute::NoUnwind); @@ -5325,29 +5644,6 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module translator.build_interpreter(); } -#if LLVM_VERSION_MAJOR < 17 - legacy::FunctionPassManager pm(_module.get()); - - // Basic optimizations - //pm.add(createCFGSimplificationPass()); - //pm.add(createPromoteMemoryToRegisterPass()); - pm.add(createEarlyCSEPass()); - //pm.add(createTailCallEliminationPass()); - //pm.add(createInstructionCombiningPass()); - //pm.add(createBasicAAWrapperPass()); - //pm.add(new MemoryDependenceAnalysis()); - //pm.add(createLICMPass()); - //pm.add(createLoopInstSimplifyPass()); - //pm.add(createNewGVNPass()); - //pm.add(createDeadStoreEliminationPass()); - //pm.add(createSCCPPass()); - //pm.add(createReassociatePass()); - //pm.add(createInstructionCombiningPass()); - //pm.add(createInstructionSimplifierPass()); - //pm.add(createAggressiveDCEPass()); - //pm.add(createCFGSimplificationPass()); - //pm.add(createLintPass()); // Check -#else // Create the analysis managers. // These must be declared in this order so that they are destroyed in the // correct order due to inter-analysis-manager references. @@ -5372,10 +5668,15 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module FunctionPassManager fpm; // Basic optimizations fpm.addPass(EarlyCSEPass()); -#endif + + u32 guest_code_size = 0; + u32 min_addr = umax; + u32 max_addr = 0; + u32 num_func = 0; // Translate functions - for (usz fi = 0, fmax = module_part.funcs.size(); fi < fmax; fi++) + // Start with the lowest bound of the module, function list is sorted + for (const auto& mod_func : module_part.get_funcs()) { if (Emu.IsStopped()) { @@ -5383,18 +5684,19 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module return; } - if (module_part.funcs[fi].size) + if (mod_func.size) { + num_func++; + guest_code_size += mod_func.size; + max_addr = std::max(max_addr, mod_func.addr + mod_func.size); + min_addr = std::min(min_addr, mod_func.addr); + // Translate - if (const auto func = translator.Translate(module_part.funcs[fi])) + if (const auto func = translator.Translate(mod_func)) { #ifdef ARCH_X64 // TODO // Run optimization passes -#if LLVM_VERSION_MAJOR < 17 - pm.run(*func); -#else fpm.run(*func, fam); -#endif #endif // ARCH_X64 } else @@ -5405,18 +5707,14 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module } } - // Run this only in one module for all functions - if (&whole_module != &module_part) + // Run this only in one module for all functions compiled + if (module_part.jit_bounds) { - if (const auto func = translator.GetSymbolResolver(whole_module)) + if (const auto func = translator.GetSymbolResolver(module_part)) { #ifdef ARCH_X64 // TODO // Run optimization passes -#if LLVM_VERSION_MAJOR < 17 - pm.run(*func); -#else fpm.run(*func, fam); -#endif #endif // ARCH_X64 } else @@ -5452,7 +5750,7 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module return; } - ppu_log.notice("LLVM: %zu functions generated", _module->getFunctionList().size()); + ppu_log.notice("LLVM: %zu functions generated (code_size=0x%x, num_func=%d, max_addr(-)min_addr=0x%x)", _module->getFunctionList().size(), guest_code_size, num_func, max_addr - min_addr); } // Load or compile module diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index edbb4f515a..8962cc284d 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -1,4 +1,3 @@ -#include #ifdef LLVM_AVAILABLE #include "Emu/system_config.h" @@ -114,7 +113,7 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* _module, const ppu_mo const auto caddr = m_info.segs[0].addr; const auto cend = caddr + m_info.segs[0].size; - for (const auto& rel : m_info.relocs) + for (const auto& rel : m_info.get_relocs()) { if (rel.addr >= caddr && rel.addr < cend) { @@ -162,7 +161,7 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* _module, const ppu_mo } } - if (!m_info.relocs.empty()) + if (m_info.is_relocatable) { m_reloc = &m_info.segs[0]; } @@ -185,7 +184,12 @@ bool ppu_test_address_may_be_mmio(std::span> insts); Function* PPUTranslator::Translate(const ppu_function& info) { - m_function = m_module->getFunction(info.name); + // Instruction address is (m_addr + base) + const u64 base = m_reloc ? m_reloc->addr : 0; + m_addr = info.addr - base; + m_attr = m_info.attr; + + m_function = m_module->getFunction(fmt::format("__0x%x", m_addr)); std::fill(std::begin(m_globals), std::end(m_globals), nullptr); std::fill(std::begin(m_locals), std::end(m_locals), nullptr); @@ -193,11 +197,6 @@ Function* PPUTranslator::Translate(const ppu_function& info) IRBuilder<> irb(BasicBlock::Create(m_context, "__entry", m_function)); m_ir = &irb; - // Instruction address is (m_addr + base) - const u64 base = m_reloc ? m_reloc->addr : 0; - m_addr = info.addr - base; - m_attr = info.attr; - // Don't emit check in small blocks without terminator bool need_check = info.size >= 16; @@ -325,6 +324,9 @@ Function* PPUTranslator::Translate(const ppu_function& info) Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) { + ensure(m_module->getFunction("__resolve_symbols") == nullptr); + ensure(info.jit_bounds); + m_function = cast(m_module->getOrInsertFunction("__resolve_symbols", FunctionType::get(get_type(), { get_type(), get_type() }, false)).getCallee()); IRBuilder<> irb(BasicBlock::Create(m_context, "__entry", m_function)); @@ -351,12 +353,11 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) // This is made in loop instead of inlined because it took tremendous amount of time to compile. std::vector vec_addrs; - vec_addrs.reserve(info.funcs.size()); // Create an array of function pointers std::vector functions; - for (const auto& f : info.funcs) + for (const auto& f : info.get_funcs(false, true)) { if (!f.size) { @@ -379,7 +380,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) const auto addr_array = new GlobalVariable(*m_module, addr_array_type, false, GlobalValue::PrivateLinkage, ConstantDataArray::get(m_context, vec_addrs)); // Create an array of function pointers - const auto func_table_type = ArrayType::get(ftype->getPointerTo(), info.funcs.size()); + const auto func_table_type = ArrayType::get(ftype->getPointerTo(), functions.size()); const auto init_func_table = ConstantArray::get(func_table_type, functions); const auto func_table = new GlobalVariable(*m_module, func_table_type, false, GlobalVariable::PrivateLinkage, init_func_table); @@ -2770,12 +2771,7 @@ void PPUTranslator::MFOCRF(ppu_opcode_t op) if (op.l11) { // MFOCRF - -#if LLVM_VERSION_MAJOR < 17 - const u64 pos = countLeadingZeros(op.crm) - 24; -#else const u64 pos = countl_zero(op.crm) - 24; -#endif if (pos >= 8 || 0x80u >> pos != op.crm) { @@ -3062,11 +3058,7 @@ void PPUTranslator::MTOCRF(ppu_opcode_t op) if (op.l11) { // MTOCRF -#if LLVM_VERSION_MAJOR < 17 - const u64 pos = countLeadingZeros(op.crm) - 24; -#else const u64 pos = countl_zero(op.crm) - 24; -#endif if (pos >= 8 || 0x80u >> pos != op.crm) { diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index dd621ae088..01041555e1 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -3,7 +3,7 @@ #include "Loader/ELF.h" #include "util/asm.hpp" -#include "Emu/Cell/RawSPUThread.h" +#include "SPUThread.h" inline void try_start(spu_thread& spu) { diff --git a/rpcs3/Emu/Cell/RawSPUThread.h b/rpcs3/Emu/Cell/RawSPUThread.h index 921db8f1f6..6f70f09bee 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.h +++ b/rpcs3/Emu/Cell/RawSPUThread.h @@ -1,3 +1 @@ #pragma once - -#include "SPUThread.h" diff --git a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp index 3a3ef1cda3..3eb75082a2 100644 --- a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp @@ -5,15 +5,12 @@ #include "Emu/IdManager.h" #include "Emu/Cell/timers.hpp" -#include "SPUDisAsm.h" #include "SPUThread.h" #include "SPUInterpreter.h" -#include "PPUAnalyser.h" #include "Crypto/sha1.h" #include "util/asm.hpp" #include "util/v128.hpp" -#include "util/simd.hpp" #include "util/sysinfo.hpp" #include diff --git a/rpcs3/Emu/Cell/SPUAnalyser.h b/rpcs3/Emu/Cell/SPUAnalyser.h index faa85a5a41..103c655a9e 100644 --- a/rpcs3/Emu/Cell/SPUAnalyser.h +++ b/rpcs3/Emu/Cell/SPUAnalyser.h @@ -1,7 +1,5 @@ #pragma once -#include "util/types.hpp" - // SPU Instruction Type struct spu_itype { diff --git a/rpcs3/Emu/Cell/SPUInterpreter.cpp b/rpcs3/Emu/Cell/SPUInterpreter.cpp index 3d69d5f9e1..a781da5e8f 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/SPUInterpreter.cpp @@ -3,7 +3,6 @@ #include "Utilities/JIT.h" #include "SPUThread.h" -#include "Emu/Cell/Common.h" #include "Emu/Cell/SPUAnalyser.h" #include "Emu/system_config.h" @@ -13,7 +12,6 @@ #include "util/sysinfo.hpp" #include -#include #if !defined(_MSC_VER) #pragma GCC diagnostic push diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 4fae99e344..72f589192e 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -43,11 +43,6 @@ const extern spu_decoder g_spu_iflag; #include #include #include -#if LLVM_VERSION_MAJOR < 17 -#include -#include -#include -#else #include #include #include @@ -58,7 +53,6 @@ const extern spu_decoder g_spu_iflag; #include #include #include -#endif #ifdef _MSC_VER #pragma warning(pop) #else @@ -1658,7 +1652,7 @@ public: u32 elements; u32 dwords; - if (m_use_avx512 && g_cfg.core.full_width_avx512) + if (m_use_avx512) { stride = 64; elements = 16; @@ -1681,96 +1675,202 @@ public: llvm::Value* starta_pc = m_ir->CreateAnd(get_pc(starta), 0x3fffc); llvm::Value* data_addr = m_ir->CreateGEP(get_type(), m_lsptr, starta_pc); - llvm::Value* acc = nullptr; + llvm::Value* acc0 = nullptr; + llvm::Value* acc1 = nullptr; + bool toggle = true; - for (u32 j = starta; j < end; j += stride) + // Use a 512bit simple checksum to verify integrity if size is atleast 512b * 3 + // This code uses a 512bit vector for all hardware to ensure behavior matches. + // The checksum path is still faster even on narrow hardware. + if ((end - starta) >= 192 && !g_cfg.core.precise_spu_verification) { - int indices[16]; - bool holes = false; - bool data = false; - - for (u32 i = 0; i < elements; i++) + for (u32 j = starta; j < end; j += 64) { - const u32 k = j + i * 4; + int indices[16]; + bool holes = false; + bool data = false; - if (k < start || k >= end || !func.data[(k - start) / 4]) + for (u32 i = 0; i < 16; i++) { - indices[i] = elements; - holes = true; + const u32 k = j + i * 4; + + if (k < start || k >= end || !func.data[(k - start) / 4]) + { + indices[i] = 16; + holes = true; + } + else + { + indices[i] = i; + data = true; + } + } + + if (!data) + { + // Skip full-sized holes + continue; + } + + llvm::Value* vls = nullptr; + + // Load unaligned code block from LS + vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); + + // Mask if necessary + if (holes) + { + vls = m_ir->CreateShuffleVector(vls, ConstantAggregateZero::get(vls->getType()), llvm::ArrayRef(indices, 16)); + } + + // Interleave accumulators for more performance + if (toggle) + { + acc0 = acc0 ? m_ir->CreateAdd(acc0, vls) : vls; } else { - indices[i] = i; - data = true; + acc1 = acc1 ? m_ir->CreateAdd(acc1, vls) : vls; + } + toggle = !toggle; + check_iterations++; + } + + llvm::Value* acc = (acc0 && acc1) ? m_ir->CreateAdd(acc0, acc1): (acc0 ? acc0 : acc1); + + // Create the checksum + u32 checksum[16] = {0}; + + for (u32 j = 0; j < func.data.size(); j += 16) // Process 16 elements per iteration + { + for (u32 i = 0; i < 16; i++) + { + if (j + i < func.data.size()) + { + checksum[i] += func.data[j + i]; + } } } - if (!data) - { - // Skip full-sized holes - continue; - } + auto* const_vector = ConstantDataVector::get(m_context, llvm::ArrayRef(checksum, 16)); + acc = m_ir->CreateXor(acc, const_vector); - llvm::Value* vls = nullptr; - - // Load unaligned code block from LS - if (m_use_avx512 && g_cfg.core.full_width_avx512) - { - vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); - } - else if (m_use_avx) - { - vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); - } - else - { - vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); - } - - // Mask if necessary - if (holes) - { - vls = m_ir->CreateShuffleVector(vls, ConstantAggregateZero::get(vls->getType()), llvm::ArrayRef(indices, elements)); - } - - // Perform bitwise comparison and accumulate - u32 words[16]; - - for (u32 i = 0; i < elements; i++) - { - const u32 k = j + i * 4; - words[i] = k >= start && k < end ? func.data[(k - start) / 4] : 0; - } - - vls = m_ir->CreateXor(vls, ConstantDataVector::get(m_context, llvm::ArrayRef(words, elements))); - acc = acc ? m_ir->CreateOr(acc, vls) : vls; - check_iterations++; - } - - // Pattern for PTEST - if (m_use_avx512 && g_cfg.core.full_width_avx512) - { + // Pattern for PTEST acc = m_ir->CreateBitCast(acc, get_type()); - } - else if (m_use_avx) - { - acc = m_ir->CreateBitCast(acc, get_type()); + + llvm::Value* elem = m_ir->CreateExtractElement(acc, u64{0}); + + for (u32 i = 1; i < 8; i++) + { + elem = m_ir->CreateOr(elem, m_ir->CreateExtractElement(acc, i)); + } + + // Compare result with zero + const auto cond = m_ir->CreateICmpNE(elem, m_ir->getInt64(0)); + m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely); } else { - acc = m_ir->CreateBitCast(acc, get_type()); + for (u32 j = starta; j < end; j += stride) + { + int indices[16]; + bool holes = false; + bool data = false; + + for (u32 i = 0; i < elements; i++) + { + const u32 k = j + i * 4; + + if (k < start || k >= end || !func.data[(k - start) / 4]) + { + indices[i] = elements; + holes = true; + } + else + { + indices[i] = i; + data = true; + } + } + + if (!data) + { + // Skip full-sized holes + continue; + } + + llvm::Value* vls = nullptr; + + // Load unaligned code block from LS + if (m_use_avx512) + { + vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); + } + else if (m_use_avx) + { + vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); + } + else + { + vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); + } + + // Mask if necessary + if (holes) + { + vls = m_ir->CreateShuffleVector(vls, ConstantAggregateZero::get(vls->getType()), llvm::ArrayRef(indices, elements)); + } + + // Perform bitwise comparison and accumulate + u32 words[16]; + + for (u32 i = 0; i < elements; i++) + { + const u32 k = j + i * 4; + words[i] = k >= start && k < end ? func.data[(k - start) / 4] : 0; + } + + vls = m_ir->CreateXor(vls, ConstantDataVector::get(m_context, llvm::ArrayRef(words, elements))); + + // Interleave accumulators for more performance + if (toggle) + { + acc0 = acc0 ? m_ir->CreateAdd(acc0, vls) : vls; + } + else + { + acc1 = acc1 ? m_ir->CreateAdd(acc1, vls) : vls; + } + toggle = !toggle; + check_iterations++; + } + llvm::Value* acc = (acc0 && acc1) ? m_ir->CreateAdd(acc0, acc1): (acc0 ? acc0 : acc1); + + // Pattern for PTEST + if (m_use_avx512) + { + acc = m_ir->CreateBitCast(acc, get_type()); + } + else if (m_use_avx) + { + acc = m_ir->CreateBitCast(acc, get_type()); + } + else + { + acc = m_ir->CreateBitCast(acc, get_type()); + } + + llvm::Value* elem = m_ir->CreateExtractElement(acc, u64{0}); + + for (u32 i = 1; i < dwords; i++) + { + elem = m_ir->CreateOr(elem, m_ir->CreateExtractElement(acc, i)); + } + + // Compare result with zero + const auto cond = m_ir->CreateICmpNE(elem, m_ir->getInt64(0)); + m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely); } - - llvm::Value* elem = m_ir->CreateExtractElement(acc, u64{0}); - - for (u32 i = 1; i < dwords; i++) - { - elem = m_ir->CreateOr(elem, m_ir->CreateExtractElement(acc, i)); - } - - // Compare result with zero - const auto cond = m_ir->CreateICmpNE(elem, m_ir->getInt64(0)); - m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely); } // Increase block counter with statistics @@ -2567,21 +2667,6 @@ public: m_function_table->eraseFromParent(); } -#if LLVM_VERSION_MAJOR < 17 - // Initialize pass manager - legacy::FunctionPassManager pm(_module.get()); - - // Basic optimizations - pm.add(createEarlyCSEPass()); - pm.add(createCFGSimplificationPass()); - //pm.add(createNewGVNPass()); - pm.add(createDeadStoreEliminationPass()); - pm.add(createLICMPass()); - pm.add(createAggressiveDCEPass()); - pm.add(createDeadCodeEliminationPass()); - //pm.add(createLintPass()); // Check -#else - // Create the analysis managers. // These must be declared in this order so that they are destroyed in the // correct order due to inter-analysis-manager references. @@ -2610,7 +2695,6 @@ public: fpm.addPass(DSEPass()); fpm.addPass(createFunctionToLoopPassAdaptor(LICMPass(LICMOptions()), true)); fpm.addPass(ADCEPass()); -#endif for (auto& f : *m_module) { @@ -2620,11 +2704,7 @@ public: for (const auto& func : m_functions) { const auto f = func.second.fn ? func.second.fn : func.second.chunk; -#if LLVM_VERSION_MAJOR < 17 - pm.run(*f); -#else fpm.run(*f, fam); -#endif } // Clear context (TODO) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 54679dee41..4248fb84f1 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -8,6 +8,7 @@ #include "Loader/ELF.h" #include "Emu/VFS.h" #include "Emu/IdManager.h" +#include "Emu/System.h" #include "Emu/perf_meter.hpp" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/ErrorCodes.h" @@ -23,7 +24,6 @@ #include "Emu/Cell/timers.hpp" #include "Emu/RSX/Core/RSXReservationLock.hpp" -#include "Emu/RSX/RSXThread.h" #include #include diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 9c45e9efe5..37cf1cf074 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -10,6 +10,8 @@ #include "util/logs.hpp" #include "util/to_endian.hpp" +#include "Utilities/mutex.h" + #include "Loader/ELF.h" #include diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 6349bd60c6..e5c00fab9d 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -9,7 +9,6 @@ #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/SPUThread.h" #include "Emu/Cell/ErrorCodes.h" -#include "Emu/Cell/MFC.h" #include "sys_sync.h" #include "sys_lwmutex.h" #include "sys_lwcond.h" @@ -56,7 +55,7 @@ #include #include #include -#include +#include #include "util/tsc.hpp" #include "util/sysinfo.hpp" #include "util/init_mutex.hpp" diff --git a/rpcs3/Emu/Cell/lv2/sys_bdemu.cpp b/rpcs3/Emu/Cell/lv2/sys_bdemu.cpp index 4dfc6eb3fa..51d8e48842 100644 --- a/rpcs3/Emu/Cell/lv2/sys_bdemu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_bdemu.cpp @@ -1,5 +1,4 @@ #include "stdafx.h" -#include "Emu/Memory/vm.h" #include "Emu/Cell/ErrorCodes.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_cond.cpp b/rpcs3/Emu/Cell/lv2/sys_cond.cpp index 4387d27306..401ed3bd66 100644 --- a/rpcs3/Emu/Cell/lv2/sys_cond.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_cond.cpp @@ -2,7 +2,6 @@ #include "util/serialization.hpp" #include "Emu/IdManager.h" -#include "Emu/IPC.h" #include "Emu/System.h" #include "Emu/Cell/ErrorCodes.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_config.cpp b/rpcs3/Emu/Cell/lv2/sys_config.cpp index a6ccb74106..499a87dbf4 100644 --- a/rpcs3/Emu/Cell/lv2/sys_config.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_config.cpp @@ -1,6 +1,4 @@ #include "stdafx.h" -#include "Emu/System.h" -#include "Emu/Memory/vm.h" #include "Emu/IdManager.h" #include "Emu/Cell/lv2/sys_event.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_config.h b/rpcs3/Emu/Cell/lv2/sys_config.h index ea9b9da76d..0804671141 100644 --- a/rpcs3/Emu/Cell/lv2/sys_config.h +++ b/rpcs3/Emu/Cell/lv2/sys_config.h @@ -1,11 +1,8 @@ #pragma once -#include -#include - #include "util/atomic.hpp" #include "util/shared_ptr.hpp" - +#include "Emu/Cell/timers.hpp" /* * sys_config is a "subscription-based data storage API" diff --git a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp index 971408e042..c28efaf711 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp @@ -2,13 +2,10 @@ #include "sys_event_flag.h" #include "Emu/IdManager.h" -#include "Emu/IPC.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" -#include - #include "util/asm.hpp" LOG_CHANNEL(sys_event_flag); diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index 3cb8e8eb2f..1f76bb7090 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -2,8 +2,8 @@ #include "sys_sync.h" #include "sys_fs.h" #include "sys_memory.h" +#include "util/asm.hpp" -#include "Emu/Cell/PPUModule.h" #include "Emu/Cell/PPUThread.h" #include "Crypto/unedat.h" #include "Emu/System.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.h b/rpcs3/Emu/Cell/lv2/sys_fs.h index 825140d7ab..e64a2b4edb 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.h +++ b/rpcs3/Emu/Cell/lv2/sys_fs.h @@ -6,7 +6,6 @@ #include "Utilities/StrUtil.h" #include -#include // Open Flags enum : s32 diff --git a/rpcs3/Emu/Cell/lv2/sys_game.cpp b/rpcs3/Emu/Cell/lv2/sys_game.cpp index 6e69b9175f..1164dbd216 100644 --- a/rpcs3/Emu/Cell/lv2/sys_game.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_game.cpp @@ -10,6 +10,7 @@ #include "Utilities/StrUtil.h" #include "Utilities/Thread.h" +#include "Emu/Cell/timers.hpp" #include "sys_game.h" LOG_CHANNEL(sys_game); diff --git a/rpcs3/Emu/Cell/lv2/sys_hid.cpp b/rpcs3/Emu/Cell/lv2/sys_hid.cpp index e5b5a310aa..05f87895ad 100644 --- a/rpcs3/Emu/Cell/lv2/sys_hid.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_hid.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "sys_hid.h" -#include "Emu/Memory/vm.h" #include "Emu/Memory/vm_var.h" #include "Emu/Cell/PPUThread.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.cpp b/rpcs3/Emu/Cell/lv2/sys_memory.cpp index 6cfc3a1860..9f726e994d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_memory.cpp @@ -7,7 +7,6 @@ #include "Emu/Cell/SPUThread.h" #include "Emu/IdManager.h" -#include "util/vm.hpp" #include "util/asm.hpp" LOG_CHANNEL(sys_memory); diff --git a/rpcs3/Emu/Cell/lv2/sys_mutex.cpp b/rpcs3/Emu/Cell/lv2/sys_mutex.cpp index f2d23291ff..92bf877a8a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_mutex.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_mutex.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "Emu/IdManager.h" -#include "Emu/IPC.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index aae2454619..827a4c98f2 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -15,7 +15,6 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" #endif -#include #include #include #include @@ -24,7 +23,6 @@ #include #include #include -#include #include #ifdef __clang__ #pragma GCC diagnostic pop @@ -33,9 +31,7 @@ #include "Emu/NP/np_handler.h" #include "Emu/NP/np_helpers.h" -#include "Emu/NP/np_dnshook.h" - -#include +#include "Emu/Cell/timers.hpp" #include #include "sys_net/network_context.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp index 7bdd18b3fd..0d2a598214 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp @@ -35,7 +35,7 @@ std::size_t lv2_socket::get_queue_size() const } socket_type lv2_socket::get_socket() const { - return socket; + return native_socket; } #ifdef _WIN32 diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h index f0e3b61d00..314840f245 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h @@ -6,6 +6,7 @@ #include "Utilities/mutex.h" #include "Emu/IdManager.h" #include "Emu/Cell/lv2/sys_net.h" +#include "Emu/NP/ip_address.h" #ifdef _WIN32 #include @@ -21,12 +22,6 @@ #endif #endif -#ifdef _WIN32 -using socket_type = uptr; -#else -using socket_type = int; -#endif - enum class thread_state : u32; class lv2_socket @@ -126,7 +121,7 @@ protected: shared_mutex mutex; s32 lv2_id = 0; - socket_type socket = 0; + socket_type native_socket = 0; lv2_socket_family family{}; lv2_socket_type type{}; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp index d0a735b178..d420f23cc8 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp @@ -87,9 +87,9 @@ s32 lv2_socket_native::create_socket() return CELL_OK; } -void lv2_socket_native::set_socket(socket_type socket, lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol) +void lv2_socket_native::set_socket(socket_type native_socket, lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol) { - this->socket = socket; + this->native_socket = native_socket; this->family = family; this->type = type; this->protocol = protocol; @@ -115,12 +115,12 @@ std::tuple, sys_net_sockaddr> lv2_socket_nativ sys_net.error("Calling socket::accept() from a previously connected socket!"); } - socket_type native_socket = ::accept(socket, reinterpret_cast(&native_addr), &native_addrlen); + socket_type client_socket = ::accept(native_socket, reinterpret_cast(&native_addr), &native_addrlen); - if (native_socket != invalid_socket) + if (client_socket != invalid_socket) { auto newsock = make_single(family, type, protocol); - newsock->set_socket(native_socket, family, type, protocol); + newsock->set_socket(client_socket, family, type, protocol); // Sockets inherit non blocking behaviour from their parent newsock->so_nbio = so_nbio; @@ -173,7 +173,7 @@ s32 lv2_socket_native::bind(const sys_net_sockaddr& addr) sys_net.warning("[Native] Trying to bind %s:%d", native_addr.sin_addr, std::bit_cast, u16>(native_addr.sin_port)); - if (::bind(socket, reinterpret_cast(&native_addr), native_addr_len) == 0) + if (::bind(native_socket, reinterpret_cast(&native_addr), native_addr_len) == 0) { // Only UPNP port forward binds to 0.0.0.0 if (saddr == 0) @@ -182,7 +182,7 @@ s32 lv2_socket_native::bind(const sys_net_sockaddr& addr) { sockaddr_in client_addr; socklen_t client_addr_size = sizeof(client_addr); - ensure(::getsockname(socket, reinterpret_cast(&client_addr), &client_addr_size) == 0); + ensure(::getsockname(native_socket, reinterpret_cast(&client_addr), &client_addr_size) == 0); bound_port = std::bit_cast>(client_addr.sin_port); } else @@ -245,7 +245,7 @@ std::optional lv2_socket_native::connect(const sys_net_sockaddr& addr) return -SYS_NET_EALREADY; } - if (::connect(socket, reinterpret_cast(&native_addr), native_addr_len) == 0) + if (::connect(native_socket, reinterpret_cast(&native_addr), native_addr_len) == 0) { return CELL_OK; } @@ -272,7 +272,7 @@ std::optional lv2_socket_native::connect(const sys_net_sockaddr& addr) { int native_error; ::socklen_t size = sizeof(native_error); - if (::getsockopt(socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&native_error), &size) != 0 || size != sizeof(int)) + if (::getsockopt(native_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&native_error), &size) != 0 || size != sizeof(int)) { so_error = 1; } @@ -304,7 +304,7 @@ s32 lv2_socket_native::connect_followup() { int native_error; ::socklen_t size = sizeof(native_error); - if (::getsockopt(socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&native_error), &size) != 0 || size != sizeof(int)) + if (::getsockopt(native_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&native_error), &size) != 0 || size != sizeof(int)) { return -1; } @@ -320,7 +320,7 @@ std::pair lv2_socket_native::getpeername() ::sockaddr_storage native_addr; ::socklen_t native_addrlen = sizeof(native_addr); - if (::getpeername(socket, reinterpret_cast(&native_addr), &native_addrlen) == 0) + if (::getpeername(native_socket, reinterpret_cast(&native_addr), &native_addrlen) == 0) { ensure(native_addr.ss_family == AF_INET); @@ -339,7 +339,7 @@ std::pair lv2_socket_native::getsockname() ::sockaddr_storage native_addr; ::socklen_t native_addrlen = sizeof(native_addr); - if (::getsockname(socket, reinterpret_cast(&native_addr), &native_addrlen) == 0) + if (::getsockname(native_socket, reinterpret_cast(&native_addr), &native_addrlen) == 0) { ensure(native_addr.ss_family == AF_INET); @@ -581,7 +581,7 @@ std::tuple lv2_socket_native::getsockopt(s32 return {-SYS_NET_EINVAL, {}, {}}; } - if (::getsockopt(socket, native_level, native_opt, native_val.ch, &native_len) != 0) + if (::getsockopt(native_socket, native_level, native_opt, native_val.ch, &native_len) != 0) { return {-get_last_error(false), {}, {}}; } @@ -864,7 +864,7 @@ s32 lv2_socket_native::setsockopt(s32 level, s32 optname, const std::vector& return -SYS_NET_EINVAL; } - if (::setsockopt(socket, native_level, native_opt, static_cast(native_val), native_len) == 0) + if (::setsockopt(native_socket, native_level, native_opt, static_cast(native_val), native_len) == 0) { return {}; } @@ -876,7 +876,7 @@ s32 lv2_socket_native::listen(s32 backlog) { std::lock_guard lock(mutex); - if (::listen(socket, backlog) == 0) + if (::listen(native_socket, backlog) == 0) { return CELL_OK; } @@ -930,7 +930,7 @@ std::optional, sys_net_sockaddr>> lv2_socket_nat native_flags |= MSG_WAITALL; } - auto native_result = ::recvfrom(socket, reinterpret_cast(res_buf.data()), len, native_flags, reinterpret_cast(&native_addr), &native_addrlen); + auto native_result = ::recvfrom(native_socket, reinterpret_cast(res_buf.data()), len, native_flags, reinterpret_cast(&native_addr), &native_addrlen); if (native_result >= 0) { @@ -1021,7 +1021,7 @@ std::optional lv2_socket_native::sendto(s32 flags, const std::vector& b } } - native_result = ::sendto(socket, reinterpret_cast(buf.data()), ::narrow(buf.size()), native_flags, native_addr ? reinterpret_cast(&native_addr.value()) : nullptr, native_addr ? sizeof(sockaddr_in) : 0); + native_result = ::sendto(native_socket, reinterpret_cast(buf.data()), ::narrow(buf.size()), native_flags, native_addr ? reinterpret_cast(&native_addr.value()) : nullptr, native_addr ? sizeof(sockaddr_in) : 0); if (native_result >= 0) { @@ -1075,7 +1075,7 @@ std::optional lv2_socket_native::sendmsg(s32 flags, const sys_net_msghdr& m const u32 len = msg.msg_iov[i].iov_len; const std::vector buf_copy(vm::_ptr(iov_base.addr()), vm::_ptr(iov_base.addr()) + len); - native_result = ::send(socket, reinterpret_cast(buf_copy.data()), ::narrow(buf_copy.size()), native_flags); + native_result = ::send(native_socket, reinterpret_cast(buf_copy.data()), ::narrow(buf_copy.size()), native_flags); if (native_result >= 0) { @@ -1096,15 +1096,9 @@ std::optional lv2_socket_native::sendmsg(s32 flags, const sys_net_msghdr& m void lv2_socket_native::close() { std::lock_guard lock(mutex); - if (socket) - { -#ifdef _WIN32 - ::closesocket(socket); -#else - ::close(socket); -#endif - socket = {}; - } + + np::close_socket(native_socket); + native_socket = {}; if (auto dnshook = g_fxo->try_get()) { @@ -1141,7 +1135,7 @@ s32 lv2_socket_native::shutdown(s32 how) SHUT_RDWR; #endif - if (::shutdown(socket, native_how) == 0) + if (::shutdown(native_socket, native_how) == 0) { return CELL_OK; } @@ -1163,7 +1157,7 @@ s32 lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) sys_net.warning("sys_net_bnet_poll(fd=%d): events=0x%x", sn_pfd.fd, sn_pfd.events); } - native_pfd.fd = socket; + native_pfd.fd = native_socket; if (sn_pfd.events & SYS_NET_POLLIN) { @@ -1179,7 +1173,7 @@ s32 lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) std::tuple lv2_socket_native::select(bs_t selected, pollfd& native_pfd) { - native_pfd.fd = socket; + native_pfd.fd = native_socket; if (selected & lv2_socket::poll_t::read) { native_pfd.events |= POLLIN; @@ -1196,12 +1190,12 @@ void lv2_socket_native::set_default_buffers() { // Those are the default PS3 values u32 default_RCVBUF = (type == SYS_NET_SOCK_STREAM) ? 65535 : 9216; - if (::setsockopt(socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&default_RCVBUF), sizeof(default_RCVBUF)) != 0) + if (::setsockopt(native_socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&default_RCVBUF), sizeof(default_RCVBUF)) != 0) { sys_net.error("Error setting default SO_RCVBUF on sys_net_bnet_socket socket"); } u32 default_SNDBUF = 131072; - if (::setsockopt(socket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&default_SNDBUF), sizeof(default_SNDBUF)) != 0) + if (::setsockopt(native_socket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&default_SNDBUF), sizeof(default_SNDBUF)) != 0) { sys_net.error("Error setting default SO_SNDBUF on sys_net_bnet_socket socket"); } @@ -1212,12 +1206,7 @@ void lv2_socket_native::set_non_blocking() // Set non-blocking // This is done to avoid having threads stuck on blocking socket functions // Blocking functions just put the thread to sleep and delegate the waking up to network_thread which polls the sockets -#ifdef _WIN32 - u_long _true = 1; - ::ioctlsocket(socket, FIONBIO, &_true); -#else - ::fcntl(socket, F_SETFL, ::fcntl(socket, F_GETFL, 0) | O_NONBLOCK); -#endif + np::set_socket_non_blocking(native_socket); } bool lv2_socket_native::is_socket_connected() @@ -1232,7 +1221,7 @@ bool lv2_socket_native::is_socket_connected() int listening = 0; socklen_t len = sizeof(listening); - if (::getsockopt(socket, SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast(&listening), &len) == -1) + if (::getsockopt(native_socket, SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast(&listening), &len) == -1) { return false; } @@ -1248,8 +1237,8 @@ bool lv2_socket_native::is_socket_connected() FD_ZERO(&readfds); FD_ZERO(&writefds); - FD_SET(socket, &readfds); - FD_SET(socket, &writefds); + FD_SET(native_socket, &readfds); + FD_SET(native_socket, &writefds); // Use select to check for readability and writability const int result = ::select(1, &readfds, &writefds, NULL, &timeout); @@ -1261,5 +1250,5 @@ bool lv2_socket_native::is_socket_connected() } // Socket is connected if it's readable or writable - return FD_ISSET(socket, &readfds) || FD_ISSET(socket, &writefds); + return FD_ISSET(native_socket, &readfds) || FD_ISSET(native_socket, &writefds); } diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h index 808529356a..cf07dfcb76 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h @@ -9,7 +9,6 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" #endif -#include #include #include #include @@ -18,7 +17,6 @@ #include #include #include -#include #include #ifdef __clang__ #pragma GCC diagnostic pop @@ -62,7 +60,7 @@ public: s32 shutdown(s32 how) override; private: - void set_socket(socket_type socket, lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol); + void set_socket(socket_type native_socket, lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol); void set_default_buffers(); void set_non_blocking(); diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp index 8614d37205..7acb1273d6 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp @@ -164,7 +164,7 @@ s32 lv2_socket_p2p::bind(const sys_net_sockaddr& addr) std::lock_guard lock(mutex); port = p2p_port; vport = p2p_vport; - socket = real_socket; + native_socket = real_socket; bound_addr = psa_in_p2p->sin_addr; } @@ -176,7 +176,7 @@ std::pair lv2_socket_p2p::getsockname() std::lock_guard lock(mutex); // Unbound socket - if (!socket) + if (!native_socket) { return {CELL_OK, {}}; } @@ -299,7 +299,7 @@ std::optional lv2_socket_p2p::sendto(s32 flags, const std::vector& buf, native_flags |= MSG_WAITALL; } - auto native_result = ::sendto(socket, reinterpret_cast(p2p_data.data()), ::size32(p2p_data), native_flags, reinterpret_cast(&native_addr), sizeof(native_addr)); + auto native_result = np::sendto_possibly_ipv6(native_socket, reinterpret_cast(p2p_data.data()), ::size32(p2p_data), &native_addr, native_flags); if (native_result >= 0) { diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp index d6c98b670a..3996eae82d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "Utilities/Thread.h" -#include "util/asm.hpp" #include "util/atomic.hpp" #include "lv2_socket_p2ps.h" #include "Emu/NP/np_helpers.h" @@ -275,10 +274,10 @@ lv2_socket_p2ps::lv2_socket_p2ps(lv2_socket_family family, lv2_socket_type type, sockopts[(static_cast(SYS_NET_SOL_SOCKET) << 32ull) | SYS_NET_SO_TYPE] = cache_type; } -lv2_socket_p2ps::lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq, s32 so_nbio) +lv2_socket_p2ps::lv2_socket_p2ps(socket_type native_socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq, s32 so_nbio) : lv2_socket_p2p(SYS_NET_AF_INET, SYS_NET_SOCK_STREAM_P2P, SYS_NET_IPPROTO_IP) { - this->socket = socket; + this->native_socket = native_socket; this->port = port; this->vport = vport; this->op_addr = op_addr; @@ -467,7 +466,7 @@ bool lv2_socket_p2ps::handle_listening(p2ps_encapsulated_tcp* tcp_header, [[mayb const u16 new_op_vport = tcp_header->src_port; const u64 new_cur_seq = send_hdr.seq + 1; const u64 new_data_beg_seq = send_hdr.ack; - auto sock_lv2 = make_shared(socket, port, vport, new_op_addr, new_op_port, new_op_vport, new_cur_seq, new_data_beg_seq, so_nbio); + auto sock_lv2 = make_shared(native_socket, port, vport, new_op_addr, new_op_port, new_op_vport, new_cur_seq, new_data_beg_seq, so_nbio); const s32 new_sock_id = idm::import_existing(sock_lv2); sock_lv2->set_lv2_id(new_sock_id); const u64 key_connected = (reinterpret_cast(op_addr)->sin_addr.s_addr) | (static_cast(tcp_header->src_port) << 48) | (static_cast(tcp_header->dst_port) << 32); @@ -518,8 +517,9 @@ void lv2_socket_p2ps::send_u2s_packet(std::vector data, const ::sockaddr_in* { char ip_str[16]; inet_ntop(AF_INET, &dst->sin_addr, ip_str, sizeof(ip_str)); - sys_net.trace("[P2PS] Sending U2S packet on socket %d(id:%d): data(%d, seq %d, require_ack %d) to %s:%d", socket, lv2_id, data.size(), seq, require_ack, ip_str, std::bit_cast>(dst->sin_port)); - while (::sendto(socket, reinterpret_cast(data.data()), ::size32(data), 0, reinterpret_cast(dst), sizeof(sockaddr_in)) == -1) + sys_net.trace("[P2PS] Sending U2S packet on socket %d(id:%d): data(%d, seq %d, require_ack %d) to %s:%d", native_socket, lv2_id, data.size(), seq, require_ack, ip_str, std::bit_cast>(dst->sin_port)); + + while (np::sendto_possibly_ipv6(native_socket, reinterpret_cast(data.data()), ::size32(data), dst, 0) == -1) { const sys_net_error err = get_last_error(false); // concurrency on the socket can result in EAGAIN error in which case we try again @@ -707,7 +707,7 @@ s32 lv2_socket_p2ps::bind(const sys_net_sockaddr& addr) port = p2p_port; vport = p2p_vport; - socket = real_socket; + native_socket = real_socket; bound_addr = psa_in_p2p->sin_addr; } } @@ -720,7 +720,7 @@ std::pair lv2_socket_p2ps::getsockname() std::lock_guard lock(mutex); // Unbound socket - if (!socket) + if (!native_socket) { return {CELL_OK, {}}; } @@ -783,7 +783,7 @@ std::optional lv2_socket_p2ps::connect(const sys_net_sockaddr& addr) } } - socket = real_socket; + native_socket = real_socket; send_hdr.src_port = vport; send_hdr.dst_port = dst_vport; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp index 84e90e53fe..61bd5a4045 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp @@ -1,35 +1,73 @@ +#include "Emu/NP/ip_address.h" #include "stdafx.h" #include "Emu/Cell/lv2/sys_sync.h" #include "Emu/Cell/Modules/sceNp.h" // for SCE_NP_PORT #include "network_context.h" -#include "Emu/system_config.h" #include "sys_net_helpers.h" LOG_CHANNEL(sys_net); // Used by RPCN to send signaling packets to RPCN server(for UDP hole punching) -s32 send_packet_from_p2p_port(const std::vector& data, const sockaddr_in& addr) +bool send_packet_from_p2p_port_ipv4(const std::vector& data, const sockaddr_in& addr) { - s32 res{}; auto& nc = g_fxo->get(); { std::lock_guard list_lock(nc.list_p2p_ports_mutex); if (nc.list_p2p_ports.contains(SCE_NP_PORT)) { auto& def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT); - res = ::sendto(def_port.p2p_socket, reinterpret_cast(data.data()), ::size32(data), 0, reinterpret_cast(&addr), sizeof(sockaddr_in)); - if (res == -1) - sys_net.error("Failed to send signaling packet: %s", get_last_error(false, false)); + if (def_port.is_ipv6) + { + const auto addr6 = np::sockaddr_to_sockaddr6(addr); + + if (::sendto(def_port.p2p_socket, reinterpret_cast(data.data()), ::size32(data), 0, reinterpret_cast(&addr6), sizeof(sockaddr_in6)) == -1) + { + sys_net.error("Failed to send IPv4 signaling packet on IPv6 socket: %s", get_last_error(false, false)); + return false; + } + } + else if (::sendto(def_port.p2p_socket, reinterpret_cast(data.data()), ::size32(data), 0, reinterpret_cast(&addr), sizeof(sockaddr_in)) == -1) + { + sys_net.error("Failed to send signaling packet on IPv4 socket: %s", get_last_error(false, false)); + return false; + } } else { - sys_net.error("send_packet_from_p2p_port: port %d not present", +SCE_NP_PORT); + sys_net.error("send_packet_from_p2p_port_ipv4: port %d not present", +SCE_NP_PORT); + return false; } } - return res; + return true; +} + +bool send_packet_from_p2p_port_ipv6(const std::vector& data, const sockaddr_in6& addr) +{ + auto& nc = g_fxo->get(); + { + std::lock_guard list_lock(nc.list_p2p_ports_mutex); + if (nc.list_p2p_ports.contains(SCE_NP_PORT)) + { + auto& def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT); + ensure(def_port.is_ipv6); + + if (::sendto(def_port.p2p_socket, reinterpret_cast(data.data()), ::size32(data), 0, reinterpret_cast(&addr), sizeof(sockaddr_in6)) == -1) + { + sys_net.error("Failed to send signaling packet on IPv6 socket: %s", get_last_error(false, false)); + return false; + } + } + else + { + sys_net.error("send_packet_from_p2p_port_ipv6: port %d not present", +SCE_NP_PORT); + return false; + } + } + + return true; } std::vector> get_rpcn_msgs() @@ -241,13 +279,18 @@ void p2p_thread::operator()() auto num_p2p_sockets = 0; std::memset(p2p_fd.data(), 0, p2p_fd.size() * sizeof(::pollfd)); { - std::lock_guard lock(list_p2p_ports_mutex); - for (const auto& p2p_port : list_p2p_ports) + auto set_fd = [&](socket_type socket) { p2p_fd[num_p2p_sockets].events = POLLIN; p2p_fd[num_p2p_sockets].revents = 0; - p2p_fd[num_p2p_sockets].fd = p2p_port.second.p2p_socket; + p2p_fd[num_p2p_sockets].fd = socket; num_p2p_sockets++; + }; + + std::lock_guard lock(list_p2p_ports_mutex); + for (const auto& [_, p2p_port] : list_p2p_ports) + { + set_fd(p2p_port.p2p_socket); } } @@ -260,14 +303,20 @@ void p2p_thread::operator()() { std::lock_guard lock(list_p2p_ports_mutex); auto fd_index = 0; - for (auto& p2p_port : list_p2p_ports) + + auto process_fd = [&](nt_p2p_port& p2p_port) { if ((p2p_fd[fd_index].revents & POLLIN) == POLLIN || (p2p_fd[fd_index].revents & POLLRDNORM) == POLLRDNORM) { - while (p2p_port.second.recv_data()) + while (p2p_port.recv_data()) ; } fd_index++; + }; + + for (auto& [_, p2p_port] : list_p2p_ports) + { + process_fd(p2p_port); } wake_threads(); diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp index 574d670978..5f48d273dd 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp @@ -1,11 +1,7 @@ #include "stdafx.h" - -#include - +#include "Emu/NP/ip_address.h" #include "nt_p2p_port.h" -#include "lv2_socket_native.h" #include "lv2_socket_p2ps.h" -#include "util/asm.hpp" #include "sys_net_helpers.h" #include "Emu/NP/signaling_handler.h" #include "sys_net_helpers.h" @@ -44,9 +40,10 @@ namespace sys_net_helpers nt_p2p_port::nt_p2p_port(u16 port) : port(port) { - // Creates and bind P2P Socket - p2p_socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + is_ipv6 = np::is_ipv6_supported(); + // Creates and bind P2P Socket + p2p_socket = is_ipv6 ? ::socket(AF_INET6, SOCK_DGRAM, 0) : ::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); #ifdef _WIN32 if (p2p_socket == INVALID_SOCKET) #else @@ -54,22 +51,30 @@ nt_p2p_port::nt_p2p_port(u16 port) #endif fmt::throw_exception("Failed to create DGRAM socket for P2P socket: %s!", get_last_error(true)); -#ifdef _WIN32 - u_long _true = 1; - ::ioctlsocket(p2p_socket, FIONBIO, &_true); -#else - ::fcntl(p2p_socket, F_SETFL, ::fcntl(p2p_socket, F_GETFL, 0) | O_NONBLOCK); -#endif + np::set_socket_non_blocking(p2p_socket); u32 optval = 131072; // value obtained from DECR for a SOCK_DGRAM_P2P socket(should maybe be bigger for actual socket?) if (setsockopt(p2p_socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&optval), sizeof(optval)) != 0) fmt::throw_exception("Error setsockopt SO_RCVBUF on P2P socket: %s", get_last_error(true)); - ::sockaddr_in p2p_saddr{}; - p2p_saddr.sin_family = AF_INET; - p2p_saddr.sin_port = std::bit_cast>(port); // htons(port); - p2p_saddr.sin_addr.s_addr = 0; // binds to 0.0.0.0 - const auto ret_bind = ::bind(p2p_socket, reinterpret_cast(&p2p_saddr), sizeof(p2p_saddr)); + int ret_bind = 0; + const u16 be_port = std::bit_cast>(port); + + if (is_ipv6) + { + // Some OS(Windows, maybe more) will only support IPv6 adressing by default and we need IPv4 over IPv6 + optval = 0; + if (setsockopt(p2p_socket, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&optval), sizeof(optval)) != 0) + fmt::throw_exception("Error setsockopt IPV6_V6ONLY on P2P socket: %s", get_last_error(true)); + + ::sockaddr_in6 p2p_ipv6_addr{.sin6_family = AF_INET6, .sin6_port = be_port}; + ret_bind = ::bind(p2p_socket, reinterpret_cast(&p2p_ipv6_addr), sizeof(p2p_ipv6_addr)); + } + else + { + ::sockaddr_in p2p_ipv4_addr{.sin_family = AF_INET, .sin_port = be_port}; + ret_bind = ::bind(p2p_socket, reinterpret_cast(&p2p_ipv4_addr), sizeof(p2p_ipv4_addr)); + } if (ret_bind == -1) fmt::throw_exception("Failed to bind DGRAM socket to %d for P2P: %s!", port, get_last_error(true)); @@ -82,14 +87,7 @@ nt_p2p_port::nt_p2p_port(u16 port) nt_p2p_port::~nt_p2p_port() { - if (p2p_socket) - { -#ifdef _WIN32 - ::closesocket(p2p_socket); -#else - ::close(p2p_socket); -#endif - } + np::close_socket(p2p_socket); } void nt_p2p_port::dump_packet(p2ps_encapsulated_tcp* tcph) @@ -153,7 +151,7 @@ bool nt_p2p_port::recv_data() { auto lerr = get_last_error(false); if (lerr != SYS_NET_EINPROGRESS && lerr != SYS_NET_EWOULDBLOCK) - sys_net.error("Error recvfrom on P2P socket: %d", lerr); + sys_net.error("Error recvfrom on %s P2P socket: %d", is_ipv6 ? "IPv6" : "IPv4", lerr); return false; } @@ -166,6 +164,14 @@ bool nt_p2p_port::recv_data() u16 dst_vport = reinterpret_cast&>(p2p_recv_data[0]); + if (is_ipv6) + { + const auto* addr_ipv6 = reinterpret_cast(&native_addr); + const auto addr_ipv4 = np::sockaddr6_to_sockaddr(*addr_ipv6); + native_addr = {}; + std::memcpy(&native_addr, &addr_ipv4, sizeof(addr_ipv4)); + } + if (dst_vport == 0) { if (recv_res < VPORT_0_HEADER_SIZE) @@ -339,7 +345,7 @@ bool nt_p2p_port::recv_data() send_hdr.flags = p2ps_tcp_flags::RST; auto packet = generate_u2s_packet(send_hdr, nullptr, 0); - if (::sendto(p2p_socket, reinterpret_cast(packet.data()), ::size32(packet), 0, reinterpret_cast(&native_addr), sizeof(sockaddr_in)) == -1) + if (np::sendto_possibly_ipv6(p2p_socket, reinterpret_cast(packet.data()), ::size32(packet), reinterpret_cast(&native_addr), 0) == -1) { sys_net.error("[P2PS] Error sending RST to sender to unbound P2PS: %s", get_last_error(false)); return true; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h index 2d57bc8461..3ffc3df54d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h @@ -48,6 +48,8 @@ struct nt_p2p_port socket_type p2p_socket = 0; u16 port = 0; + bool is_ipv6 = false; + shared_mutex bound_p2p_vports_mutex; // For DGRAM_P2P sockets (vport, sock_ids) std::map> bound_p2p_vports{}; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp b/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp index 0fabc61cae..acd4bf6a1f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp @@ -1,11 +1,8 @@ #include "stdafx.h" - #include "Emu/IdManager.h" #include "Emu/Cell/PPUThread.h" - #include "lv2_socket.h" #include "sys_net_helpers.h" - #include "network_context.h" LOG_CHANNEL(sys_net); @@ -32,13 +29,13 @@ sys_net_error convert_error(bool is_blocking, int native_error, [[maybe_unused]] #define ERROR_CASE(error) \ case WSA##error: \ result = SYS_NET_##error; \ - name = #error; \ + name = #error; \ break; #else #define ERROR_CASE(error) \ case error: \ result = SYS_NET_##error; \ - name = #error; \ + name = #error; \ break; #endif switch (native_error) @@ -86,11 +83,11 @@ sys_net_error convert_error(bool is_blocking, int native_error, [[maybe_unused]] ERROR_CASE(EHOSTDOWN); ERROR_CASE(EHOSTUNREACH); #ifdef _WIN32 - // Windows likes to be special with unique errors - case WSAENETRESET: - result = SYS_NET_ECONNRESET; - name = "WSAENETRESET"; - break; + // Windows likes to be special with unique errors + case WSAENETRESET: + result = SYS_NET_ECONNRESET; + name = "WSAENETRESET"; + break; #endif default: fmt::throw_exception("sys_net get_last_error(is_blocking=%d, native_error=%d): Unknown/illegal socket error", is_blocking, native_error); @@ -137,11 +134,11 @@ sys_net_sockaddr native_addr_to_sys_net_addr(const ::sockaddr_storage& native_ad sys_net_sockaddr_in* paddr = reinterpret_cast(&sn_addr); - paddr->sin_len = sizeof(sys_net_sockaddr_in); + paddr->sin_len = sizeof(sys_net_sockaddr_in); paddr->sin_family = SYS_NET_AF_INET; - paddr->sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); - paddr->sin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); - paddr->sin_zero = 0; + paddr->sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); + paddr->sin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); + paddr->sin_zero = 0; return sn_addr; } @@ -153,8 +150,8 @@ sys_net_sockaddr native_addr_to_sys_net_addr(const ::sockaddr_storage& native_ad const sys_net_sockaddr_in* psa_in = reinterpret_cast(&sn_addr); ::sockaddr_in native_addr{}; - native_addr.sin_family = AF_INET; - native_addr.sin_port = std::bit_cast(psa_in->sin_port); + native_addr.sin_family = AF_INET; + native_addr.sin_port = std::bit_cast(psa_in->sin_port); native_addr.sin_addr.s_addr = std::bit_cast(psa_in->sin_addr); #ifdef _WIN32 @@ -189,9 +186,9 @@ u32 network_clear_queue(ppu_thread& ppu) u32 cleared = 0; idm::select([&](u32, lv2_socket& sock) - { - cleared += sock.clear_queue(&ppu); - }); + { + cleared += sock.clear_queue(&ppu); + }); return cleared; } @@ -204,6 +201,7 @@ void clear_ppu_to_awake(ppu_thread& ppu) #ifdef _WIN32 // Workaround function for WSAPoll not reporting failed connections +// Note that this was fixed in Windows 10 version 2004 (after more than 10 years lol) void windows_poll(std::vector& fds, unsigned long nfds, int timeout, std::vector& connecting) { ensure(fds.size() >= nfds); @@ -237,12 +235,12 @@ void windows_poll(std::vector& fds, unsigned long nfds, int timeout, std { if (!fds[i].revents) { - int error = 0; + int error = 0; socklen_t intlen = sizeof(error); if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &intlen) == -1 || error != 0) { // Connection silently failed - connecting[i] = false; + connecting[i] = false; fds[i].revents = POLLERR | POLLHUP | (fds[i].events & (POLLIN | POLLOUT)); } } diff --git a/rpcs3/Emu/Cell/lv2/sys_overlay.h b/rpcs3/Emu/Cell/lv2/sys_overlay.h index 1c950a4d0c..1a204ba9eb 100644 --- a/rpcs3/Emu/Cell/lv2/sys_overlay.h +++ b/rpcs3/Emu/Cell/lv2/sys_overlay.h @@ -3,7 +3,6 @@ #include "Emu/Cell/PPUAnalyser.h" #include "Emu/Memory/vm_ptr.h" #include "sys_sync.h" -#include struct lv2_overlay final : ppu_module { diff --git a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp index 80e91bbd0d..1aa9409f46 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp @@ -3,7 +3,6 @@ #include "Emu/System.h" #include "Emu/IdManager.h" -#include "Emu/perf_meter.hpp" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" @@ -17,6 +16,8 @@ #include "util/asm.hpp" +#include + LOG_CHANNEL(sys_ppu_thread); // Simple structure to cleanup previous thread, because can't remove its own thread diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index 896dd0b00c..87176ae035 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "sys_rsx.h" +#include "Emu/System.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/timers.hpp" diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.h b/rpcs3/Emu/Cell/lv2/sys_rsx.h index c681a2dcb2..5188453c5b 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.h +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.h @@ -1,6 +1,5 @@ #pragma once -#include "Utilities/mutex.h" #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/ErrorCodes.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp b/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp index 5f06415ac7..9fa3a890a8 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp @@ -4,10 +4,8 @@ #include "Emu/System.h" #include "Emu/system_config.h" #include "Emu//Audio/audio_utils.h" -#include "Emu//Cell/Modules/cellAudioOut.h" #include "util/video_provider.h" -#include "sys_process.h" #include "sys_rsxaudio.h" #include @@ -825,7 +823,7 @@ void rsxaudio_data_thread::extract_audio_data() return rsxaudio_obj_ptr; }(); - if (Emu.IsPaused() || !rsxaudio_obj) + if (Emu.IsPausedOrReady() || !rsxaudio_obj) { advance_all_timers(); return; @@ -1533,7 +1531,7 @@ void rsxaudio_backend_thread::operator()() backend_failed = false; } - if (!Emu.IsPaused() || !use_aux_ringbuf) // Don't pause if thread is in direct mode + if (!Emu.IsPausedOrReady() || !use_aux_ringbuf) // Don't pause if thread is in direct mode { if (!backend_playing()) { diff --git a/rpcs3/Emu/Cell/lv2/sys_rwlock.cpp b/rpcs3/Emu/Cell/lv2/sys_rwlock.cpp index 173fe68a79..c2abd40284 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rwlock.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rwlock.cpp @@ -2,7 +2,6 @@ #include "sys_rwlock.h" #include "Emu/IdManager.h" -#include "Emu/IPC.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_semaphore.cpp b/rpcs3/Emu/Cell/lv2/sys_semaphore.cpp index 02e40522e2..7440cf2def 100644 --- a/rpcs3/Emu/Cell/lv2/sys_semaphore.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_semaphore.cpp @@ -2,7 +2,6 @@ #include "sys_semaphore.h" #include "Emu/IdManager.h" -#include "Emu/IPC.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_sm.cpp b/rpcs3/Emu/Cell/lv2/sys_sm.cpp index 5700be9aee..50a9b1c679 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sm.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_sm.cpp @@ -1,5 +1,4 @@ #include "stdafx.h" -#include "Emu/Memory/vm.h" #include "Emu/System.h" #include "Emu/Cell/ErrorCodes.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 61a5ee80d1..2c6bd6f3ad 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -2,8 +2,6 @@ #include "sys_spu.h" #include "Emu/System.h" -#include "Emu/system_config.h" -#include "Emu/VFS.h" #include "Emu/IdManager.h" #include "Crypto/unself.h" #include "Crypto/unedat.h" @@ -13,7 +11,6 @@ #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" -#include "Emu/Cell/RawSPUThread.h" #include "Emu/Cell/timers.hpp" #include "Emu/Memory/vm_reservation.h" #include "sys_interrupt.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_ss.cpp b/rpcs3/Emu/Cell/lv2/sys_ss.cpp index 67f4e70148..7eb36d4e19 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ss.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_ss.cpp @@ -3,7 +3,6 @@ #include "sys_process.h" #include "Emu/IdManager.h" -#include "Emu/Cell/PPUThread.h" #include "Emu/Cell/timers.hpp" #include "Emu/system_config.h" #include "util/sysinfo.hpp" diff --git a/rpcs3/Emu/Cell/lv2/sys_storage.cpp b/rpcs3/Emu/Cell/lv2/sys_storage.cpp index d83a5b2d39..d5ee3bbdae 100644 --- a/rpcs3/Emu/Cell/lv2/sys_storage.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_storage.cpp @@ -1,5 +1,4 @@ #include "stdafx.h" -#include "Emu/Memory/vm.h" #include "Emu/IdManager.h" #include "Emu/Cell/ErrorCodes.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index bd6004dfaa..bf99bcb056 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -1,19 +1,14 @@ #pragma once #include "Utilities/mutex.h" -#include "Utilities/sema.h" #include "Emu/CPU/CPUThread.h" #include "Emu/Cell/ErrorCodes.h" -#include "Emu/Cell/timers.hpp" -#include "Emu/Memory/vm_reservation.h" #include "Emu/IdManager.h" #include "Emu/IPC.h" #include "util/shared_ptr.hpp" -#include - // attr_protocol (waiting scheduling policy) enum lv2_protocol : u8 { diff --git a/rpcs3/Emu/Cell/lv2/sys_time.cpp b/rpcs3/Emu/Cell/lv2/sys_time.cpp index 92ac650f8d..1413304a8b 100644 --- a/rpcs3/Emu/Cell/lv2/sys_time.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_time.cpp @@ -5,13 +5,17 @@ #include "Emu/system_config.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/timers.hpp" +#include "util/tsc.hpp" -#include "util/asm.hpp" #include "util/sysinfo.hpp" static u64 timebase_offset; static u64 systemtime_offset; +#ifndef __linux__ +#include "util/asm.hpp" +#endif + #ifdef _WIN32 #include @@ -147,7 +151,7 @@ u64 convert_to_timebased_time(u64 time) u64 get_timebased_time() { - if (0) if (u64 freq = utils::get_tsc_freq()) + if (u64 freq = utils::get_tsc_freq()) { const u64 tsc = utils::get_tsc(); @@ -207,7 +211,7 @@ void initialize_timebased_time(u64 timebased_init, bool reset) // Returns some relative time in microseconds, don't change this fact u64 get_system_time() { - if (0) if (u64 freq = utils::get_tsc_freq()) + if (u64 freq = utils::get_tsc_freq()) { const u64 tsc = utils::get_tsc(); diff --git a/rpcs3/Emu/Cell/lv2/sys_timer.cpp b/rpcs3/Emu/Cell/lv2/sys_timer.cpp index b4b3b780f2..5e6c3d20cb 100644 --- a/rpcs3/Emu/Cell/lv2/sys_timer.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_timer.cpp @@ -135,7 +135,7 @@ void lv2_timer_thread::operator()() sleep_time = umax; - if (Emu.IsPaused()) + if (Emu.IsPausedOrReady()) { sleep_time = 10000; continue; diff --git a/rpcs3/Emu/Cell/lv2/sys_timer.h b/rpcs3/Emu/Cell/lv2/sys_timer.h index 14e1f31f8d..8a0b2eda82 100644 --- a/rpcs3/Emu/Cell/lv2/sys_timer.h +++ b/rpcs3/Emu/Cell/lv2/sys_timer.h @@ -1,8 +1,6 @@ #pragma once #include "sys_event.h" - -#include "Utilities/Thread.h" #include "Emu/Memory/vm_ptr.h" diff --git a/rpcs3/Emu/Cell/lv2/sys_uart.cpp b/rpcs3/Emu/Cell/lv2/sys_uart.cpp index 3bf5f6a0c0..67e91b97a0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_uart.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_uart.cpp @@ -1968,7 +1968,7 @@ void vuart_av_thread::operator()() { while (thread_ctrl::state() != thread_state::aborting) { - if (Emu.IsPaused()) + if (Emu.IsPausedOrReady()) { thread_ctrl::wait_for(5000); continue; diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp index 8e0678b510..67fde17d83 100644 --- a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp @@ -6,12 +6,12 @@ #include #include "Emu/System.h" #include "Emu/system_config.h" -#include "Emu/Memory/vm.h" #include "Emu/IdManager.h" #include "Emu/vfs_config.h" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/ErrorCodes.h" +#include "Emu/Cell/timers.hpp" #include "Emu/Io/usb_device.h" #include "Emu/Io/usb_vfs.h" @@ -73,6 +73,19 @@ struct UsbPipe u8 endpoint = 0; }; +struct usb_allow_list_entry +{ + u16 id_vendor; + u16 id_product_min; + u16 id_product_max; + std::string_view device_name; + u16(*max_device_count)(void); + std::shared_ptr(*make_instance)(u32, const std::array&); + auto operator<(const usb_allow_list_entry& r) const + { + return std::tuple(id_vendor, id_product_min, id_product_max, device_name, max_device_count, make_instance) < std::tuple(r.id_vendor, r.id_product_min, r.id_product_max, device_name, max_device_count, make_instance); + } +}; class usb_handler_thread { public: @@ -132,6 +145,8 @@ public: shared_mutex mutex_sq; ppu_thread* sq{}; + atomic_t usb_hotplug_timeout = umax; + static constexpr auto thread_name = "Usb Manager Thread"sv; private: @@ -140,6 +155,7 @@ private: u32 get_free_transfer_id(); void send_message(u32 message, u32 tr_id); + void perform_scan(); private: // Counters for device IDs, transfer IDs and pipe IDs @@ -150,6 +166,101 @@ private: // List of device drivers std::unordered_map> ldds; + const std::vector device_allow_list + { + // Portals + {0x1430, 0x0150, 0x0150, "Skylanders Portal", &usb_device_skylander::get_num_emu_devices, &usb_device_skylander::make_instance}, + {0x0E6F, 0x0129, 0x0129, "Disney Infinity Base", &usb_device_infinity::get_num_emu_devices, &usb_device_infinity::make_instance}, + {0x0E6F, 0x0241, 0x0241, "Lego Dimensions Portal", &usb_device_dimensions::get_num_emu_devices, &usb_device_dimensions::make_instance}, + {0x0E6F, 0x200A, 0x200A, "Kamen Rider Summonride Portal", nullptr, nullptr}, + + // Cameras + // {0x1415, 0x0020, 0x2000, "Sony Playstation Eye", nullptr, nullptr}, // TODO: verifiy + + // Music devices + {0x1415, 0x0000, 0x0000, "Singstar Microphone", nullptr, nullptr}, + // {0x1415, 0x0020, 0x0020, "SingStar Microphone Wireless", nullptr, nullptr}, // TODO: verifiy + + {0x12BA, 0x00FF, 0x00FF, "Rocksmith Guitar Adapter", nullptr, nullptr}, + {0x12BA, 0x0100, 0x0100, "Guitar Hero Guitar", nullptr, nullptr}, + {0x12BA, 0x0120, 0x0120, "Guitar Hero Drums", nullptr, nullptr}, + {0x12BA, 0x074B, 0x074B, "Guitar Hero Live Guitar", &usb_device_ghltar::get_num_emu_devices, &usb_device_ghltar::make_instance}, + + {0x12BA, 0x0140, 0x0140, "DJ Hero Turntable", &usb_device_turntable::get_num_emu_devices, &usb_device_turntable::make_instance}, + {0x12BA, 0x0200, 0x020F, "Harmonix Guitar", nullptr, nullptr}, + {0x12BA, 0x0210, 0x021F, "Harmonix Drums", nullptr, nullptr}, + {0x12BA, 0x2330, 0x233F, "Harmonix Keyboard", nullptr, nullptr}, + {0x12BA, 0x2430, 0x243F, "Harmonix Button Guitar", nullptr, nullptr}, + {0x12BA, 0x2530, 0x253F, "Harmonix Real Guitar", nullptr, nullptr}, + + {0x1BAD, 0x0004, 0x0004, "Harmonix RB1 Guitar - Wii", nullptr, nullptr}, + {0x1BAD, 0x0005, 0x0005, "Harmonix RB1 Drums - Wii", nullptr, nullptr}, + {0x1BAD, 0x3010, 0x301F, "Harmonix RB2 Guitar - Wii", nullptr, nullptr}, + {0x1BAD, 0x3110, 0x313F, "Harmonix RB2 Drums - Wii", nullptr, nullptr}, + {0x1BAD, 0x3330, 0x333F, "Harmonix Keyboard - Wii", nullptr, nullptr}, + {0x1BAD, 0x3430, 0x343F, "Harmonix Button Guitar - Wii", nullptr, nullptr}, + {0x1BAD, 0x3530, 0x353F, "Harmonix Real Guitar - Wii", nullptr, nullptr}, + + //Top Shot Elite controllers + {0x12BA, 0x04A0, 0x04A0, "Top Shot Elite", nullptr, nullptr}, + {0x12BA, 0x04A1, 0x04A1, "Top Shot Fearmaster", nullptr, nullptr}, + {0x12BA, 0x04B0, 0x04B0, "Rapala Fishing Rod", nullptr, nullptr}, + + + // GT5 Wheels&co + {0x046D, 0xC283, 0xC29B, "lgFF_c283_c29b", nullptr, nullptr}, + {0x044F, 0xB653, 0xB653, "Thrustmaster RGT FFB Pro", nullptr, nullptr}, + {0x044F, 0xB65A, 0xB65A, "Thrustmaster F430", nullptr, nullptr}, + {0x044F, 0xB65D, 0xB65D, "Thrustmaster FFB", nullptr, nullptr}, + {0x044F, 0xB65E, 0xB65E, "Thrustmaster TRS", nullptr, nullptr}, + {0x044F, 0xB660, 0xB660, "Thrustmaster T500 RS Gear Shift", nullptr, nullptr}, + + // GT6 + {0x2833, 0x0001, 0x0001, "Oculus", nullptr, nullptr}, + {0x046D, 0xCA03, 0xCA03, "lgFF_ca03_ca03", nullptr, nullptr}, + + // Buzz controllers + {0x054C, 0x1000, 0x1040, "buzzer0", &usb_device_buzz::get_num_emu_devices, &usb_device_buzz::make_instance}, + {0x054C, 0x0001, 0x0041, "buzzer1", nullptr, nullptr}, + {0x054C, 0x0042, 0x0042, "buzzer2", nullptr, nullptr}, + {0x046D, 0xC220, 0xC220, "buzzer9", nullptr, nullptr}, + + // GCon3 Gun + {0x0B9A, 0x0800, 0x0800, "guncon3", nullptr, nullptr}, + + // uDraw GameTablet + {0x20D6, 0xCB17, 0xCB17, "uDraw GameTablet", nullptr, nullptr}, + + // DVB-T + {0x1415, 0x0003, 0x0003, "PlayTV SCEH-0036", nullptr, nullptr}, + + // PSP Devices + {0x054C, 0x01C8, 0x01C8, "PSP Type A", nullptr, nullptr}, + {0x054C, 0x01C9, 0x01C9, "PSP Type B", nullptr, nullptr}, + {0x054C, 0x01CA, 0x01CA, "PSP Type C", nullptr, nullptr}, + {0x054C, 0x01CB, 0x01CB, "PSP Type D", nullptr, nullptr}, + {0x054C, 0x02D2, 0x02D2, "PSP Slim", nullptr, nullptr}, + + // 0x0900: "H050 USJ(C) PCB rev00", 0x0910: "USIO PCB rev00" + {0x0B9A, 0x0900, 0x0910, "PS3A-USJ", &usb_device_usio::get_num_emu_devices, &usb_device_usio::make_instance}, + + // Densha de GO! controller + {0x0AE4, 0x0004, 0x0004, "Densha de GO! Type 2 Controller", nullptr, nullptr}, + + // EA Active 2 dongle for connecting wristbands & legband + {0x21A4, 0xAC27, 0xAC27, "EA Active 2 Dongle", nullptr, nullptr}, + + // Tony Hawk RIDE Skateboard + {0x12BA, 0x0400, 0x0400, "Tony Hawk RIDE Skateboard Controller", nullptr, nullptr}, + + // PSP in UsbPspCm mode + {0x054C, 0x01CB, 0x01CB, "UsbPspcm", nullptr, nullptr}, + + // Sony Stereo Headsets + {0x12BA, 0x0032, 0x0032, "Wireless Stereo Headset", nullptr, nullptr}, + {0x12BA, 0x0042, 0x0042, "Wireless Stereo Headset", nullptr, nullptr}, + }; + // List of pipes std::map open_pipes; // Transfers infos @@ -163,8 +274,15 @@ private: // List of devices "connected" to the ps3 std::array location{}; std::vector> usb_devices; + std::unordered_map> usb_passthrough_devices; libusb_context* ctx = nullptr; + +#if LIBUSB_API_VERSION >= 0x01000102 + libusb_hotplug_callback_handle callback_handle {}; +#endif + + bool hotplug_supported = false; }; void LIBUSB_CALL callback_transfer(struct libusb_transfer* transfer) @@ -177,6 +295,14 @@ void LIBUSB_CALL callback_transfer(struct libusb_transfer* transfer) usbh.transfer_complete(transfer); } +#if LIBUSB_API_VERSION >= 0x01000102 +static int LIBUSB_CALL hotplug_callback(libusb_context* /*ctx*/, libusb_device * /*dev*/, libusb_hotplug_event event, void * /*user_data*/) +{ + handle_hotplug_event(event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); + return 0; +} +#endif + #if LIBUSB_API_VERSION >= 0x0100010A static void LIBUSB_CALL log_cb(libusb_context* /*ctx*/, enum libusb_log_level level, const char* str) { @@ -205,6 +331,93 @@ static void LIBUSB_CALL log_cb(libusb_context* /*ctx*/, enum libusb_log_level le } #endif +void usb_handler_thread::perform_scan() +{ + // look if any device which we could be interested in is actually connected + libusb_device** list = nullptr; + const ssize_t ndev = libusb_get_device_list(ctx, &list); + std::set seen_usb_devices; + + if (ndev < 0) + { + sys_usbd.error("Failed to get device list: %s", libusb_error_name(static_cast(ndev))); + return; + } + + for (ssize_t index = 0; index < ndev; index++) + { + libusb_device* dev = list[index]; + libusb_device_descriptor desc; + if (int res = libusb_get_device_descriptor(dev, &desc); res < 0) + { + sys_usbd.error("Failed to get device descriptor: %s", libusb_error_name(res)); + continue; + } + + const u8 port = libusb_get_port_number(dev); + const u8 address = libusb_get_device_address(dev); + const u64 usb_id = (static_cast(desc.idVendor) << 48) | (static_cast(desc.idProduct) << 32) | (static_cast(port) << 8) | address; + + seen_usb_devices.insert(usb_id); + if (usb_passthrough_devices.contains(usb_id)) + { + continue; + } + + for (const auto& entry : device_allow_list) + { + // attach + if (desc.idVendor == entry.id_vendor + && desc.idProduct >= entry.id_product_min + && desc.idProduct <= entry.id_product_max) + { + sys_usbd.success("Found device: %s", std::basic_string(entry.device_name)); + libusb_ref_device(dev); + std::shared_ptr usb_dev = std::make_shared(dev, desc, get_new_location()); + connect_usb_device(usb_dev, true); + usb_passthrough_devices[usb_id] = usb_dev; + } + } + + if (desc.idVendor == 0x1209 && desc.idProduct == 0x2882) + { + sys_usbd.success("Found device: Santroller"); + // Send the device a specific control transfer so that it jumps to a RPCS3 compatible mode + libusb_device_handle* lusb_handle; + if (libusb_open(dev, &lusb_handle) == LIBUSB_SUCCESS) + { +#ifdef __linux__ + libusb_set_auto_detach_kernel_driver(lusb_handle, true); + libusb_claim_interface(lusb_handle, 2); +#endif + libusb_control_transfer(lusb_handle, +LIBUSB_ENDPOINT_IN | +LIBUSB_REQUEST_TYPE_CLASS | +LIBUSB_RECIPIENT_INTERFACE, 0x01, 0x03f2, 2, nullptr, 0, 5000); + libusb_close(lusb_handle); + } + else + { + sys_usbd.error("Unable to open Santroller device, make sure Santroller isn't open in the background."); + } + } + } + + for (auto it = usb_passthrough_devices.begin(); it != usb_passthrough_devices.end();) + { + auto& dev = *it; + // If a device is no longer visible, disconnect it + if (seen_usb_devices.contains(dev.first)) + { + ++it; + } + else + { + disconnect_usb_device(dev.second, true); + it = usb_passthrough_devices.erase(it); + } + + } + libusb_free_device_list(list, 1); +} + usb_handler_thread::usb_handler_thread() { #if LIBUSB_API_VERSION >= 0x0100010A @@ -230,164 +443,98 @@ usb_handler_thread::usb_handler_thread() return; } +#ifdef _WIN32 + hotplug_supported = true; +#elif LIBUSB_API_VERSION >= 0x01000102 + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) + { + if (int res = libusb_hotplug_register_callback(ctx, static_cast(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), static_cast(0), LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, + LIBUSB_HOTPLUG_MATCH_ANY, static_cast(hotplug_callback), nullptr, + &callback_handle); res < 0) + { + sys_usbd.error("Failed to initialize sys_usbd hotplug: %s", libusb_error_name(res)); + } + else + { + hotplug_supported = true; + } + } +#endif + for (u32 index = 0; index < MAX_SYS_USBD_TRANSFERS; index++) { transfers[index].transfer = libusb_alloc_transfer(8); transfers[index].transfer_id = index; } - // look if any device which we could be interested in is actually connected - libusb_device** list = nullptr; - ssize_t ndev = libusb_get_device_list(ctx, &list); - - if (ndev < 0) + if (!g_cfg_usio.load()) { - sys_usbd.error("Failed to get device list: %s", libusb_error_name(static_cast(ndev))); - return; + sys_usbd.notice("Could not load usio config. Using defaults."); } - bool found_skylander = false; - bool found_infinity = false; - bool found_dimension = false; - bool found_usj = false; + sys_usbd.notice("USIO config=\n", g_cfg_usio.to_string()); - for (ssize_t index = 0; index < ndev; index++) + if (g_cfg.io.ghltar == ghltar_handler::one_controller || g_cfg.io.ghltar == ghltar_handler::two_controllers) { - libusb_device_descriptor desc; - if (int res = libusb_get_device_descriptor(list[index], &desc); res < 0) + if (!g_cfg_ghltar.load()) { - sys_usbd.error("Failed to get device descriptor: %s", libusb_error_name(res)); - continue; + sys_usbd.notice("Could not load ghltar config. Using defaults."); } - auto check_device = [&](const u16 id_vendor, const u16 id_product_min, const u16 id_product_max, const char* s_name) -> bool - { - if (desc.idVendor == id_vendor && desc.idProduct >= id_product_min && desc.idProduct <= id_product_max) - { - sys_usbd.success("Found device: %s", s_name); - libusb_ref_device(list[index]); - std::shared_ptr usb_dev = std::make_shared(list[index], desc, get_new_location()); - usb_devices.push_back(usb_dev); - return true; - } - return false; - }; - - // Portals - if (check_device(0x1430, 0x0150, 0x0150, "Skylanders Portal")) - { - found_skylander = true; - } - - if (check_device(0x0E6F, 0x0129, 0x0129, "Disney Infinity Base")) - { - found_infinity = true; - } - - if (check_device(0x0E6F, 0x0241, 0x0241, "Lego Dimensions Portal")) - { - found_dimension = true; - } - - check_device(0x0E6F, 0x200A, 0x200A, "Kamen Rider Summonride Portal"); - - // Cameras - // check_device(0x1415, 0x0020, 0x2000, "Sony Playstation Eye"); // TODO: verifiy - - // Music devices - check_device(0x1415, 0x0000, 0x0000, "SingStar Microphone"); - // check_device(0x1415, 0x0020, 0x0020, "SingStar Microphone Wireless"); // TODO: verifiy - check_device(0x12BA, 0x0100, 0x0100, "Guitar Hero Guitar"); - check_device(0x12BA, 0x0120, 0x0120, "Guitar Hero Drums"); - check_device(0x12BA, 0x074B, 0x074B, "Guitar Hero Live Guitar"); - - check_device(0x12BA, 0x0140, 0x0140, "DJ Hero Turntable"); - check_device(0x12BA, 0x0200, 0x020F, "Harmonix Guitar"); - check_device(0x12BA, 0x0210, 0x021F, "Harmonix Drums"); - check_device(0x12BA, 0x2330, 0x233F, "Harmonix Keyboard"); - check_device(0x12BA, 0x2430, 0x243F, "Harmonix Button Guitar"); - check_device(0x12BA, 0x2530, 0x253F, "Harmonix Real Guitar"); - - check_device(0x1BAD, 0x0004, 0x0004, "Harmonix RB1 Guitar - Wii"); - check_device(0x1BAD, 0x0005, 0x0005, "Harmonix RB1 Drums - Wii"); - check_device(0x1BAD, 0x3010, 0x301F, "Harmonix RB2 Guitar - Wii"); - check_device(0x1BAD, 0x3110, 0x313F, "Harmonix RB2 Drums - Wii"); - check_device(0x1BAD, 0x3330, 0x333F, "Harmonix Keyboard - Wii"); - check_device(0x1BAD, 0x3430, 0x343F, "Harmonix Button Guitar - Wii"); - check_device(0x1BAD, 0x3530, 0x353F, "Harmonix Real Guitar - Wii"); - - if (desc.idVendor == 0x1209 && desc.idProduct == 0x2882) - { - sys_usbd.success("Found device: Santroller"); - // Send the device a specific control transfer so that it jumps to a RPCS3 compatible mode - libusb_device_handle* lusb_handle; - if (libusb_open(list[index], &lusb_handle) == LIBUSB_SUCCESS) - { -#ifdef __linux__ - libusb_set_auto_detach_kernel_driver(lusb_handle, true); - libusb_claim_interface(lusb_handle, 2); -#endif - libusb_control_transfer(lusb_handle, +LIBUSB_ENDPOINT_IN | +LIBUSB_REQUEST_TYPE_CLASS | +LIBUSB_RECIPIENT_INTERFACE, 0x01, 0x03f2, 2, nullptr, 0, 5000); - libusb_close(lusb_handle); - } - else - { - sys_usbd.error("Unable to open Santroller device, make sure Santroller isn't open in the background."); - } - } - - // Top Shot Elite controllers - check_device(0x12BA, 0x04A0, 0x04A0, "Top Shot Elite"); - check_device(0x12BA, 0x04A1, 0x04A1, "Top Shot Fearmaster"); - check_device(0x12BA, 0x04B0, 0x04B0, "Rapala Fishing Rod"); - - // GT5 Wheels&co - check_device(0x046D, 0xC283, 0xC29B, "lgFF_c283_c29b"); - check_device(0x044F, 0xB653, 0xB653, "Thrustmaster RGT FFB Pro"); - check_device(0x044F, 0xB65A, 0xB65A, "Thrustmaster F430"); - check_device(0x044F, 0xB65D, 0xB65D, "Thrustmaster FFB"); - check_device(0x044F, 0xB65E, 0xB65E, "Thrustmaster TRS"); - check_device(0x044F, 0xB660, 0xB660, "Thrustmaster T500 RS Gear Shift"); - - // GT6 - check_device(0x2833, 0x0001, 0x0001, "Oculus"); - check_device(0x046D, 0xCA03, 0xCA03, "lgFF_ca03_ca03"); - - // Buzz controllers - check_device(0x054C, 0x1000, 0x1040, "buzzer0"); - check_device(0x054C, 0x0001, 0x0041, "buzzer1"); - check_device(0x054C, 0x0042, 0x0042, "buzzer2"); - check_device(0x046D, 0xC220, 0xC220, "buzzer9"); - - // GunCon3 Gun - check_device(0x0B9A, 0x0800, 0x0800, "GunCon3"); - - // uDraw GameTablet - check_device(0x20D6, 0xCB17, 0xCB17, "uDraw GameTablet"); - - // DVB-T - check_device(0x1415, 0x0003, 0x0003, "PlayTV SCEH-0036"); - - // 0x0900: "H050 USJ(C) PCB rev00", 0x0910: "USIO PCB rev00" - if (check_device(0x0B9A, 0x0900, 0x0910, "PS3A-USJ")) - { - found_usj = true; - } - - // Densha de GO! controller - check_device(0x0AE4, 0x0004, 0x0004, "Densha de GO! Type 2 Controller"); - - // EA Active 2 dongle for connecting wristbands & legband - check_device(0x21A4, 0xAC27, 0xAC27, "EA Active 2 Dongle"); - - // Tony Hawk RIDE Skateboard - check_device(0x12BA, 0x0400, 0x0400, "Tony Hawk RIDE Skateboard Controller"); - - // PSP in UsbPspCm mode - check_device(0x054C, 0x01CB, 0x01CB, "UsbPspcm"); + sys_usbd.notice("Ghltar config=\n", g_cfg_ghltar.to_string()); } - libusb_free_device_list(list, 1); + if (g_cfg.io.turntable == turntable_handler::one_controller || g_cfg.io.turntable == turntable_handler::two_controllers) + { + if (!g_cfg_turntable.load()) + { + sys_usbd.notice("Could not load turntable config. Using defaults."); + } + + sys_usbd.notice("Turntable config=\n", g_cfg_turntable.to_string()); + } + + if (g_cfg.io.buzz == buzz_handler::one_controller || g_cfg.io.buzz == buzz_handler::two_controllers) + { + if (!g_cfg_buzz.load()) + { + sys_usbd.notice("Could not load buzz config. Using defaults."); + } + + sys_usbd.notice("Buzz config=\n", g_cfg_buzz.to_string()); + } + + perform_scan(); + + // Set up emulated devices for any devices that are not already being passed through + std::map passthrough_usb_device_counts; + for (const auto& dev : usb_devices) + { + for (const auto& entry : device_allow_list) + { + const u16 idVendor = dev->device._device.idVendor; + const u16 idProduct = dev->device._device.idProduct; + if (entry.max_device_count != nullptr && (idVendor == entry.id_vendor && idProduct >= entry.id_product_min && idProduct <= entry.id_product_max)) + { + passthrough_usb_device_counts[entry]++; + } + } + } + + for (const auto& entry : device_allow_list) + { + if (entry.max_device_count && entry.make_instance) + { + const int count = passthrough_usb_device_counts[entry]; + for (int i = count; i < entry.max_device_count(); i++) + { + sys_usbd.success("Emulating device: %s (%d)", std::basic_string(entry.device_name), i + 1); + auto usb_dev = entry.make_instance(i, get_new_location()); + connect_usb_device(usb_dev, true); + } + } + } for (int i = 0; i < 8; i++) // Add VFS USB mass storage devices (/dev_usbXXX) to the USB device list { @@ -396,37 +543,6 @@ usb_handler_thread::usb_handler_thread() usb_devices.push_back(std::make_shared(usb_info, get_new_location())); } - if (!found_skylander) - { - sys_usbd.notice("Adding emulated skylander"); - usb_devices.push_back(std::make_shared(get_new_location())); - } - - if (!found_infinity) - { - sys_usbd.notice("Adding emulated infinity base"); - usb_devices.push_back(std::make_shared(get_new_location())); - } - - if (!found_dimension) - { - sys_usbd.notice("Adding emulated dimension toypad"); - usb_devices.push_back(std::make_shared(get_new_location())); - } - - if (!found_usj) - { - if (!g_cfg_usio.load()) - { - sys_usbd.notice("Could not load usio config. Using defaults."); - } - - sys_usbd.notice("Adding emulated USIO"); - usb_devices.push_back(std::make_shared(get_new_location())); - - sys_usbd.notice("USIO config=\n", g_cfg_usio.to_string()); - } - const std::vector devices_list = fmt::split(g_cfg.io.midi_devices.to_string(), { "@@@" }); for (usz index = 0; index < std::min(max_midi_devices, devices_list.size()); index++) { @@ -458,65 +574,6 @@ usb_handler_thread::usb_handler_thread() break; } } - - if (g_cfg.io.ghltar == ghltar_handler::one_controller || g_cfg.io.ghltar == ghltar_handler::two_controllers) - { - if (!g_cfg_ghltar.load()) - { - sys_usbd.notice("Could not load ghltar config. Using defaults."); - } - - sys_usbd.notice("Adding emulated GHLtar (1 player)"); - usb_devices.push_back(std::make_shared(0, get_new_location())); - - if (g_cfg.io.ghltar == ghltar_handler::two_controllers) - { - sys_usbd.notice("Adding emulated GHLtar (2 players)"); - usb_devices.push_back(std::make_shared(1, get_new_location())); - } - - sys_usbd.notice("Ghltar config=\n", g_cfg_ghltar.to_string()); - } - - if (g_cfg.io.turntable == turntable_handler::one_controller || g_cfg.io.turntable == turntable_handler::two_controllers) - { - if (!g_cfg_turntable.load()) - { - sys_usbd.notice("Could not load turntable config. Using defaults."); - } - - sys_usbd.notice("Adding emulated turntable (1 player)"); - usb_devices.push_back(std::make_shared(0, get_new_location())); - - if (g_cfg.io.turntable == turntable_handler::two_controllers) - { - sys_usbd.notice("Adding emulated turntable (2 players)"); - usb_devices.push_back(std::make_shared(1, get_new_location())); - } - - sys_usbd.notice("Turntable config=\n", g_cfg_turntable.to_string()); - } - - if (g_cfg.io.buzz == buzz_handler::one_controller || g_cfg.io.buzz == buzz_handler::two_controllers) - { - if (!g_cfg_buzz.load()) - { - sys_usbd.notice("Could not load buzz config. Using defaults."); - } - - sys_usbd.notice("Adding emulated Buzz! buzzer (1-4 players)"); - usb_devices.push_back(std::make_shared(0, 3, get_new_location())); - - if (g_cfg.io.buzz == buzz_handler::two_controllers) - { - // The current buzz emulation piggybacks on the pad input. - // Since there can only be 7 pads connected on a PS3 the 8th player is currently not supported - sys_usbd.notice("Adding emulated Buzz! buzzer (5-7 players)"); - usb_devices.push_back(std::make_shared(4, 6, get_new_location())); - } - - sys_usbd.notice("Buzz config=\n", g_cfg_buzz.to_string()); - } } usb_handler_thread::~usb_handler_thread() @@ -525,6 +582,7 @@ usb_handler_thread::~usb_handler_thread() handled_devices.clear(); open_pipes.clear(); usb_devices.clear(); + usb_passthrough_devices.clear(); for (u32 index = 0; index < MAX_SYS_USBD_TRANSFERS; index++) { @@ -532,6 +590,10 @@ usb_handler_thread::~usb_handler_thread() libusb_free_transfer(transfers[index].transfer); } +#if LIBUSB_API_VERSION >= 0x01000102 + libusb_hotplug_deregister_callback(ctx, callback_handle); +#endif + if (ctx) libusb_exit(ctx); } @@ -539,10 +601,21 @@ usb_handler_thread::~usb_handler_thread() void usb_handler_thread::operator()() { timeval lusb_tv{0, 0}; - + if (!hotplug_supported) + { + usb_hotplug_timeout = get_system_time() + 4'000'000ull; + } while (ctx && thread_ctrl::state() != thread_state::aborting) { - // Todo: Hotplug here? + const u64 now = get_system_time(); + if (now > usb_hotplug_timeout) + { + // If we did the hotplug scan each cycle the game performance was significantly degraded, so we only perform this scan + // every 4 seconds. + // On systems where hotplug is native, we wait a little bit for devices to settle before we start the scan + perform_scan(); + usb_hotplug_timeout = hotplug_supported ? umax : get_system_time() + 4'000'000ull; + } // Process asynchronous requests that are pending libusb_handle_events_timeout_completed(ctx, &lusb_tv, nullptr); @@ -836,6 +909,7 @@ void usb_handler_thread::connect_usb_device(std::shared_ptr dev, boo if (!dev->open_device()) { sys_usbd.error("Failed to open USB device(VID=0x%04x, PID=0x%04x) for LDD <%s>", dev->device._device.idVendor, dev->device._device.idProduct, name); + disconnect_usb_device(dev); return; } @@ -844,6 +918,7 @@ void usb_handler_thread::connect_usb_device(std::shared_ptr dev, boo handled_devices.emplace(dev->assigned_number, std::pair(UsbInternalDevice{0x00, narrow(dev->assigned_number), 0x02, 0x40}, dev)); send_message(SYS_USBD_ATTACH, dev->assigned_number); sys_usbd.success("USB device(VID=0x%04x, PID=0x%04x) matches up with LDD <%s>, assigned as handled_device=0x%x", dev->device._device.idVendor, dev->device._device.idProduct, name, dev->assigned_number); + return; } } } @@ -855,10 +930,18 @@ void usb_handler_thread::disconnect_usb_device(std::shared_ptr dev, send_message(SYS_USBD_DETACH, dev->assigned_number); sys_usbd.success("USB device(VID=0x%04x, PID=0x%04x) unassigned, handled_device=0x%x", dev->device._device.idVendor, dev->device._device.idProduct, dev->assigned_number); dev->assigned_number = 0; + std::erase_if(open_pipes, [&](const auto& val) + { + return val.second.device == dev; + }); } - if (update_usb_devices) - usb_devices.erase(find(usb_devices.begin(), usb_devices.end(), dev)); + { + std::erase_if(usb_devices, [&](const auto& val) + { + return val == dev; + }); + } } void connect_usb_controller(u8 index, input::product_type type) @@ -947,6 +1030,14 @@ void connect_usb_controller(u8 index, input::product_type type) } } +void handle_hotplug_event(bool connected) +{ + if (auto usbh = g_fxo->try_get>()) + { + usbh->usb_hotplug_timeout = get_system_time() + (connected ? 1'000'000ull : 0); + } +} + error_code sys_usbd_initialize(ppu_thread& ppu, vm::ptr handle) { diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.h b/rpcs3/Emu/Cell/lv2/sys_usbd.h index 9c81fb7b80..a2fd911e35 100644 --- a/rpcs3/Emu/Cell/lv2/sys_usbd.h +++ b/rpcs3/Emu/Cell/lv2/sys_usbd.h @@ -89,3 +89,4 @@ error_code sys_usbd_register_extra_ldd(ppu_thread& ppu, u32 handle, vm::cptr s_product, u16 slen_product); void connect_usb_controller(u8 index, input::product_type); +void handle_hotplug_event(bool connected); diff --git a/rpcs3/Emu/Cell/lv2/sys_vm.cpp b/rpcs3/Emu/Cell/lv2/sys_vm.cpp index 2591824b1d..2a224d2339 100644 --- a/rpcs3/Emu/Cell/lv2/sys_vm.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_vm.cpp @@ -6,7 +6,6 @@ #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/timers.hpp" -#include "Emu/Memory/vm_locking.h" sys_vm_t::sys_vm_t(u32 _addr, u32 vsize, lv2_memory_container* ct, u32 psize) : ct(ct) diff --git a/rpcs3/Emu/GDB.cpp b/rpcs3/Emu/GDB.cpp index 56ad52705a..1a4724d47e 100644 --- a/rpcs3/Emu/GDB.cpp +++ b/rpcs3/Emu/GDB.cpp @@ -34,6 +34,8 @@ #endif #endif +#include "Emu/Cell/timers.hpp" + #include #include #include @@ -830,7 +832,7 @@ bool gdb_thread::cmd_vcont(gdb_cmd& cmd) { Emu.Run(true); } - if (Emu.IsPaused()) + else if (Emu.IsPaused()) { Emu.Resume(); } diff --git a/rpcs3/Emu/GDB.h b/rpcs3/Emu/GDB.h index 8ba241e110..75d272f9d7 100644 --- a/rpcs3/Emu/GDB.h +++ b/rpcs3/Emu/GDB.h @@ -1,7 +1,6 @@ #pragma once #include "Utilities/Thread.h" -#include #include struct gdb_cmd; diff --git a/rpcs3/Emu/IPC.h b/rpcs3/Emu/IPC.h index 26819a3fbd..7c94458376 100644 --- a/rpcs3/Emu/IPC.h +++ b/rpcs3/Emu/IPC.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include "Utilities/mutex.h" diff --git a/rpcs3/Emu/IdManager.cpp b/rpcs3/Emu/IdManager.cpp index b8225b07ef..2a8f9ffeee 100644 --- a/rpcs3/Emu/IdManager.cpp +++ b/rpcs3/Emu/IdManager.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "IdManager.h" -#include "Utilities/Thread.h" shared_mutex id_manager::g_mutex; diff --git a/rpcs3/Emu/IdManager.h b/rpcs3/Emu/IdManager.h index 7846d6f70f..3884e439b2 100644 --- a/rpcs3/Emu/IdManager.h +++ b/rpcs3/Emu/IdManager.h @@ -5,8 +5,6 @@ #include #include -#include -#include #include #include "util/serialization.hpp" diff --git a/rpcs3/Emu/Io/Buzz.cpp b/rpcs3/Emu/Io/Buzz.cpp index 575c5585b1..7c7890d41e 100644 --- a/rpcs3/Emu/Io/Buzz.cpp +++ b/rpcs3/Emu/Io/Buzz.cpp @@ -4,6 +4,7 @@ #include "Buzz.h" #include "Emu/Cell/lv2/sys_usbd.h" #include "Emu/Io/buzz_config.h" +#include "Emu/system_config.h" #include "Input/pad_thread.h" LOG_CHANNEL(buzz_log, "BUZZ"); @@ -85,6 +86,23 @@ usb_device_buzz::~usb_device_buzz() { } +std::shared_ptr usb_device_buzz::make_instance(u32 controller_index, const std::array& location) +{ + if (controller_index == 0) + { + return std::make_shared(0, 3, location); + } + + // The current buzz emulation piggybacks on the pad input. + // Since there can only be 7 pads connected on a PS3 the 8th player is currently not supported + return std::make_shared(4, 6, location); +} + +u16 usb_device_buzz::get_num_emu_devices() +{ + return static_cast(g_cfg.io.buzz.get()); +} + void usb_device_buzz::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) { transfer->fake = true; diff --git a/rpcs3/Emu/Io/Buzz.h b/rpcs3/Emu/Io/Buzz.h index 0515dbac71..e8aafc0e35 100644 --- a/rpcs3/Emu/Io/Buzz.h +++ b/rpcs3/Emu/Io/Buzz.h @@ -8,6 +8,9 @@ public: usb_device_buzz(u32 first_controller, u32 last_controller, const std::array& location); ~usb_device_buzz(); + static std::shared_ptr make_instance(u32 controller_index, const std::array& location); + static u16 get_num_emu_devices(); + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/Dimensions.cpp b/rpcs3/Emu/Io/Dimensions.cpp index 40216e9ba4..a8ef6bc447 100644 --- a/rpcs3/Emu/Io/Dimensions.cpp +++ b/rpcs3/Emu/Io/Dimensions.cpp @@ -2,11 +2,6 @@ #include "Dimensions.h" #include -#include - -#include "Crypto/aes.h" -#include "Crypto/sha1.h" -#include "util/asm.hpp" #include "Emu/Cell/lv2/sys_usbd.h" @@ -573,6 +568,16 @@ usb_device_dimensions::~usb_device_dimensions() { } +std::shared_ptr usb_device_dimensions::make_instance(u32, const std::array& location) +{ + return std::make_shared(location); +} + +u16 usb_device_dimensions::get_num_emu_devices() +{ + return 1; +} + void usb_device_dimensions::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) { usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer); diff --git a/rpcs3/Emu/Io/Dimensions.h b/rpcs3/Emu/Io/Dimensions.h index db1c6577c3..9cf1bd7395 100644 --- a/rpcs3/Emu/Io/Dimensions.h +++ b/rpcs3/Emu/Io/Dimensions.h @@ -71,6 +71,9 @@ public: usb_device_dimensions(const std::array& location); ~usb_device_dimensions(); + static std::shared_ptr make_instance(u32 controller_index, const std::array& location); + static u16 get_num_emu_devices(); + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; void isochronous_transfer(UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/GHLtar.cpp b/rpcs3/Emu/Io/GHLtar.cpp index db60cccd5b..e520e53c7f 100644 --- a/rpcs3/Emu/Io/GHLtar.cpp +++ b/rpcs3/Emu/Io/GHLtar.cpp @@ -4,6 +4,7 @@ #include "GHLtar.h" #include "Emu/Cell/lv2/sys_usbd.h" #include "Emu/Io/ghltar_config.h" +#include "Emu/system_config.h" #include "Input/pad_thread.h" LOG_CHANNEL(ghltar_log, "GHLTAR"); @@ -52,6 +53,16 @@ usb_device_ghltar::~usb_device_ghltar() { } +std::shared_ptr usb_device_ghltar::make_instance(u32 controller_index, const std::array& location) +{ + return std::make_shared(controller_index, location); +} + +u16 usb_device_ghltar::get_num_emu_devices() +{ + return static_cast(g_cfg.io.ghltar.get()); +} + void usb_device_ghltar::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) { transfer->fake = true; diff --git a/rpcs3/Emu/Io/GHLtar.h b/rpcs3/Emu/Io/GHLtar.h index b75fcc8df9..e36df36498 100644 --- a/rpcs3/Emu/Io/GHLtar.h +++ b/rpcs3/Emu/Io/GHLtar.h @@ -8,6 +8,9 @@ public: usb_device_ghltar(u32 controller_index, const std::array& location); ~usb_device_ghltar(); + static std::shared_ptr make_instance(u32 controller_index, const std::array& location); + static u16 get_num_emu_devices(); + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/Infinity.cpp b/rpcs3/Emu/Io/Infinity.cpp index a91c295028..ff4ce9e85b 100644 --- a/rpcs3/Emu/Io/Infinity.cpp +++ b/rpcs3/Emu/Io/Infinity.cpp @@ -5,7 +5,6 @@ #include "Crypto/aes.h" #include "Crypto/sha1.h" -#include "util/asm.hpp" LOG_CHANNEL(infinity_log, "infinity"); @@ -373,6 +372,16 @@ usb_device_infinity::~usb_device_infinity() { } +std::shared_ptr usb_device_infinity::make_instance(u32, const std::array& location) +{ + return std::make_shared(location); +} + +u16 usb_device_infinity::get_num_emu_devices() +{ + return 1; +} + void usb_device_infinity::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) { usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer); diff --git a/rpcs3/Emu/Io/Infinity.h b/rpcs3/Emu/Io/Infinity.h index 1aaeebeb41..2709a0c266 100644 --- a/rpcs3/Emu/Io/Infinity.h +++ b/rpcs3/Emu/Io/Infinity.h @@ -60,6 +60,9 @@ public: usb_device_infinity(const std::array& location); ~usb_device_infinity(); + static std::shared_ptr make_instance(u32 controller_index, const std::array& location); + static u16 get_num_emu_devices(); + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/KeyboardHandler.cpp b/rpcs3/Emu/Io/KeyboardHandler.cpp index 73ef8d0696..05ef6ef526 100644 --- a/rpcs3/Emu/Io/KeyboardHandler.cpp +++ b/rpcs3/Emu/Io/KeyboardHandler.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "KeyboardHandler.h" -#include "Utilities/StrUtil.h" LOG_CHANNEL(input_log, "Input"); diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 44648f252f..cafb85bd78 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "PadHandler.h" -#include "Emu/system_utils.hpp" #include "Emu/system_config.h" #include "Emu/Cell/timers.hpp" #include "Input/pad_thread.h" diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index eb536fde11..792515c5d8 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -9,7 +9,9 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" #endif -#include "3rdparty/fusion/fusion/Fusion/Fusion.h" +extern "C" { +#include "3rdparty/fusion/fusion/Fusion/FusionAhrs.h" +} #ifndef _MSC_VER #pragma GCC diagnostic pop #endif diff --git a/rpcs3/Emu/Io/Skylander.cpp b/rpcs3/Emu/Io/Skylander.cpp index 7f5b7628ec..974e9a887f 100644 --- a/rpcs3/Emu/Io/Skylander.cpp +++ b/rpcs3/Emu/Io/Skylander.cpp @@ -2,8 +2,6 @@ #include "Skylander.h" #include "Emu/Cell/lv2/sys_usbd.h" -#include "util/asm.hpp" - LOG_CHANNEL(skylander_log, "skylander"); sky_portal g_skyportal; @@ -208,6 +206,16 @@ usb_device_skylander::~usb_device_skylander() { } +std::shared_ptr usb_device_skylander::make_instance(u32, const std::array& location) +{ + return std::make_shared(location); +} + +u16 usb_device_skylander::get_num_emu_devices() +{ + return 1; +} + void usb_device_skylander::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) { transfer->fake = true; @@ -342,7 +350,7 @@ void usb_device_skylander::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoin transfer->fake = true; transfer->expected_count = buf_size; transfer->expected_result = HC_CC_NOERR; - + if (endpoint == 0x02) { // Audio transfers are fairly quick(~1ms) diff --git a/rpcs3/Emu/Io/Skylander.h b/rpcs3/Emu/Io/Skylander.h index da7dc3dfa4..be67c8c3cc 100644 --- a/rpcs3/Emu/Io/Skylander.h +++ b/rpcs3/Emu/Io/Skylander.h @@ -46,6 +46,9 @@ public: usb_device_skylander(const std::array& location); ~usb_device_skylander(); + static std::shared_ptr make_instance(u32 controller_index, const std::array& location); + static u16 get_num_emu_devices(); + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/Turntable.cpp b/rpcs3/Emu/Io/Turntable.cpp index 0ef979725e..eca1926825 100644 --- a/rpcs3/Emu/Io/Turntable.cpp +++ b/rpcs3/Emu/Io/Turntable.cpp @@ -5,6 +5,7 @@ #include "Emu/Cell/lv2/sys_usbd.h" #include "Emu/Io/turntable_config.h" #include "Input/pad_thread.h" +#include "Emu/system_config.h" LOG_CHANNEL(turntable_log, "TURN"); @@ -53,6 +54,16 @@ usb_device_turntable::~usb_device_turntable() { } +std::shared_ptr usb_device_turntable::make_instance(u32 controller_index, const std::array& location) +{ + return std::make_shared(controller_index, location); +} + +u16 usb_device_turntable::get_num_emu_devices() +{ + return static_cast(g_cfg.io.turntable.get()); +} + void usb_device_turntable::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) { transfer->fake = true; diff --git a/rpcs3/Emu/Io/Turntable.h b/rpcs3/Emu/Io/Turntable.h index 7ae0f51ead..f8186e9098 100644 --- a/rpcs3/Emu/Io/Turntable.h +++ b/rpcs3/Emu/Io/Turntable.h @@ -8,6 +8,9 @@ public: usb_device_turntable(u32 controller_index, const std::array& location); ~usb_device_turntable(); + static std::shared_ptr make_instance(u32 controller_index, const std::array& location); + static u16 get_num_emu_devices(); + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/buzz_config.h b/rpcs3/Emu/Io/buzz_config.h index 71f51f972f..f340ecd70e 100644 --- a/rpcs3/Emu/Io/buzz_config.h +++ b/rpcs3/Emu/Io/buzz_config.h @@ -2,8 +2,6 @@ #include "emulated_pad_config.h" -#include - enum class buzz_btn { red, diff --git a/rpcs3/Emu/Io/gem_config.h b/rpcs3/Emu/Io/gem_config.h index 2be86ff3c4..9cfca88ddb 100644 --- a/rpcs3/Emu/Io/gem_config.h +++ b/rpcs3/Emu/Io/gem_config.h @@ -2,8 +2,6 @@ #include "emulated_pad_config.h" -#include - enum class gem_btn : u32 { start, diff --git a/rpcs3/Emu/Io/ghltar_config.h b/rpcs3/Emu/Io/ghltar_config.h index 524542fa9f..dfe43ea2c5 100644 --- a/rpcs3/Emu/Io/ghltar_config.h +++ b/rpcs3/Emu/Io/ghltar_config.h @@ -2,8 +2,6 @@ #include "emulated_pad_config.h" -#include - enum class ghltar_btn { w1, diff --git a/rpcs3/Emu/Io/guncon3_config.h b/rpcs3/Emu/Io/guncon3_config.h index 6224fa856c..c2ea82a9f3 100644 --- a/rpcs3/Emu/Io/guncon3_config.h +++ b/rpcs3/Emu/Io/guncon3_config.h @@ -2,8 +2,6 @@ #include "emulated_pad_config.h" -#include - enum class guncon3_btn { trigger, diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index f48fbf7850..26a21f1f53 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -504,6 +504,8 @@ struct Pad u16 m_vendor_id{0}; u16 m_product_id{0}; + u64 m_disconnection_timer{0}; + s32 m_pressure_intensity_button_index{-1}; // Special button index. -1 if not set. bool m_pressure_intensity_button_pressed{}; // Last sensitivity button press state, used for toggle. bool m_pressure_intensity_toggled{}; // Whether the sensitivity is toggled on or off. diff --git a/rpcs3/Emu/Io/rb3drums_config.cpp b/rpcs3/Emu/Io/rb3drums_config.cpp index 34c289818c..76c86252ab 100644 --- a/rpcs3/Emu/Io/rb3drums_config.cpp +++ b/rpcs3/Emu/Io/rb3drums_config.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "rb3drums_config.h" -#include LOG_CHANNEL(cfg_log, "CFG"); diff --git a/rpcs3/Emu/Io/recording_config.cpp b/rpcs3/Emu/Io/recording_config.cpp index 357106e074..9b360a24ef 100644 --- a/rpcs3/Emu/Io/recording_config.cpp +++ b/rpcs3/Emu/Io/recording_config.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "recording_config.h" -#include LOG_CHANNEL(cfg_log, "CFG"); diff --git a/rpcs3/Emu/Io/topshotelite_config.h b/rpcs3/Emu/Io/topshotelite_config.h index e7a5639103..fa9ca952d2 100644 --- a/rpcs3/Emu/Io/topshotelite_config.h +++ b/rpcs3/Emu/Io/topshotelite_config.h @@ -2,8 +2,6 @@ #include "emulated_pad_config.h" -#include - enum class topshotelite_btn { trigger, diff --git a/rpcs3/Emu/Io/topshotfearmaster_config.h b/rpcs3/Emu/Io/topshotfearmaster_config.h index ef372c9b68..cd2aadf02d 100644 --- a/rpcs3/Emu/Io/topshotfearmaster_config.h +++ b/rpcs3/Emu/Io/topshotfearmaster_config.h @@ -2,8 +2,6 @@ #include "emulated_pad_config.h" -#include - enum class topshotfearmaster_btn { trigger, diff --git a/rpcs3/Emu/Io/turntable_config.h b/rpcs3/Emu/Io/turntable_config.h index 8461cf5217..c2237520f9 100644 --- a/rpcs3/Emu/Io/turntable_config.h +++ b/rpcs3/Emu/Io/turntable_config.h @@ -2,8 +2,6 @@ #include "emulated_pad_config.h" -#include - enum class turntable_btn { red, diff --git a/rpcs3/Emu/Io/usb_device.cpp b/rpcs3/Emu/Io/usb_device.cpp index a7c3812b41..be35a61469 100644 --- a/rpcs3/Emu/Io/usb_device.cpp +++ b/rpcs3/Emu/Io/usb_device.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "Emu/System.h" +#include "Emu/Cell/timers.hpp" #include "Emu/Cell/lv2/sys_usbd.h" #include "Emu/Io/usb_device.h" #include "Utilities/StrUtil.h" diff --git a/rpcs3/Emu/Io/usio.cpp b/rpcs3/Emu/Io/usio.cpp index 077f6c7d62..e01deccf8a 100644 --- a/rpcs3/Emu/Io/usio.cpp +++ b/rpcs3/Emu/Io/usio.cpp @@ -127,6 +127,16 @@ usb_device_usio::~usb_device_usio() save_backup(); } +std::shared_ptr usb_device_usio::make_instance(u32, const std::array& location) +{ + return std::make_shared(location); +} + +u16 usb_device_usio::get_num_emu_devices() +{ + return 1; +} + void usb_device_usio::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) { transfer->fake = true; diff --git a/rpcs3/Emu/Io/usio.h b/rpcs3/Emu/Io/usio.h index f4cc93fc20..7a83e7a8ca 100644 --- a/rpcs3/Emu/Io/usio.h +++ b/rpcs3/Emu/Io/usio.h @@ -9,6 +9,9 @@ public: usb_device_usio(const std::array& location); ~usb_device_usio(); + static std::shared_ptr make_instance(u32 controller_index, const std::array& location); + static u16 get_num_emu_devices(); + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/usio_config.h b/rpcs3/Emu/Io/usio_config.h index 1ca078b2a4..4c5fe6f017 100644 --- a/rpcs3/Emu/Io/usio_config.h +++ b/rpcs3/Emu/Io/usio_config.h @@ -2,8 +2,6 @@ #include "emulated_pad_config.h" -#include - enum class usio_btn { test, diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 6ab0ecc71d..28b8b4e279 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -4,10 +4,8 @@ #include "vm_ref.h" #include "vm_reservation.h" -#include "Utilities/mutex.h" #include "Utilities/Thread.h" #include "Utilities/address_range.h" -#include "Utilities/JIT.h" #include "Emu/CPU/CPUThread.h" #include "Emu/RSX/RSXThread.h" #include "Emu/Cell/SPURecompiler.h" @@ -20,6 +18,8 @@ #include "util/simd.hpp" #include "util/serialization.hpp" +#include + LOG_CHANNEL(vm_log, "VM"); void ppu_remove_hle_instructions(u32 addr, u32 size); diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index b76e8a89d3..9680187cd4 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -5,7 +5,6 @@ #include "util/types.hpp" #include "util/atomic.hpp" #include "util/auto_typemap.hpp" -#include "Utilities/StrFmt.h" #include "util/to_endian.hpp" diff --git a/rpcs3/Emu/NP/fb_helpers.cpp b/rpcs3/Emu/NP/fb_helpers.cpp index 04a92d77ee..100c3e864c 100644 --- a/rpcs3/Emu/NP/fb_helpers.cpp +++ b/rpcs3/Emu/NP/fb_helpers.cpp @@ -1,5 +1,4 @@ #include "stdafx.h" -#include "np_handler.h" #include "Emu/Cell/lv2/sys_process.h" #include "fb_helpers.h" @@ -46,7 +45,7 @@ namespace np { sce_group->groupId = fb_group->groupId(); sce_group->withPassword = fb_group->withPassword(); - sce_group->withLabel = fb_group->withLabel(); + sce_group->withLabel = fb_group->label() ? 1 : 0; if (fb_group->label()) { for (flatbuffers::uoffset_t l_index = 0; l_index < fb_group->label()->size(); l_index++) diff --git a/rpcs3/Emu/NP/fb_helpers.h b/rpcs3/Emu/NP/fb_helpers.h index 82ab034ea2..6b92cfba37 100644 --- a/rpcs3/Emu/NP/fb_helpers.h +++ b/rpcs3/Emu/NP/fb_helpers.h @@ -1,5 +1,10 @@ #pragma once +#include "Emu/Cell/Modules/sceNp.h" +#include "Emu/Cell/Modules/sceNp2.h" +#include "np_event_data.h" +#include "generated/np2_structs_generated.h" + namespace np { void BinAttr_to_SceNpMatching2BinAttr(event_data& edata, const BinAttr* bin_attr, SceNpMatching2BinAttr* binattr_info); diff --git a/rpcs3/Emu/NP/generated/np2_structs.fbs b/rpcs3/Emu/NP/generated/np2_structs.fbs index 634d3fe6b8..bf360bdb86 100644 --- a/rpcs3/Emu/NP/generated/np2_structs.fbs +++ b/rpcs3/Emu/NP/generated/np2_structs.fbs @@ -1,3 +1,18 @@ +table SignalingAddr { + ip:[uint8]; + port:uint16; +} + +table MatchingSignalingInfo { + npid:string; + addr:SignalingAddr; +} + +table Matching2SignalingInfo { + member_id:uint16; + addr:SignalingAddr; +} + table BinAttr { id:uint16; data:[uint8]; @@ -27,7 +42,6 @@ table OptParam { table GroupConfig { slotNum:uint32; - withLabel:bool; label:[uint8]; withPassword:bool; } @@ -52,7 +66,6 @@ table RoomMemberDataInternal { table RoomGroup { groupId:uint8; withPassword:bool; - withLabel:bool; label:[uint8]; slotNum:uint32; curGroupMemberNum:uint32; @@ -108,7 +121,7 @@ table PresenceOptionData { } table RoomGroupPasswordConfig { - groupId:[uint8]; + groupId:uint8; withPassword:bool; } @@ -161,6 +174,11 @@ table JoinRoomRequest { teamId:uint8; } +table JoinRoomResponse { + room_data: RoomDataInternal; + signaling_data: [Matching2SignalingInfo]; +} + table LeaveRoomRequest { roomId:uint64; optData:PresenceOptionData; @@ -221,6 +239,12 @@ table RoomMemberUpdateInfo { optData:PresenceOptionData; } +table NotificationUserJoinedRoom { + room_id:uint64; + update_info:RoomMemberUpdateInfo; + signaling:SignalingAddr; +} + table RoomUpdateInfo { eventCause:uint8; errorCode:int32; diff --git a/rpcs3/Emu/NP/generated/np2_structs_generated.h b/rpcs3/Emu/NP/generated/np2_structs_generated.h index a31fc0c9c4..f1d24ae568 100644 --- a/rpcs3/Emu/NP/generated/np2_structs_generated.h +++ b/rpcs3/Emu/NP/generated/np2_structs_generated.h @@ -13,6 +13,15 @@ static_assert(FLATBUFFERS_VERSION_MAJOR == 24 && FLATBUFFERS_VERSION_REVISION == 25, "Non-compatible flatbuffers version included"); +struct SignalingAddr; +struct SignalingAddrBuilder; + +struct MatchingSignalingInfo; +struct MatchingSignalingInfoBuilder; + +struct Matching2SignalingInfo; +struct Matching2SignalingInfoBuilder; + struct BinAttr; struct BinAttrBuilder; @@ -70,6 +79,9 @@ struct CreateJoinRoomRequestBuilder; struct JoinRoomRequest; struct JoinRoomRequestBuilder; +struct JoinRoomResponse; +struct JoinRoomResponseBuilder; + struct LeaveRoomRequest; struct LeaveRoomRequestBuilder; @@ -100,6 +112,9 @@ struct GetRoomDataInternalRequestBuilder; struct RoomMemberUpdateInfo; struct RoomMemberUpdateInfoBuilder; +struct NotificationUserJoinedRoom; +struct NotificationUserJoinedRoomBuilder; + struct RoomUpdateInfo; struct RoomUpdateInfoBuilder; @@ -256,6 +271,185 @@ struct SearchJoinRoomGUIRequestBuilder; struct MatchingSearchJoinRoomInfo; struct MatchingSearchJoinRoomInfoBuilder; +struct SignalingAddr FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef SignalingAddrBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_IP = 4, + VT_PORT = 6 + }; + const ::flatbuffers::Vector *ip() const { + return GetPointer *>(VT_IP); + } + uint16_t port() const { + return GetField(VT_PORT, 0); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_IP) && + verifier.VerifyVector(ip()) && + VerifyField(verifier, VT_PORT, 2) && + verifier.EndTable(); + } +}; + +struct SignalingAddrBuilder { + typedef SignalingAddr Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_ip(::flatbuffers::Offset<::flatbuffers::Vector> ip) { + fbb_.AddOffset(SignalingAddr::VT_IP, ip); + } + void add_port(uint16_t port) { + fbb_.AddElement(SignalingAddr::VT_PORT, port, 0); + } + explicit SignalingAddrBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateSignalingAddr( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> ip = 0, + uint16_t port = 0) { + SignalingAddrBuilder builder_(_fbb); + builder_.add_ip(ip); + builder_.add_port(port); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateSignalingAddrDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *ip = nullptr, + uint16_t port = 0) { + auto ip__ = ip ? _fbb.CreateVector(*ip) : 0; + return CreateSignalingAddr( + _fbb, + ip__, + port); +} + +struct MatchingSignalingInfo FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef MatchingSignalingInfoBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NPID = 4, + VT_ADDR = 6 + }; + const ::flatbuffers::String *npid() const { + return GetPointer(VT_NPID); + } + const SignalingAddr *addr() const { + return GetPointer(VT_ADDR); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NPID) && + verifier.VerifyString(npid()) && + VerifyOffset(verifier, VT_ADDR) && + verifier.VerifyTable(addr()) && + verifier.EndTable(); + } +}; + +struct MatchingSignalingInfoBuilder { + typedef MatchingSignalingInfo Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_npid(::flatbuffers::Offset<::flatbuffers::String> npid) { + fbb_.AddOffset(MatchingSignalingInfo::VT_NPID, npid); + } + void add_addr(::flatbuffers::Offset addr) { + fbb_.AddOffset(MatchingSignalingInfo::VT_ADDR, addr); + } + explicit MatchingSignalingInfoBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateMatchingSignalingInfo( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> npid = 0, + ::flatbuffers::Offset addr = 0) { + MatchingSignalingInfoBuilder builder_(_fbb); + builder_.add_addr(addr); + builder_.add_npid(npid); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateMatchingSignalingInfoDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const char *npid = nullptr, + ::flatbuffers::Offset addr = 0) { + auto npid__ = npid ? _fbb.CreateString(npid) : 0; + return CreateMatchingSignalingInfo( + _fbb, + npid__, + addr); +} + +struct Matching2SignalingInfo FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef Matching2SignalingInfoBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_MEMBER_ID = 4, + VT_ADDR = 6 + }; + uint16_t member_id() const { + return GetField(VT_MEMBER_ID, 0); + } + const SignalingAddr *addr() const { + return GetPointer(VT_ADDR); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_MEMBER_ID, 2) && + VerifyOffset(verifier, VT_ADDR) && + verifier.VerifyTable(addr()) && + verifier.EndTable(); + } +}; + +struct Matching2SignalingInfoBuilder { + typedef Matching2SignalingInfo Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_member_id(uint16_t member_id) { + fbb_.AddElement(Matching2SignalingInfo::VT_MEMBER_ID, member_id, 0); + } + void add_addr(::flatbuffers::Offset addr) { + fbb_.AddOffset(Matching2SignalingInfo::VT_ADDR, addr); + } + explicit Matching2SignalingInfoBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateMatching2SignalingInfo( + ::flatbuffers::FlatBufferBuilder &_fbb, + uint16_t member_id = 0, + ::flatbuffers::Offset addr = 0) { + Matching2SignalingInfoBuilder builder_(_fbb); + builder_.add_addr(addr); + builder_.add_member_id(member_id); + return builder_.Finish(); +} + struct BinAttr FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef BinAttrBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -549,16 +743,12 @@ struct GroupConfig FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef GroupConfigBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_SLOTNUM = 4, - VT_WITHLABEL = 6, - VT_LABEL = 8, - VT_WITHPASSWORD = 10 + VT_LABEL = 6, + VT_WITHPASSWORD = 8 }; uint32_t slotNum() const { return GetField(VT_SLOTNUM, 0); } - bool withLabel() const { - return GetField(VT_WITHLABEL, 0) != 0; - } const ::flatbuffers::Vector *label() const { return GetPointer *>(VT_LABEL); } @@ -568,7 +758,6 @@ struct GroupConfig FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_SLOTNUM, 4) && - VerifyField(verifier, VT_WITHLABEL, 1) && VerifyOffset(verifier, VT_LABEL) && verifier.VerifyVector(label()) && VerifyField(verifier, VT_WITHPASSWORD, 1) && @@ -583,9 +772,6 @@ struct GroupConfigBuilder { void add_slotNum(uint32_t slotNum) { fbb_.AddElement(GroupConfig::VT_SLOTNUM, slotNum, 0); } - void add_withLabel(bool withLabel) { - fbb_.AddElement(GroupConfig::VT_WITHLABEL, static_cast(withLabel), 0); - } void add_label(::flatbuffers::Offset<::flatbuffers::Vector> label) { fbb_.AddOffset(GroupConfig::VT_LABEL, label); } @@ -606,28 +792,24 @@ struct GroupConfigBuilder { inline ::flatbuffers::Offset CreateGroupConfig( ::flatbuffers::FlatBufferBuilder &_fbb, uint32_t slotNum = 0, - bool withLabel = false, ::flatbuffers::Offset<::flatbuffers::Vector> label = 0, bool withPassword = false) { GroupConfigBuilder builder_(_fbb); builder_.add_label(label); builder_.add_slotNum(slotNum); builder_.add_withPassword(withPassword); - builder_.add_withLabel(withLabel); return builder_.Finish(); } inline ::flatbuffers::Offset CreateGroupConfigDirect( ::flatbuffers::FlatBufferBuilder &_fbb, uint32_t slotNum = 0, - bool withLabel = false, const std::vector *label = nullptr, bool withPassword = false) { auto label__ = label ? _fbb.CreateVector(*label) : 0; return CreateGroupConfig( _fbb, slotNum, - withLabel, label__, withPassword); } @@ -854,10 +1036,9 @@ struct RoomGroup FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_GROUPID = 4, VT_WITHPASSWORD = 6, - VT_WITHLABEL = 8, - VT_LABEL = 10, - VT_SLOTNUM = 12, - VT_CURGROUPMEMBERNUM = 14 + VT_LABEL = 8, + VT_SLOTNUM = 10, + VT_CURGROUPMEMBERNUM = 12 }; uint8_t groupId() const { return GetField(VT_GROUPID, 0); @@ -865,9 +1046,6 @@ struct RoomGroup FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { bool withPassword() const { return GetField(VT_WITHPASSWORD, 0) != 0; } - bool withLabel() const { - return GetField(VT_WITHLABEL, 0) != 0; - } const ::flatbuffers::Vector *label() const { return GetPointer *>(VT_LABEL); } @@ -881,7 +1059,6 @@ struct RoomGroup FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { return VerifyTableStart(verifier) && VerifyField(verifier, VT_GROUPID, 1) && VerifyField(verifier, VT_WITHPASSWORD, 1) && - VerifyField(verifier, VT_WITHLABEL, 1) && VerifyOffset(verifier, VT_LABEL) && verifier.VerifyVector(label()) && VerifyField(verifier, VT_SLOTNUM, 4) && @@ -900,9 +1077,6 @@ struct RoomGroupBuilder { void add_withPassword(bool withPassword) { fbb_.AddElement(RoomGroup::VT_WITHPASSWORD, static_cast(withPassword), 0); } - void add_withLabel(bool withLabel) { - fbb_.AddElement(RoomGroup::VT_WITHLABEL, static_cast(withLabel), 0); - } void add_label(::flatbuffers::Offset<::flatbuffers::Vector> label) { fbb_.AddOffset(RoomGroup::VT_LABEL, label); } @@ -927,7 +1101,6 @@ inline ::flatbuffers::Offset CreateRoomGroup( ::flatbuffers::FlatBufferBuilder &_fbb, uint8_t groupId = 0, bool withPassword = false, - bool withLabel = false, ::flatbuffers::Offset<::flatbuffers::Vector> label = 0, uint32_t slotNum = 0, uint32_t curGroupMemberNum = 0) { @@ -935,7 +1108,6 @@ inline ::flatbuffers::Offset CreateRoomGroup( builder_.add_curGroupMemberNum(curGroupMemberNum); builder_.add_slotNum(slotNum); builder_.add_label(label); - builder_.add_withLabel(withLabel); builder_.add_withPassword(withPassword); builder_.add_groupId(groupId); return builder_.Finish(); @@ -945,7 +1117,6 @@ inline ::flatbuffers::Offset CreateRoomGroupDirect( ::flatbuffers::FlatBufferBuilder &_fbb, uint8_t groupId = 0, bool withPassword = false, - bool withLabel = false, const std::vector *label = nullptr, uint32_t slotNum = 0, uint32_t curGroupMemberNum = 0) { @@ -954,7 +1125,6 @@ inline ::flatbuffers::Offset CreateRoomGroupDirect( _fbb, groupId, withPassword, - withLabel, label__, slotNum, curGroupMemberNum); @@ -1565,16 +1735,15 @@ struct RoomGroupPasswordConfig FLATBUFFERS_FINAL_CLASS : private ::flatbuffers:: VT_GROUPID = 4, VT_WITHPASSWORD = 6 }; - const ::flatbuffers::Vector *groupId() const { - return GetPointer *>(VT_GROUPID); + uint8_t groupId() const { + return GetField(VT_GROUPID, 0); } bool withPassword() const { return GetField(VT_WITHPASSWORD, 0) != 0; } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_GROUPID) && - verifier.VerifyVector(groupId()) && + VerifyField(verifier, VT_GROUPID, 1) && VerifyField(verifier, VT_WITHPASSWORD, 1) && verifier.EndTable(); } @@ -1584,8 +1753,8 @@ struct RoomGroupPasswordConfigBuilder { typedef RoomGroupPasswordConfig Table; ::flatbuffers::FlatBufferBuilder &fbb_; ::flatbuffers::uoffset_t start_; - void add_groupId(::flatbuffers::Offset<::flatbuffers::Vector> groupId) { - fbb_.AddOffset(RoomGroupPasswordConfig::VT_GROUPID, groupId); + void add_groupId(uint8_t groupId) { + fbb_.AddElement(RoomGroupPasswordConfig::VT_GROUPID, groupId, 0); } void add_withPassword(bool withPassword) { fbb_.AddElement(RoomGroupPasswordConfig::VT_WITHPASSWORD, static_cast(withPassword), 0); @@ -1603,25 +1772,14 @@ struct RoomGroupPasswordConfigBuilder { inline ::flatbuffers::Offset CreateRoomGroupPasswordConfig( ::flatbuffers::FlatBufferBuilder &_fbb, - ::flatbuffers::Offset<::flatbuffers::Vector> groupId = 0, + uint8_t groupId = 0, bool withPassword = false) { RoomGroupPasswordConfigBuilder builder_(_fbb); - builder_.add_groupId(groupId); builder_.add_withPassword(withPassword); + builder_.add_groupId(groupId); return builder_.Finish(); } -inline ::flatbuffers::Offset CreateRoomGroupPasswordConfigDirect( - ::flatbuffers::FlatBufferBuilder &_fbb, - const std::vector *groupId = nullptr, - bool withPassword = false) { - auto groupId__ = groupId ? _fbb.CreateVector(*groupId) : 0; - return CreateRoomGroupPasswordConfig( - _fbb, - groupId__, - withPassword); -} - struct SearchRoomRequest FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef SearchRoomRequestBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -2250,6 +2408,71 @@ inline ::flatbuffers::Offset CreateJoinRoomRequestDirect( teamId); } +struct JoinRoomResponse FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef JoinRoomResponseBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ROOM_DATA = 4, + VT_SIGNALING_DATA = 6 + }; + const RoomDataInternal *room_data() const { + return GetPointer(VT_ROOM_DATA); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *signaling_data() const { + return GetPointer> *>(VT_SIGNALING_DATA); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_ROOM_DATA) && + verifier.VerifyTable(room_data()) && + VerifyOffset(verifier, VT_SIGNALING_DATA) && + verifier.VerifyVector(signaling_data()) && + verifier.VerifyVectorOfTables(signaling_data()) && + verifier.EndTable(); + } +}; + +struct JoinRoomResponseBuilder { + typedef JoinRoomResponse Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_room_data(::flatbuffers::Offset room_data) { + fbb_.AddOffset(JoinRoomResponse::VT_ROOM_DATA, room_data); + } + void add_signaling_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> signaling_data) { + fbb_.AddOffset(JoinRoomResponse::VT_SIGNALING_DATA, signaling_data); + } + explicit JoinRoomResponseBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateJoinRoomResponse( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset room_data = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> signaling_data = 0) { + JoinRoomResponseBuilder builder_(_fbb); + builder_.add_signaling_data(signaling_data); + builder_.add_room_data(room_data); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateJoinRoomResponseDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset room_data = 0, + const std::vector<::flatbuffers::Offset> *signaling_data = nullptr) { + auto signaling_data__ = signaling_data ? _fbb.CreateVector<::flatbuffers::Offset>(*signaling_data) : 0; + return CreateJoinRoomResponse( + _fbb, + room_data, + signaling_data__); +} + struct LeaveRoomRequest FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef LeaveRoomRequestBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -2995,6 +3218,69 @@ inline ::flatbuffers::Offset CreateRoomMemberUpdateInfo( return builder_.Finish(); } +struct NotificationUserJoinedRoom FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef NotificationUserJoinedRoomBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ROOM_ID = 4, + VT_UPDATE_INFO = 6, + VT_SIGNALING = 8 + }; + uint64_t room_id() const { + return GetField(VT_ROOM_ID, 0); + } + const RoomMemberUpdateInfo *update_info() const { + return GetPointer(VT_UPDATE_INFO); + } + const SignalingAddr *signaling() const { + return GetPointer(VT_SIGNALING); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ROOM_ID, 8) && + VerifyOffset(verifier, VT_UPDATE_INFO) && + verifier.VerifyTable(update_info()) && + VerifyOffset(verifier, VT_SIGNALING) && + verifier.VerifyTable(signaling()) && + verifier.EndTable(); + } +}; + +struct NotificationUserJoinedRoomBuilder { + typedef NotificationUserJoinedRoom Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_room_id(uint64_t room_id) { + fbb_.AddElement(NotificationUserJoinedRoom::VT_ROOM_ID, room_id, 0); + } + void add_update_info(::flatbuffers::Offset update_info) { + fbb_.AddOffset(NotificationUserJoinedRoom::VT_UPDATE_INFO, update_info); + } + void add_signaling(::flatbuffers::Offset signaling) { + fbb_.AddOffset(NotificationUserJoinedRoom::VT_SIGNALING, signaling); + } + explicit NotificationUserJoinedRoomBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateNotificationUserJoinedRoom( + ::flatbuffers::FlatBufferBuilder &_fbb, + uint64_t room_id = 0, + ::flatbuffers::Offset update_info = 0, + ::flatbuffers::Offset signaling = 0) { + NotificationUserJoinedRoomBuilder builder_(_fbb); + builder_.add_room_id(room_id); + builder_.add_signaling(signaling); + builder_.add_update_info(update_info); + return builder_.Finish(); +} + struct RoomUpdateInfo FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef RoomUpdateInfoBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { diff --git a/rpcs3/Emu/NP/ip_address.cpp b/rpcs3/Emu/NP/ip_address.cpp new file mode 100644 index 0000000000..705f4782e2 --- /dev/null +++ b/rpcs3/Emu/NP/ip_address.cpp @@ -0,0 +1,249 @@ +#include "stdafx.h" +#include "ip_address.h" +#include "Utilities/StrFmt.h" +#include "Emu/IdManager.h" +#include "util/endian.hpp" +#include "util/types.hpp" +#include "Emu/NP/rpcn_config.h" +#include + +#ifndef _WIN32 +#include +#include +#endif + +LOG_CHANNEL(IPv6_log, "IPv6_layer"); + +namespace np +{ + bool ip_address_translator::is_ipv6(u32 addr_be) + { + const u32 addr = std::bit_cast>(addr_be); + return (addr >= translation_range_begin && addr <= translation_range_end); + } + + u32 ip_address_translator::register_ipv6(const std::array& ipv6_addr) + { + if (ipv6_addr[10] == 0xFF && ipv6_addr[11] == 0xFF && + std::all_of(ipv6_addr.begin(), ipv6_addr.begin() + 10, [](u8 val) + { + return val == 0; + })) + { + // IPv4 over IPv6 + u32 ip_addr{}; + std::memcpy(&ip_addr, &ipv6_addr[12], sizeof(u32)); + return ip_addr; + } + + std::lock_guard lock(mutex); + + const auto it = ipv6_to_ipv4.find(ipv6_addr); + + if (it == ipv6_to_ipv4.end()) + { + ensure(cur_addr != translation_range_end); + const u32 translation_ip = cur_addr++; + const u32 translation_ip_be = std::bit_cast>(translation_ip); + ipv6_to_ipv4.emplace(ipv6_addr, translation_ip_be); + ipv4_to_ipv6.push_back(ipv6_addr); + return translation_ip_be; + } + + return it->second; + } + + sockaddr_in6 ip_address_translator::get_ipv6_sockaddr(u32 addr_be, u16 port_be) + { + const u32 addr_native = std::bit_cast>(addr_be); + const auto ipv6_addr = ::at32(ipv4_to_ipv6, addr_native - translation_range_begin); + + sockaddr_in6 sockaddr_ipv6{.sin6_family = AF_INET6, .sin6_port = port_be}; + std::memcpy(&sockaddr_ipv6.sin6_addr, ipv6_addr.data(), ipv6_addr.size()); + + return sockaddr_ipv6; + } + + u32 register_ip(const flatbuffers::Vector* vec) + { + if (vec->size() == 4) + { + const u32 ip = static_cast(vec->Get(0)) << 24 | static_cast(vec->Get(1)) << 16 | + static_cast(vec->Get(2)) << 8 | static_cast(vec->Get(3)); + + u32 result_ip = std::bit_cast>(ip); + + return result_ip; + } + else if (vec->size() == 16) + { + std::array ipv6_addr{}; + std::memcpy(ipv6_addr.data(), vec->Data(), 16); + + auto& translator = g_fxo->get(); + return translator.register_ipv6(ipv6_addr); + } + else + { + fmt::throw_exception("Received ip address with size = %d", vec->size()); + } + } + + bool is_ipv6_supported(std::optional force_state) + { + static atomic_t ipv6_status = IPV6_SUPPORT::IPV6_UNKNOWN; + + if (force_state) + ipv6_status = *force_state; + + if (ipv6_status != IPV6_SUPPORT::IPV6_UNKNOWN) + return ipv6_status == IPV6_SUPPORT::IPV6_SUPPORTED; + + static shared_mutex mtx; + std::lock_guard lock(mtx); + + if (ipv6_status != IPV6_SUPPORT::IPV6_UNKNOWN) + return ipv6_status == IPV6_SUPPORT::IPV6_SUPPORTED; + + // IPv6 feature is only used by RPCN + if (!g_cfg_rpcn.get_ipv6_support()) + { + IPv6_log.notice("is_ipv6_supported(): disabled through config"); + ipv6_status = IPV6_SUPPORT::IPV6_UNSUPPORTED; + return false; + } + + // We try to connect to ipv6.google.com:8080 + addrinfo* addr_info{}; + socket_type socket_ipv6{}; + + auto cleanup = [&]() + { + if (socket_ipv6) + close_socket(socket_ipv6); + + if (addr_info) + freeaddrinfo(addr_info); + }; + + auto error_and_disable = [&](const char* message) -> bool + { + IPv6_log.error("is_ipv6_supported(): %s", message); + ipv6_status = IPV6_SUPPORT::IPV6_UNSUPPORTED; + cleanup(); + return false; + }; + + addrinfo hints{.ai_family = AF_INET6}; + + if (getaddrinfo("ipv6.google.com", nullptr, &hints, &addr_info)) + return error_and_disable("Failed to resolve ipv6.google.com!"); + + addrinfo* found = addr_info; + + while (found != nullptr) + { + if (found->ai_family == AF_INET6) + break; + + found = found->ai_next; + } + + if (found == nullptr) + return error_and_disable("Failed to find IPv6 for ipv6.google.com"); + + socket_type socket_or_err = ::socket(AF_INET6, SOCK_STREAM, 0); + +#ifdef _WIN32 + if (socket_or_err == INVALID_SOCKET) +#else + if (socket_or_err == -1) +#endif + return error_and_disable("Failed to create IPv6 socket!"); + + socket_ipv6 = socket_or_err; + sockaddr_in6 ipv6_addr = *reinterpret_cast(found->ai_addr); + ipv6_addr.sin6_port = std::bit_cast>(443); + + if (::connect(socket_ipv6, reinterpret_cast(&ipv6_addr), sizeof(ipv6_addr)) != 0) + return error_and_disable("Failed to connect to ipv6.google.com"); + + cleanup(); + + IPv6_log.success("Successfully tested IPv6 support!"); + ipv6_status = IPV6_SUPPORT::IPV6_SUPPORTED; + return true; + } + + s32 sendto_possibly_ipv6(socket_type native_socket, const char* data, u32 size, const sockaddr_in* addr, int native_flags) + { + if (is_ipv6_supported()) + { + sockaddr_in6 addr_ipv6{}; + + if (np::ip_address_translator::is_ipv6(addr->sin_addr.s_addr)) + { + auto& translator = g_fxo->get(); + addr_ipv6 = translator.get_ipv6_sockaddr(addr->sin_addr.s_addr, addr->sin_port); + } + else + { + addr_ipv6 = sockaddr_to_sockaddr6(*addr); + } + + return ::sendto(native_socket, data, size, native_flags, reinterpret_cast(&addr_ipv6), sizeof(sockaddr_in6)); + } + + return ::sendto(native_socket, data, size, native_flags, reinterpret_cast(addr), sizeof(sockaddr_in)); + } + + sockaddr_in6 sockaddr_to_sockaddr6(const sockaddr_in& addr) + { + sockaddr_in6 addr_ipv6{.sin6_family = AF_INET6, .sin6_port = addr.sin_port}; + std::array ipv6{}; + + // IPv4 over IPv6 + ipv6[10] = 0xFF; + ipv6[11] = 0xFF; + std::memcpy(ipv6.data() + 12, &addr.sin_addr.s_addr, 4); + std::memcpy(&addr_ipv6.sin6_addr, ipv6.data(), ipv6.size()); + return addr_ipv6; + } + + sockaddr_in sockaddr6_to_sockaddr(const sockaddr_in6& addr) + { + sockaddr_in addr_ipv4{.sin_family = AF_INET, .sin_port = addr.sin6_port}; + + std::array ipv6{}; + std::memcpy(ipv6.data(), &addr.sin6_addr, 16); + auto& translator = g_fxo->get(); + addr_ipv4.sin_addr.s_addr = translator.register_ipv6(ipv6); + + return addr_ipv4; + } + + void close_socket(socket_type socket) + { + if (socket) + { +#ifdef _WIN32 + ::closesocket(socket); +#else + ::close(socket); +#endif + } + } + + void set_socket_non_blocking(socket_type socket) + { + if (socket) + { +#ifdef _WIN32 + u_long _true = 1; + ::ioctlsocket(socket, FIONBIO, &_true); +#else + ::fcntl(socket, F_SETFL, ::fcntl(socket, F_GETFL, 0) | O_NONBLOCK); +#endif + } + } +} // namespace np diff --git a/rpcs3/Emu/NP/ip_address.h b/rpcs3/Emu/NP/ip_address.h new file mode 100644 index 0000000000..3857275aac --- /dev/null +++ b/rpcs3/Emu/NP/ip_address.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +#include + +#include "util/types.hpp" +#include "Utilities/mutex.h" + +#ifdef _WIN32 +#include +#include +#else +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +#include +#include +#include +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif +#endif + +#ifdef _WIN32 +using socket_type = uptr; +#else +using socket_type = int; +#endif + +template <> +struct std::hash> +{ + std::size_t operator()(const std::array& array) const noexcept + { + std::size_t hash = 0; + std::hash hasher; + + for (auto byte : array) + { + hash ^= hasher(byte) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + } + + return hash; + } +}; + +namespace np +{ + class ip_address_translator + { + public: + static bool is_ipv6(u32 addr_be); + + u32 register_ipv6(const std::array& ipv6_addr); + sockaddr_in6 get_ipv6_sockaddr(u32 addr_be, u16 port_be); + + private: + static constexpr u32 translation_range_begin = 0x00'00'00'01; + static constexpr u32 translation_range_end = 0x00'00'FF'FF; + + shared_mutex mutex; + u32 cur_addr = translation_range_begin; + std::unordered_map, u32> ipv6_to_ipv4; + std::vector> ipv4_to_ipv6; + }; + + u32 register_ip(const flatbuffers::Vector* vec); + + enum class IPV6_SUPPORT : u8 + { + IPV6_UNKNOWN, + IPV6_UNSUPPORTED, + IPV6_SUPPORTED, + }; + + bool is_ipv6_supported(std::optional force_state = std::nullopt); + s32 sendto_possibly_ipv6(socket_type native_socket, const char* data, u32 size, const sockaddr_in* addr, int native_flags); + sockaddr_in6 sockaddr_to_sockaddr6(const sockaddr_in& addr); + sockaddr_in sockaddr6_to_sockaddr(const sockaddr_in6& addr); + + void close_socket(socket_type socket); + void set_socket_non_blocking(socket_type socket); +} // namespace np diff --git a/rpcs3/Emu/NP/np_contexts.cpp b/rpcs3/Emu/NP/np_contexts.cpp index e448a3f56c..acc934c8b9 100644 --- a/rpcs3/Emu/NP/np_contexts.cpp +++ b/rpcs3/Emu/NP/np_contexts.cpp @@ -45,11 +45,10 @@ error_code generic_async_transaction_context::wait_for_completion() return *result; } -bool generic_async_transaction_context::set_result_and_wake(error_code err) +void generic_async_transaction_context::set_result_and_wake(error_code err) { result = err; wake_cond.notify_one(); - return true; } tus_ctx::tus_ctx(vm::cptr communicationId, vm::cptr passphrase) diff --git a/rpcs3/Emu/NP/np_contexts.h b/rpcs3/Emu/NP/np_contexts.h index d82410c0a2..20e123730b 100644 --- a/rpcs3/Emu/NP/np_contexts.h +++ b/rpcs3/Emu/NP/np_contexts.h @@ -4,17 +4,13 @@ #include #include #include -#include #include "Utilities/mutex.h" - -#include "Emu/IdManager.h" #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/Modules/sceNp.h" #include "Emu/Cell/Modules/sceNp2.h" #include "Emu/Cell/Modules/sceNpCommerce2.h" #include "Emu/Cell/Modules/sceNpTus.h" -#include "Emu/NP/np_event_data.h" #include "Utilities/Thread.h" // Used By Score and Tus @@ -27,7 +23,7 @@ struct generic_async_transaction_context std::optional get_transaction_status(); void abort_transaction(); error_code wait_for_completion(); - bool set_result_and_wake(error_code err); + void set_result_and_wake(error_code err); shared_mutex mutex; std::condition_variable_any wake_cond, completion_cond; diff --git a/rpcs3/Emu/NP/np_gui_cache.h b/rpcs3/Emu/NP/np_gui_cache.h index f3c4125e2e..d742cc5749 100644 --- a/rpcs3/Emu/NP/np_gui_cache.h +++ b/rpcs3/Emu/NP/np_gui_cache.h @@ -1,12 +1,8 @@ #pragma once #include -#include - #include "Utilities/mutex.h" - #include "Emu/Cell/Modules/sceNp.h" -#include "Emu/Cell/Modules/sceNp2.h" template <> struct std::less diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index 7c9723a25f..6c2ac08b48 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -1,17 +1,18 @@ #include "stdafx.h" #include "Emu/system_config.h" #include "Emu/NP/np_handler.h" -#include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/PPUCallback.h" #include "Emu/Cell/Modules/sceNp.h" #include "Emu/Cell/Modules/sceNp2.h" #include "Emu/Cell/Modules/cellNetCtl.h" +#include "Emu/Cell/timers.hpp" #include "Utilities/StrUtil.h" #include "Emu/IdManager.h" -#include "Emu/NP/np_structs_extra.h" #include "Emu/System.h" #include "Emu/NP/rpcn_config.h" #include "Emu/NP/np_contexts.h" #include "Emu/NP/np_helpers.h" +#include "Emu/NP/signaling_handler.h" #include "Emu/RSX/Overlays/overlay_message.h" #include "Emu/Cell/lv2/sys_net/network_context.h" #include "Emu/Cell/lv2/sys_net/sys_net_helpers.h" @@ -42,7 +43,6 @@ #include #endif -#include "util/asm.hpp" #include "util/yaml.hpp" #include @@ -788,7 +788,14 @@ namespace np break; std::lock_guard lock(mutex_rpcn); - rpcn = rpcn::rpcn_client::get_instance(); + + bool was_already_started = true; + + if (!rpcn) + { + rpcn = rpcn::rpcn_client::get_instance(); + was_already_started = false; + } // Make sure we're connected @@ -808,7 +815,8 @@ namespace np return; } - rsx::overlays::queue_message(localized_string_id::RPCN_SUCCESS_LOGGED_ON); + if (!was_already_started) + rsx::overlays::queue_message(localized_string_id::RPCN_SUCCESS_LOGGED_ON); string_to_online_name(rpcn->get_online_name(), online_name); string_to_avatar_url(rpcn->get_avatar_url(), avatar_url); @@ -836,11 +844,20 @@ namespace np { np_memory.release(); + manager_cb = {}; + manager_cb_arg = {}; + basic_handler_registered = false; + room_event_cb = {}; + room_event_cb_ctx = 0; + room_event_cb_arg = {}; + room_msg_cb = {}; + room_msg_cb_ctx = 0; + room_msg_cb_arg = {}; + if (g_cfg.net.psn_status == np_psn_status::psn_rpcn) { - rpcn_log.notice("Disconnecting from RPCN!"); - std::lock_guard lock(mutex_rpcn); - rpcn.reset(); + rpcn_log.notice("Setting RPCN state to disconnected!"); + rpcn->reset_state(); } } @@ -854,10 +871,13 @@ namespace np auto& data = ::at32(match2_req_results, event_key); data.apply_relocations(dest_addr); - vm::ptr dest = vm::cast(dest_addr); + const u32 size_copied = std::min(size, data.size()); - u32 size_copied = std::min(size, data.size()); - memcpy(dest.get_ptr(), data.data(), size_copied); + if (dest_addr && size_copied) + { + vm::ptr dest = vm::cast(dest_addr); + memcpy(dest.get_ptr(), data.data(), size_copied); + } np_memory.free(data.addr()); match2_req_results.erase(event_key); @@ -1073,61 +1093,63 @@ namespace np auto replies = rpcn->get_replies(); for (auto& reply : replies) { - const u16 command = reply.second.first; + const rpcn::CommandType command = static_cast(reply.second.first); const u32 req_id = reply.first; std::vector& data = reply.second.second; // Every reply should at least contain a return value/error code ensure(data.size() >= 1); + const auto error = static_cast(data[0]); + vec_stream reply_data(data, 1); switch (command) { - case rpcn::CommandType::GetWorldList: reply_get_world_list(req_id, data); break; - case rpcn::CommandType::CreateRoom: reply_create_join_room(req_id, data); break; - case rpcn::CommandType::JoinRoom: reply_join_room(req_id, data); break; - case rpcn::CommandType::LeaveRoom: reply_leave_room(req_id, data); break; - case rpcn::CommandType::SearchRoom: reply_search_room(req_id, data); break; - case rpcn::CommandType::GetRoomDataExternalList: reply_get_roomdata_external_list(req_id, data); break; - case rpcn::CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, data); break; - case rpcn::CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, data); break; - case rpcn::CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, data); break; - case rpcn::CommandType::GetRoomMemberDataInternal: reply_get_roommemberdata_internal(req_id, data); break; - case rpcn::CommandType::SetRoomMemberDataInternal: reply_set_roommemberdata_internal(req_id, data); break; - case rpcn::CommandType::SetUserInfo: reply_set_userinfo(req_id, data); break; - case rpcn::CommandType::PingRoomOwner: reply_get_ping_info(req_id, data); break; - case rpcn::CommandType::SendRoomMessage: reply_send_room_message(req_id, data); break; - case rpcn::CommandType::RequestSignalingInfos: reply_req_sign_infos(req_id, data); break; - case rpcn::CommandType::RequestTicket: reply_req_ticket(req_id, data); break; - case rpcn::CommandType::GetBoardInfos: reply_get_board_infos(req_id, data); break; - case rpcn::CommandType::RecordScore: reply_record_score(req_id, data); break; - case rpcn::CommandType::RecordScoreData: reply_record_score_data(req_id, data); break; - case rpcn::CommandType::GetScoreData: reply_get_score_data(req_id, data); break; - case rpcn::CommandType::GetScoreRange: reply_get_score_range(req_id, data); break; - case rpcn::CommandType::GetScoreFriends: reply_get_score_friends(req_id, data); break; - case rpcn::CommandType::GetScoreNpid: reply_get_score_npid(req_id, data); break; - case rpcn::CommandType::TusSetMultiSlotVariable: reply_tus_set_multislot_variable(req_id, data); break; - case rpcn::CommandType::TusGetMultiSlotVariable: reply_tus_get_multislot_variable(req_id, data); break; - case rpcn::CommandType::TusGetMultiUserVariable: reply_tus_get_multiuser_variable(req_id, data); break; - case rpcn::CommandType::TusGetFriendsVariable: reply_tus_get_friends_variable(req_id, data); break; - case rpcn::CommandType::TusAddAndGetVariable: reply_tus_add_and_get_variable(req_id, data); break; - case rpcn::CommandType::TusTryAndSetVariable: reply_tus_try_and_set_variable(req_id, data); break; - case rpcn::CommandType::TusDeleteMultiSlotVariable: reply_tus_delete_multislot_variable(req_id, data); break; - case rpcn::CommandType::TusSetData: reply_tus_set_data(req_id, data); break; - case rpcn::CommandType::TusGetData: reply_tus_get_data(req_id, data); break; - case rpcn::CommandType::TusGetMultiSlotDataStatus: reply_tus_get_multislot_data_status(req_id, data); break; - case rpcn::CommandType::TusGetMultiUserDataStatus: reply_tus_get_multiuser_data_status(req_id, data); break; - case rpcn::CommandType::TusGetFriendsDataStatus: reply_tus_get_friends_data_status(req_id, data); break; - case rpcn::CommandType::TusDeleteMultiSlotData: reply_tus_delete_multislot_data(req_id, data); break; - case rpcn::CommandType::CreateRoomGUI: reply_create_room_gui(req_id, data); break; - case rpcn::CommandType::JoinRoomGUI: reply_join_room_gui(req_id, data); break; - case rpcn::CommandType::LeaveRoomGUI: reply_leave_room_gui(req_id, data); break; - case rpcn::CommandType::GetRoomListGUI: reply_get_room_list_gui(req_id, data); break; - case rpcn::CommandType::SetRoomSearchFlagGUI: reply_set_room_search_flag_gui(req_id, data); break; - case rpcn::CommandType::GetRoomSearchFlagGUI: reply_get_room_search_flag_gui(req_id, data); break; - case rpcn::CommandType::SetRoomInfoGUI: reply_set_room_info_gui(req_id, data); break; - case rpcn::CommandType::GetRoomInfoGUI: reply_get_room_info_gui(req_id, data); break; - case rpcn::CommandType::QuickMatchGUI: reply_quickmatch_gui(req_id, data); break; - case rpcn::CommandType::SearchJoinRoomGUI: reply_searchjoin_gui(req_id, data); break; + case rpcn::CommandType::GetWorldList: reply_get_world_list(req_id, error, reply_data); break; + case rpcn::CommandType::CreateRoom: reply_create_join_room(req_id, error, reply_data); break; + case rpcn::CommandType::JoinRoom: reply_join_room(req_id, error, reply_data); break; + case rpcn::CommandType::LeaveRoom: reply_leave_room(req_id, error, reply_data); break; + case rpcn::CommandType::SearchRoom: reply_search_room(req_id, error, reply_data); break; + case rpcn::CommandType::GetRoomDataExternalList: reply_get_roomdata_external_list(req_id, error, reply_data); break; + case rpcn::CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, error); break; + case rpcn::CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, error, reply_data); break; + case rpcn::CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, error); break; + case rpcn::CommandType::GetRoomMemberDataInternal: reply_get_roommemberdata_internal(req_id, error, reply_data); break; + case rpcn::CommandType::SetRoomMemberDataInternal: reply_set_roommemberdata_internal(req_id, error); break; + case rpcn::CommandType::SetUserInfo: reply_set_userinfo(req_id, error); break; + case rpcn::CommandType::PingRoomOwner: reply_get_ping_info(req_id, error, reply_data); break; + case rpcn::CommandType::SendRoomMessage: reply_send_room_message(req_id, error); break; + case rpcn::CommandType::RequestSignalingInfos: reply_req_sign_infos(req_id, error, reply_data); break; + case rpcn::CommandType::RequestTicket: reply_req_ticket(req_id, error, reply_data); break; + case rpcn::CommandType::GetBoardInfos: reply_get_board_infos(req_id, error, reply_data); break; + case rpcn::CommandType::RecordScore: reply_record_score(req_id, error, reply_data); break; + case rpcn::CommandType::RecordScoreData: reply_record_score_data(req_id, error); break; + case rpcn::CommandType::GetScoreData: reply_get_score_data(req_id, error, reply_data); break; + case rpcn::CommandType::GetScoreRange: reply_get_score_range(req_id, error, reply_data); break; + case rpcn::CommandType::GetScoreFriends: reply_get_score_friends(req_id, error, reply_data); break; + case rpcn::CommandType::GetScoreNpid: reply_get_score_npid(req_id, error, reply_data); break; + case rpcn::CommandType::TusSetMultiSlotVariable: reply_tus_set_multislot_variable(req_id, error); break; + case rpcn::CommandType::TusGetMultiSlotVariable: reply_tus_get_multislot_variable(req_id, error, reply_data); break; + case rpcn::CommandType::TusGetMultiUserVariable: reply_tus_get_multiuser_variable(req_id, error, reply_data); break; + case rpcn::CommandType::TusGetFriendsVariable: reply_tus_get_friends_variable(req_id, error, reply_data); break; + case rpcn::CommandType::TusAddAndGetVariable: reply_tus_add_and_get_variable(req_id, error, reply_data); break; + case rpcn::CommandType::TusTryAndSetVariable: reply_tus_try_and_set_variable(req_id, error, reply_data); break; + case rpcn::CommandType::TusDeleteMultiSlotVariable: reply_tus_delete_multislot_variable(req_id, error); break; + case rpcn::CommandType::TusSetData: reply_tus_set_data(req_id, error); break; + case rpcn::CommandType::TusGetData: reply_tus_get_data(req_id, error, reply_data); break; + case rpcn::CommandType::TusGetMultiSlotDataStatus: reply_tus_get_multislot_data_status(req_id, error, reply_data); break; + case rpcn::CommandType::TusGetMultiUserDataStatus: reply_tus_get_multiuser_data_status(req_id, error, reply_data); break; + case rpcn::CommandType::TusGetFriendsDataStatus: reply_tus_get_friends_data_status(req_id, error, reply_data); break; + case rpcn::CommandType::TusDeleteMultiSlotData: reply_tus_delete_multislot_data(req_id, error); break; + case rpcn::CommandType::CreateRoomGUI: reply_create_room_gui(req_id, error, reply_data); break; + case rpcn::CommandType::JoinRoomGUI: reply_join_room_gui(req_id, error, reply_data); break; + case rpcn::CommandType::LeaveRoomGUI: reply_leave_room_gui(req_id, error, reply_data); break; + case rpcn::CommandType::GetRoomListGUI: reply_get_room_list_gui(req_id, error, reply_data); break; + case rpcn::CommandType::SetRoomSearchFlagGUI: reply_set_room_search_flag_gui(req_id, error); break; + case rpcn::CommandType::GetRoomSearchFlagGUI: reply_get_room_search_flag_gui(req_id, error, reply_data); break; + case rpcn::CommandType::SetRoomInfoGUI: reply_set_room_info_gui(req_id, error); break; + case rpcn::CommandType::GetRoomInfoGUI: reply_get_room_info_gui(req_id, error, reply_data); break; + case rpcn::CommandType::QuickMatchGUI: reply_quickmatch_gui(req_id, error, reply_data); break; + case rpcn::CommandType::SearchJoinRoomGUI: reply_searchjoin_gui(req_id, error, reply_data); break; default: fmt::throw_exception("Unknown reply(%d) received!", command); break; } } @@ -1142,9 +1164,8 @@ namespace np case rpcn::NotificationType::RoomDestroyed: notif_room_destroyed(notif.second); break; case rpcn::NotificationType::UpdatedRoomDataInternal: notif_updated_room_data_internal(notif.second); break; case rpcn::NotificationType::UpdatedRoomMemberDataInternal: notif_updated_room_member_data_internal(notif.second); break; - case rpcn::NotificationType::SignalP2PConnect: notif_p2p_connect(notif.second); break; case rpcn::NotificationType::RoomMessageReceived: notif_room_message_received(notif.second); break; - case rpcn::NotificationType::SignalingInfo: notif_signaling_info(notif.second); break; + case rpcn::NotificationType::SignalingHelper: notif_signaling_helper(notif.second); break; case rpcn::NotificationType::MemberJoinedRoomGUI: notif_member_joined_room_gui(notif.second); break; case rpcn::NotificationType::MemberLeftRoomGUI: notif_member_left_room_gui(notif.second); break; case rpcn::NotificationType::RoomDisappearedGUI: notif_room_disappeared_gui(notif.second); break; diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h index e6524ab98a..4e0352c332 100644 --- a/rpcs3/Emu/NP/np_handler.h +++ b/rpcs3/Emu/NP/np_handler.h @@ -10,8 +10,6 @@ #include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/NP/rpcn_client.h" -#include "Emu/NP/generated/np2_structs_generated.h" -#include "Emu/NP/signaling_handler.h" #include "Emu/NP/np_allocator.h" #include "Emu/NP/np_cache.h" #include "Emu/NP/np_gui_cache.h" @@ -300,8 +298,7 @@ namespace np void notif_room_destroyed(std::vector& data); void notif_updated_room_data_internal(std::vector& data); void notif_updated_room_member_data_internal(std::vector& data); - void notif_p2p_connect(std::vector& data); - void notif_signaling_info(std::vector& data); + void notif_signaling_helper(std::vector& data); void notif_room_message_received(std::vector& data); void generic_gui_notification_handler(std::vector& data, std::string_view name, s32 notification_type); @@ -314,60 +311,60 @@ namespace np void notif_quickmatch_complete_gui(std::vector& data); // Reply handlers - bool reply_get_world_list(u32 req_id, std::vector& reply_data); - bool reply_create_join_room(u32 req_id, std::vector& reply_data); - bool reply_join_room(u32 req_id, std::vector& reply_data); - bool reply_leave_room(u32 req_id, std::vector& reply_data); - bool reply_search_room(u32 req_id, std::vector& reply_data); - bool reply_get_roomdata_external_list(u32 req_id, std::vector& reply_data); - bool reply_set_roomdata_external(u32 req_id, std::vector& reply_data); - bool reply_get_roomdata_internal(u32 req_id, std::vector& reply_data); - bool reply_set_roomdata_internal(u32 req_id, std::vector& reply_data); - bool reply_set_roommemberdata_internal(u32 req_id, std::vector& reply_data); - bool reply_get_roommemberdata_internal(u32 req_id, std::vector& reply_data); - bool reply_set_userinfo(u32 req_id, std::vector& reply_data); - bool reply_get_ping_info(u32 req_id, std::vector& reply_data); - bool reply_send_room_message(u32 req_id, std::vector& reply_data); - bool reply_req_sign_infos(u32 req_id, std::vector& reply_data); - bool reply_req_ticket(u32 req_id, std::vector& reply_data); - bool reply_get_board_infos(u32 req_id, std::vector& reply_data); - bool reply_record_score(u32 req_id, std::vector& reply_data); - bool reply_record_score_data(u32 req_id, std::vector& reply_data); - bool reply_get_score_data(u32 req_id, std::vector& reply_data); - bool reply_get_score_range(u32 req_id, std::vector& reply_data); - bool reply_get_score_friends(u32 req_id, std::vector& reply_data); - bool reply_get_score_npid(u32 req_id, std::vector& reply_data); - bool reply_tus_set_multislot_variable(u32 req_id, std::vector& reply_data); - bool reply_tus_get_multislot_variable(u32 req_id, std::vector& reply_data); - bool reply_tus_get_multiuser_variable(u32 req_id, std::vector& reply_data); - bool reply_tus_get_friends_variable(u32 req_id, std::vector& reply_data); - bool reply_tus_add_and_get_variable(u32 req_id, std::vector& reply_data); - bool reply_tus_try_and_set_variable(u32 req_id, std::vector& reply_data); - bool reply_tus_delete_multislot_variable(u32 req_id, std::vector& reply_data); - bool reply_tus_set_data(u32 req_id, std::vector& reply_data); - bool reply_tus_get_data(u32 req_id, std::vector& reply_data); - bool reply_tus_get_multislot_data_status(u32 req_id, std::vector& reply_data); - bool reply_tus_get_multiuser_data_status(u32 req_id, std::vector& reply_data); - bool reply_tus_get_friends_data_status(u32 req_id, std::vector& reply_data); - bool reply_tus_delete_multislot_data(u32 req_id, std::vector& reply_data); - bool reply_create_room_gui(u32 req_id, std::vector& reply_data); - bool reply_join_room_gui(u32 req_id, std::vector& reply_data); - bool reply_leave_room_gui(u32 req_id, std::vector& reply_data); - bool reply_get_room_list_gui(u32 req_id, std::vector& reply_data); - bool reply_set_room_search_flag_gui(u32 req_id, std::vector& reply_data); - bool reply_get_room_search_flag_gui(u32 req_id, std::vector& reply_data); - bool reply_set_room_info_gui(u32 req_id, std::vector& reply_data); - bool reply_get_room_info_gui(u32 req_id, std::vector& reply_data); - bool reply_quickmatch_gui(u32 req_id, std::vector& reply_data); - bool reply_searchjoin_gui(u32 req_id, std::vector& reply_data); + void reply_get_world_list(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_create_join_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_join_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_leave_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_search_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_get_roomdata_external_list(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_set_roomdata_external(u32 req_id, rpcn::ErrorType error); + void reply_get_roomdata_internal(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_set_roomdata_internal(u32 req_id, rpcn::ErrorType error); + void reply_set_roommemberdata_internal(u32 req_id, rpcn::ErrorType error); + void reply_get_roommemberdata_internal(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_set_userinfo(u32 req_id, rpcn::ErrorType error); + void reply_get_ping_info(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_send_room_message(u32 req_id, rpcn::ErrorType error); + void reply_req_sign_infos(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_req_ticket(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_get_board_infos(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_record_score(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_record_score_data(u32 req_id, rpcn::ErrorType error); + void reply_get_score_data(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_get_score_range(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_get_score_friends(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_get_score_npid(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_tus_set_multislot_variable(u32 req_id, rpcn::ErrorType error); + void reply_tus_get_multislot_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_tus_get_multiuser_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_tus_get_friends_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_tus_add_and_get_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_tus_try_and_set_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_tus_delete_multislot_variable(u32 req_id, rpcn::ErrorType error); + void reply_tus_set_data(u32 req_id, rpcn::ErrorType error); + void reply_tus_get_data(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_tus_get_multislot_data_status(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_tus_get_multiuser_data_status(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_tus_get_friends_data_status(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_tus_delete_multislot_data(u32 req_id, rpcn::ErrorType error); + void reply_create_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_join_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_leave_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_get_room_list_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_set_room_search_flag_gui(u32 req_id, rpcn::ErrorType error); + void reply_get_room_search_flag_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_set_room_info_gui(u32 req_id, rpcn::ErrorType error); + void reply_get_room_info_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_quickmatch_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void reply_searchjoin_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply); // Helper functions std::pair get_match2_context_options(u32 ctx_id); - bool handle_GetScoreResponse(u32 req_id, std::vector& reply_data, bool simple_result = false); - bool handle_tus_no_data(u32 req_id, std::vector& reply_data); - bool handle_TusVarResponse(u32 req_id, std::vector& reply_data); - bool handle_TusVariable(u32 req_id, std::vector& reply_data); - bool handle_TusDataStatusResponse(u32 req_id, std::vector& reply_data); + void handle_GetScoreResponse(u32 req_id, rpcn::ErrorType error, vec_stream& reply, bool simple_result = false); + void handle_tus_no_data(u32 req_id, rpcn::ErrorType error); + void handle_TusVarResponse(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void handle_TusVariable(u32 req_id, rpcn::ErrorType error, vec_stream& reply); + void handle_TusDataStatusResponse(u32 req_id, rpcn::ErrorType error, vec_stream& reply); struct callback_info { diff --git a/rpcs3/Emu/NP/np_helpers.cpp b/rpcs3/Emu/NP/np_helpers.cpp index 3576d5c683..be2618b5cf 100644 --- a/rpcs3/Emu/NP/np_helpers.cpp +++ b/rpcs3/Emu/NP/np_helpers.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" #include "util/types.hpp" #include "Utilities/StrUtil.h" -#include "np_handler.h" +#include "rpcn_client.h" #ifdef _WIN32 #include diff --git a/rpcs3/Emu/NP/np_helpers.h b/rpcs3/Emu/NP/np_helpers.h index c5375235f3..a90f5d144c 100644 --- a/rpcs3/Emu/NP/np_helpers.h +++ b/rpcs3/Emu/NP/np_helpers.h @@ -2,7 +2,6 @@ #include "util/types.hpp" #include "Emu/Cell/Modules/sceNp.h" -#include "Emu/Cell/Modules/sceNp2.h" #include "rpcn_client.h" namespace np diff --git a/rpcs3/Emu/NP/np_notifications.cpp b/rpcs3/Emu/NP/np_notifications.cpp index e6b4b547df..e051c64d65 100644 --- a/rpcs3/Emu/NP/np_notifications.cpp +++ b/rpcs3/Emu/NP/np_notifications.cpp @@ -1,11 +1,13 @@ #include "stdafx.h" -#include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/PPUCallback.h" #include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/IdManager.h" #include "Emu/NP/np_handler.h" #include "Emu/NP/np_helpers.h" #include "Emu/NP/np_structs_extra.h" #include "Emu/NP/fb_helpers.h" +#include "Emu/NP/signaling_handler.h" +#include "Emu/NP/ip_address.h" LOG_CHANNEL(rpcn_log, "rpcn"); @@ -14,8 +16,7 @@ namespace np void np_handler::notif_user_joined_room(std::vector& data) { vec_stream noti(data); - u64 room_id = noti.get(); - const auto* update_info = noti.get_flatbuffer(); + const auto* notification = noti.get_flatbuffer(); if (noti.is_error()) { @@ -23,20 +24,44 @@ namespace np return; } + ensure(notification->update_info()); + const u32 event_key = get_event_key(); - auto [include_onlinename, include_avatarurl] = get_match2_context_options(room_event_cb_ctx); + const auto [include_onlinename, include_avatarurl] = get_match2_context_options(room_event_cb_ctx); + const auto room_id = notification->room_id(); auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberUpdateInfo, sizeof(SceNpMatching2RoomMemberUpdateInfo)); auto* notif_data = reinterpret_cast(edata.data()); - RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(edata, update_info, notif_data, include_onlinename, include_avatarurl); + RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(edata, notification->update_info(), notif_data, include_onlinename, include_avatarurl); np_memory.shrink_allocation(edata.addr(), edata.size()); + // Ensures we do not call the callback if the room is not in the cache(ie we left the room already) if (!np_cache.add_member(room_id, notif_data->roomMemberDataInternal.get_ptr())) + { + get_match2_event(event_key, 0, 0); return; + } rpcn_log.notice("Received notification that user %s(%d) joined the room(%d)", notif_data->roomMemberDataInternal->userInfo.npId.handle.data, notif_data->roomMemberDataInternal->memberId, room_id); extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->roomMemberDataInternal.get_ptr()); + // We initiate signaling if necessary + if (const auto* signaling_info = notification->signaling()) + { + const u32 addr_p2p = register_ip(signaling_info->ip()); + const u16 port_p2p = signaling_info->port(); + + const u16 member_id = notif_data->roomMemberDataInternal->memberId; + const SceNpId& npid = notif_data->roomMemberDataInternal->userInfo.npId; + + rpcn_log.notice("Join notification told to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast(npid.handle.data), room_id, ip_to_string(addr_p2p), port_p2p); + + // Attempt Signaling + auto& sigh = g_fxo->get>(); + const u32 conn_id = sigh.init_sig2(npid, room_id, member_id); + sigh.start_sig(conn_id, addr_p2p, port_p2p); + } + if (room_event_cb) { sysutil_register_cb([room_event_cb = this->room_event_cb, room_id, event_key, room_event_cb_ctx = this->room_event_cb_ctx, room_event_cb_arg = this->room_event_cb_arg, size = edata.size()](ppu_thread& cb_ppu) -> s32 @@ -67,8 +92,12 @@ namespace np RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(edata, update_info, notif_data, include_onlinename, include_avatarurl); np_memory.shrink_allocation(edata.addr(), edata.size()); + // Ensures we do not call the callback if the room is not in the cache(ie we left the room already) if (!np_cache.del_member(room_id, notif_data->roomMemberDataInternal->memberId)) + { + get_match2_event(event_key, 0, 0); return; + } rpcn_log.notice("Received notification that user %s(%d) left the room(%d)", notif_data->roomMemberDataInternal->userInfo.npId.handle.data, notif_data->roomMemberDataInternal->memberId, room_id); extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->roomMemberDataInternal.get_ptr()); @@ -174,7 +203,10 @@ namespace np np_memory.shrink_allocation(edata.addr(), edata.size()); if (!np_cache.add_member(room_id, notif_data->newRoomMemberDataInternal.get_ptr())) + { + get_match2_event(event_key, 0, 0); return; + } rpcn_log.notice("Received notification that user's %s(%d) room (%d) data was updated", notif_data->newRoomMemberDataInternal->userInfo.npId.handle.data, notif_data->newRoomMemberDataInternal->memberId, room_id); extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->newRoomMemberDataInternal.get_ptr()); @@ -222,33 +254,7 @@ namespace np } } - void np_handler::notif_p2p_connect(std::vector& data) - { - vec_stream noti(data); - const u64 room_id = noti.get(); - const u16 member_id = noti.get(); - const u16 port_p2p = noti.get(); - const u32 addr_p2p = noti.get(); - - if (noti.is_error()) - { - rpcn_log.error("Received faulty SignalP2PConnect notification"); - return; - } - - auto [res, npid] = np_cache.get_npid(room_id, member_id); - if (!npid) - return; - - rpcn_log.notice("Received notification to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast((*npid).handle.data), room_id, ip_to_string(addr_p2p), port_p2p); - - // Attempt Signaling - auto& sigh = g_fxo->get>(); - const u32 conn_id = sigh.init_sig2(*npid, room_id, member_id); - sigh.start_sig(conn_id, addr_p2p, port_p2p); - } - - void np_handler::notif_signaling_info(std::vector& data) + void np_handler::notif_signaling_helper(std::vector& data) { vec_stream noti(data); const u32 addr_p2p = noti.get(); @@ -257,7 +263,7 @@ namespace np if (noti.is_error()) { - rpcn_log.error("Received faulty SignalingInfo notification"); + rpcn_log.error("Received faulty SignalingHelper notification"); return; } diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index ff124ad15d..1fc8a0c1e2 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -1,5 +1,9 @@ +#include "Emu/Cell/Modules/sceNp.h" +#include "Emu/Cell/Modules/sceNp2.h" +#include "Emu/NP/rpcn_types.h" +#include "Utilities/StrFmt.h" #include "stdafx.h" -#include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/PPUCallback.h" #include "Emu/Cell/lv2/sys_sync.h" #include "Emu/system_config.h" #include "Emu/Cell/Modules/cellSysutil.h" @@ -10,6 +14,8 @@ #include "np_helpers.h" #include "np_structs_extra.h" #include "fb_helpers.h" +#include "Emu/NP/signaling_handler.h" +#include "Emu/NP/ip_address.h" LOG_CHANNEL(rpcn_log, "rpcn"); @@ -114,14 +120,14 @@ namespace np return req_id; } - bool np_handler::reply_get_world_list(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_world_list(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; - vec_stream reply(reply_data, 1); + ensure(error == rpcn::ErrorType::NoError, "Unexpected error in GetWorldList reply"); std::vector world_list; u32 num_worlds = reply.get(); @@ -130,10 +136,7 @@ namespace np world_list.push_back(reply.get()); } - if (reply.is_error()) - { - return error_and_disconnect("Malformed reply to GetWorldList command"); - } + ensure(!reply.is_error(), "Malformed reply to GetWorldList command"); const u32 event_key = get_event_key(); @@ -152,8 +155,6 @@ namespace np np_memory.shrink_allocation(edata.addr(), edata.size()); cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); - - return true; } u32 np_handler::create_join_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2CreateJoinRoomRequest* req) @@ -174,18 +175,32 @@ namespace np return req_id; } - bool np_handler::reply_create_join_room(u32 req_id, std::vector& reply_data) + void np_handler::reply_create_join_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; + + s32 error_code = CELL_OK; + + switch (error) + { + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomGroupMaxSlotMismatch: error_code = SCE_NP_MATCHING2_SERVER_ERROR_MAX_OVER_SLOT_GROUP; break; + case rpcn::ErrorType::RoomPasswordMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_PASSWORD; break; + case rpcn::ErrorType::RoomGroupNoJoinLabel: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_JOIN_GROUP_LABEL; break; + default: fmt::throw_exception("Unexpected error in reply to CreateRoom: %d", static_cast(error)); + } + + if (error_code != CELL_OK) + { + cb_info_opt->queue_callback(req_id, 0, error_code, 0); + return; + } - vec_stream reply(reply_data, 1); const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to CreateRoom command"); + ensure(!reply.is_error(), "Malformed reply to CreateRoom command"); const u32 event_key = get_event_key(); auto [include_onlinename, include_avatarurl] = get_match2_context_options(cb_info_opt->ctx_id); @@ -202,8 +217,6 @@ namespace np extra_nps::print_SceNpMatching2CreateJoinRoomResponse(room_resp); cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); - - return true; } u32 np_handler::join_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2JoinRoomRequest* req) @@ -221,55 +234,79 @@ namespace np return req_id; } - bool np_handler::reply_join_room(u32 req_id, std::vector& reply_data) + void np_handler::reply_join_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; s32 error_code = 0; - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - switch (reply_data[0]) - { - case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; - case rpcn::ErrorType::RoomAlreadyJoined: error_code = SCE_NP_MATCHING2_SERVER_ERROR_ALREADY_JOINED; break; - case rpcn::ErrorType::RoomFull: error_code = SCE_NP_MATCHING2_SERVER_ERROR_ROOM_FULL; break; - default: return false; - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; + case rpcn::ErrorType::RoomAlreadyJoined: error_code = SCE_NP_MATCHING2_SERVER_ERROR_ALREADY_JOINED; break; + case rpcn::ErrorType::RoomFull: error_code = SCE_NP_MATCHING2_SERVER_ERROR_ROOM_FULL; break; + case rpcn::ErrorType::RoomPasswordMismatch: error_code = SCE_NP_MATCHING2_SERVER_ERROR_PASSWORD_MISMATCH; break; + case rpcn::ErrorType::RoomGroupFull: error_code = SCE_NP_MATCHING2_SERVER_ERROR_GROUP_FULL; break; + case rpcn::ErrorType::RoomGroupJoinLabelNotFound: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_GROUP; break; + default: fmt::throw_exception("Unexpected error in reply to JoinRoom: %d", static_cast(error)); ; } if (error_code != 0) { cb_info_opt->queue_callback(req_id, 0, error_code, 0); - return true; + return; } - vec_stream reply(reply_data, 1); - - const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to JoinRoom command"); + const auto* resp = reply.get_flatbuffer(); + ensure(!reply.is_error(), "Malformed reply to JoinRoom command"); + ensure(resp->room_data()); const u32 event_key = get_event_key(); - auto [include_onlinename, include_avatarurl] = get_match2_context_options(cb_info_opt->ctx_id); + const auto [include_onlinename, include_avatarurl] = get_match2_context_options(cb_info_opt->ctx_id); auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_JoinRoom, sizeof(SceNpMatching2JoinRoomResponse)); auto* room_resp = reinterpret_cast(edata.data()); auto* room_info = edata.allocate(sizeof(SceNpMatching2RoomDataInternal), room_resp->roomDataInternal); - RoomDataInternal_to_SceNpMatching2RoomDataInternal(edata, resp, room_info, npid, include_onlinename, include_avatarurl); + RoomDataInternal_to_SceNpMatching2RoomDataInternal(edata, resp->room_data(), room_info, npid, include_onlinename, include_avatarurl); np_memory.shrink_allocation(edata.addr(), edata.size()); np_cache.insert_room(room_info); extra_nps::print_SceNpMatching2RoomDataInternal(room_info); - cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); + // We initiate signaling if necessary + if (const auto* signaling_data = resp->signaling_data()) + { + const u64 room_id = resp->room_data()->roomId(); - return true; + for (unsigned int i = 0; i < signaling_data->size(); i++) + { + const auto* signaling_info = signaling_data->Get(i); + ensure(signaling_info->addr()); + + const u32 addr_p2p = register_ip(signaling_info->addr()->ip()); + const u16 port_p2p = signaling_info->addr()->port(); + + const u16 member_id = signaling_info->member_id(); + const auto [npid_res, npid_p2p] = np_cache.get_npid(room_id, member_id); + + if (npid_res != CELL_OK) + continue; + + rpcn_log.notice("JoinRoomResult told to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast(npid_p2p->handle.data), room_id, ip_to_string(addr_p2p), port_p2p); + + // Attempt Signaling + auto& sigh = g_fxo->get>(); + const u32 conn_id = sigh.init_sig2(*npid_p2p, room_id, member_id); + sigh.start_sig(conn_id, addr_p2p, port_p2p); + } + } + + cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); } u32 np_handler::leave_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2LeaveRoomRequest* req) @@ -285,24 +322,35 @@ namespace np return req_id; } - bool np_handler::reply_leave_room(u32 req_id, std::vector& reply_data) + void np_handler::reply_leave_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; + + s32 error_code = CELL_OK; + + switch (error) + { + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; + default: fmt::throw_exception("Unexpected error in reply to LeaveRoom: %d", static_cast(error)); ; + } + + if (error_code != CELL_OK) + { + cb_info_opt->queue_callback(req_id, 0, error_code, 0); + return; + } - vec_stream reply(reply_data, 1); u64 room_id = reply.get(); - if (reply.is_error()) - return error_and_disconnect("Malformed reply to LeaveRoom command"); + ensure(!reply.is_error(), "Malformed reply to LeaveRoom command"); // Disconnect all users from that room auto& sigh = g_fxo->get>(); sigh.disconnect_sig2_users(room_id); cb_info_opt->queue_callback(req_id, 0, 0, 0); - - return true; } u32 np_handler::search_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SearchRoomRequest* req) @@ -320,18 +368,17 @@ namespace np return req_id; } - bool np_handler::reply_search_room(u32 req_id, std::vector& reply_data) + void np_handler::reply_search_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; + + ensure(error == rpcn::ErrorType::NoError, "Unexpected error in SearchRoom reply"); - vec_stream reply(reply_data, 1); const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to SearchRoom command"); + ensure(!reply.is_error(), "Malformed reply to SearchRoom command"); const u32 event_key = get_event_key(); @@ -343,8 +390,6 @@ namespace np extra_nps::print_SceNpMatching2SearchRoomResponse(search_resp); cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); - - return true; } u32 np_handler::get_roomdata_external_list(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2GetRoomDataExternalListRequest* req) @@ -362,18 +407,17 @@ namespace np return req_id; } - bool np_handler::reply_get_roomdata_external_list(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_roomdata_external_list(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; + + ensure(error == rpcn::ErrorType::NoError, "Unexpected error in GetRoomDataExternalList reply"); - vec_stream reply(reply_data, 1); const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to GetRoomDataExternalList command"); + ensure(!reply.is_error(), "Malformed reply to GetRoomDataExternalList command"); const u32 event_key = get_event_key(); auto [include_onlinename, include_avatarurl] = get_match2_context_options(cb_info_opt->ctx_id); @@ -386,8 +430,6 @@ namespace np extra_nps::print_SceNpMatching2GetRoomDataExternalListResponse(sce_get_room_ext_resp); cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); - - return true; } u32 np_handler::set_roomdata_external(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetRoomDataExternalRequest* req) @@ -405,16 +447,24 @@ namespace np return req_id; } - bool np_handler::reply_set_roomdata_external(u32 req_id, std::vector& /*reply_data*/) + void np_handler::reply_set_roomdata_external(u32 req_id, rpcn::ErrorType error) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; - cb_info_opt->queue_callback(req_id, 0, 0, 0); + s32 error_code = CELL_OK; - return true; + switch (error) + { + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; + case rpcn::ErrorType::Unauthorized: error_code = SCE_NP_MATCHING2_SERVER_ERROR_FORBIDDEN; break; + default: fmt::throw_exception("Unexpected error in reply to SetRoomDataExternal: %d", static_cast(error)); + } + + cb_info_opt->queue_callback(req_id, 0, error_code, 0); } u32 np_handler::get_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2GetRoomDataInternalRequest* req) @@ -430,19 +480,30 @@ namespace np return req_id; } - bool np_handler::reply_get_roomdata_internal(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_roomdata_internal(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; - vec_stream reply(reply_data, 1); + s32 error_code = CELL_OK; + + switch (error) + { + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; + default: fmt::throw_exception("Unexpected error in reply to GetRoomDataInternal: %d", static_cast(error)); + } + + if (error_code != CELL_OK) + { + cb_info_opt->queue_callback(req_id, 0, error_code, 0); + return; + } const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to GetRoomDataInternal command"); + ensure(!reply.is_error(), "Malformed reply to GetRoomDataInternal command"); const u32 event_key = get_event_key(); auto [include_onlinename, include_avatarurl] = get_match2_context_options(cb_info_opt->ctx_id); @@ -458,8 +519,6 @@ namespace np extra_nps::print_SceNpMatching2RoomDataInternal(room_info); cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); - - return true; } u32 np_handler::set_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetRoomDataInternalRequest* req) @@ -477,16 +536,23 @@ namespace np return req_id; } - bool np_handler::reply_set_roomdata_internal(u32 req_id, std::vector& /*reply_data*/) + void np_handler::reply_set_roomdata_internal(u32 req_id, rpcn::ErrorType error) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; - cb_info_opt->queue_callback(req_id, 0, 0, 0); + s32 error_code = CELL_OK; - return true; + switch (error) + { + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; + default: fmt::throw_exception("Unexpected error in reply to GetRoomDataInternal: %d", static_cast(error)); ; + } + + cb_info_opt->queue_callback(req_id, 0, error_code, 0); } u32 np_handler::get_roommemberdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2GetRoomMemberDataInternalRequest* req) @@ -503,34 +569,31 @@ namespace np return req_id; } - bool np_handler::reply_get_roommemberdata_internal(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_roommemberdata_internal(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; - if (rpcn::is_error(static_cast(reply_data[0]))) + s32 error_code = CELL_OK; + + switch (error) { - switch (reply_data[0]) - { - case rpcn::ErrorType::NotFound: - { - rpcn_log.error("GetRoomMemberDataInternal: Room or User wasn't found"); - cb_info_opt->queue_callback(req_id, 0, -1, 0); - return true; - } - default: - return error_and_disconnect(fmt::format("GetRoomMemberDataInternal failed with unknown error(%d)!", reply_data[0])); - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; + case rpcn::ErrorType::NotFound: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_USER; break; + default: fmt::throw_exception("Unexpected error in reply to GetRoomMemberDataInternal: %d", static_cast(error)); ; } - vec_stream reply(reply_data, 1); + if (error_code != CELL_OK) + { + cb_info_opt->queue_callback(req_id, 0, error_code, 0); + return; + } const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to GetRoomMemberDataInternal command"); + ensure(!reply.is_error(), "Malformed reply to GetRoomMemberDataInternal command"); const u32 event_key = get_event_key(); auto [include_onlinename, include_avatarurl] = get_match2_context_options(cb_info_opt->ctx_id); @@ -542,7 +605,6 @@ namespace np np_memory.shrink_allocation(edata.addr(), edata.size()); cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); - return true; } u32 np_handler::set_roommemberdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetRoomMemberDataInternalRequest* req) @@ -560,16 +622,25 @@ namespace np return req_id; } - bool np_handler::reply_set_roommemberdata_internal(u32 req_id, std::vector& /*reply_data*/) + void np_handler::reply_set_roommemberdata_internal(u32 req_id, rpcn::ErrorType error) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; - cb_info_opt->queue_callback(req_id, 0, 0, 0); + s32 error_code = CELL_OK; - return true; + switch (error) + { + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; + case rpcn::ErrorType::NotFound: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_USER; break; + case rpcn::ErrorType::Unauthorized: error_code = SCE_NP_MATCHING2_SERVER_ERROR_FORBIDDEN; break; + default: fmt::throw_exception("Unexpected error in reply to SetRoomMemberDataInternal: %d", static_cast(error)); + } + + cb_info_opt->queue_callback(req_id, 0, error_code, 0); } u32 np_handler::set_userinfo(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetUserInfoRequest* req) @@ -585,21 +656,20 @@ namespace np return req_id; } - bool np_handler::reply_set_userinfo(u32 req_id, std::vector& reply_data) + void np_handler::reply_set_userinfo(u32 req_id, rpcn::ErrorType error) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - return error_and_disconnect(fmt::format("SetUserInfo failed with unknown error(%d)!", reply_data[0])); + case rpcn::ErrorType::NoError: break; + default: fmt::throw_exception("Unexpected error in reply to SetUserInfo: %d", static_cast(error)); } cb_info_opt->queue_callback(req_id, 0, 0, 0); - - return true; } u32 np_handler::get_ping_info(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SignalingGetPingInfoRequest* req) @@ -615,19 +685,30 @@ namespace np return req_id; } - bool np_handler::reply_get_ping_info(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_ping_info(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; - vec_stream reply(reply_data, 1); + s32 error_code = CELL_OK; + + switch (error) + { + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; + default: fmt::throw_exception("Unexpected error in reply to PingRoomOwner: %d", static_cast(error)); + } + + if (error_code != CELL_OK) + { + cb_info_opt->queue_callback(req_id, 0, error_code, 0); + return; + } const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to PingRoomOwner command"); + ensure(!reply.is_error(), "Malformed reply to PingRoomOwner command"); const u32 event_key = get_event_key(); @@ -636,8 +717,6 @@ namespace np GetPingInfoResponse_to_SceNpMatching2SignalingGetPingInfoResponse(resp, final_ping_resp); np_memory.shrink_allocation(edata.addr(), edata.size()); cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); - - return true; } u32 np_handler::send_room_message(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SendRoomMessageRequest* req) @@ -653,16 +732,24 @@ namespace np return req_id; } - bool np_handler::reply_send_room_message(u32 req_id, std::vector& /*reply_data*/) + void np_handler::reply_send_room_message(u32 req_id, rpcn::ErrorType error) { auto cb_info_opt = take_pending_request(req_id); if (!cb_info_opt) - return true; + return; - cb_info_opt->queue_callback(req_id, 0, 0, 0); + s32 error_code = CELL_OK; - return true; + switch (error) + { + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; + case rpcn::ErrorType::Unauthorized: error_code = SCE_NP_MATCHING2_SERVER_ERROR_FORBIDDEN; break; + default: fmt::throw_exception("Unexpected error in reply to SendRoomMessage: %d", static_cast(error)); + } + + cb_info_opt->queue_callback(req_id, 0, error_code, 0); } void np_handler::req_sign_infos(const std::string& npid, u32 conn_id) @@ -680,7 +767,7 @@ namespace np } } - bool np_handler::reply_req_sign_infos(u32 req_id, std::vector& reply_data) + void np_handler::reply_req_sign_infos(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { u32 conn_id; { @@ -689,33 +776,23 @@ namespace np pending_sign_infos_requests.erase(req_id); } - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - switch (reply_data[0]) - { - case rpcn::ErrorType::NotFound: - { - rpcn_log.error("Signaling information was requested for a user that doesn't exist or is not online"); - return true; - } - case rpcn::ErrorType::Malformed: - return error_and_disconnect("RequestSignalingInfos request was malformed!"); - default: - return error_and_disconnect(fmt::format("RequestSignalingInfos failed with unknown error(%d)!", reply_data[0])); - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::NotFound: + { + rpcn_log.error("Signaling information was requested for a user that doesn't exist or is not online"); + return; + } + default: fmt::throw_exception("Unexpected error in reply to RequestSignalingInfos: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); - const u32 addr = reply.get(); - const u16 port = reply.get(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to RequestSignalingInfos command"); + const auto* resp = reply.get_flatbuffer(); + ensure(!reply.is_error() && resp->ip(), "Malformed reply to RequestSignalingInfos command"); + u32 addr = register_ip(resp->ip()); auto& sigh = g_fxo->get>(); - sigh.start_sig(conn_id, addr, port); - - return true; + sigh.start_sig(conn_id, addr, resp->port()); } u32 np_handler::get_lobby_info_list(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2GetLobbyInfoListRequest* req) @@ -779,13 +856,11 @@ namespace np return current_ticket; } - bool np_handler::reply_req_ticket([[maybe_unused]] u32 req_id, std::vector& reply_data) + void np_handler::reply_req_ticket(u32 /* req_id */, rpcn::ErrorType error, vec_stream& reply) { - vec_stream reply(reply_data, 1); + ensure(error == rpcn::ErrorType::NoError, "Unexpected error in reply to RequestTicket"); auto ticket_raw = reply.get_rawdata(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to RequestTicket command"); + ensure(!reply.is_error(), "Malformed reply to RequestTicket command"); current_ticket = ticket(std::move(ticket_raw)); auto ticket_size = static_cast(current_ticket.size()); @@ -798,8 +873,6 @@ namespace np return 0; }); } - - return true; } void np_handler::transaction_async_handler(std::unique_lock lock, const shared_ptr& trans_ctx, u32 req_id, bool async) @@ -853,14 +926,37 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_get_board_infos(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_board_infos(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - vec_stream reply(reply_data, 1); + std::lock_guard lock_trans(mutex_async_transactions); + if (!async_transactions.count(req_id)) + { + rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); + return; + } + + auto score_trans = idm::get_unlocked(::at32(async_transactions, req_id)->idm_id); + ensure(score_trans); + + s32 error_code = CELL_OK; + + switch (error) + { + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::NotFound: error_code = SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_BOARD_MASTER_NOT_FOUND; break; + default: fmt::throw_exception("Unexpected error in reply to GetBoardInfos: %d", static_cast(error)); + } + + if (error_code != CELL_OK) + { + std::lock_guard lock(score_trans->mutex); + score_trans->result = error_code; + score_trans->wake_cond.notify_one(); + return; + } const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to GetBoardInfos command"); + ensure(!reply.is_error(), "Malformed reply to GetBoardInfos command"); const SceNpScoreBoardInfo board_info{ .rankLimit = resp->rankLimit(), @@ -870,16 +966,6 @@ namespace np .uploadSizeLimit = resp->uploadSizeLimit() }; - std::lock_guard lock_trans(mutex_async_transactions); - if (!async_transactions.count(req_id)) - { - rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); - return false; - } - - auto score_trans = idm::get_unlocked(::at32(async_transactions, req_id)->idm_id); - ensure(score_trans); - std::lock_guard lock(score_trans->mutex); const auto* tdata = std::get_if(&score_trans->tdata); @@ -888,8 +974,6 @@ namespace np memcpy(reinterpret_cast(tdata->boardInfo.get_ptr()), &board_info, sizeof(SceNpScoreBoardInfo)); score_trans->result = CELL_OK; score_trans->wake_cond.notify_one(); - - return true; } void np_handler::record_score(shared_ptr& trans_ctx, SceNpScoreBoardId boardId, SceNpScoreValue score, vm::cptr scoreComment, const u8* data, u32 data_size, vm::ptr tmpRank, bool async) @@ -911,13 +995,13 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_record_score(u32 req_id, std::vector& reply_data) + void np_handler::reply_record_score(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { std::lock_guard lock_trans(mutex_async_transactions); if (!async_transactions.count(req_id)) { rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); - return false; + return; } auto score_trans = idm::get_unlocked(::at32(async_transactions, req_id)->idm_id); @@ -925,28 +1009,20 @@ namespace np std::lock_guard lock(score_trans->mutex); - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - switch (reply_data[0]) - { - case rpcn::ErrorType::ScoreNotBest: - { - score_trans->result = SCE_NP_COMMUNITY_SERVER_ERROR_NOT_BEST_SCORE; - score_trans->wake_cond.notify_one(); - return true; - } - default: return false; - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::ScoreNotBest: + { + score_trans->result = SCE_NP_COMMUNITY_SERVER_ERROR_NOT_BEST_SCORE; + score_trans->wake_cond.notify_one(); + return; + } + default: fmt::throw_exception("Unexpected error in reply_record_score: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); auto tmp_rank = reply.get(); - - if (reply.is_error()) - { - rpcn_log.error("Error parsing response in reply_record_score"); - return false; - } + ensure(!reply.is_error(), "Error parsing response in reply_record_score"); const auto* tdata = std::get_if(&score_trans->tdata); ensure(tdata); @@ -958,7 +1034,6 @@ namespace np score_trans->result = CELL_OK; score_trans->wake_cond.notify_one(); - return true; } void np_handler::record_score_data(shared_ptr& trans_ctx, SceNpScoreBoardId boardId, SceNpScoreValue score, u32 totalSize, u32 sendSize, const u8* score_data, bool async) @@ -988,37 +1063,26 @@ namespace np } } - bool np_handler::reply_record_score_data(u32 req_id, std::vector& reply_data) + void np_handler::reply_record_score_data(u32 req_id, rpcn::ErrorType error) { std::lock_guard lock_trans(mutex_async_transactions); if (!async_transactions.count(req_id)) { rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); - return false; + return; } auto trans = ::at32(async_transactions, req_id); std::lock_guard lock(trans->mutex); - auto set_result_and_wake = [&](error_code err) -> bool + switch (error) { - trans->result = err; - trans->wake_cond.notify_one(); - return true; - }; - - if (rpcn::is_error(static_cast(reply_data[0]))) - { - switch (reply_data[0]) - { - case rpcn::ErrorType::NotFound: return trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_STORE_NOT_FOUND); - case rpcn::ErrorType::ScoreInvalid: return trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_SCORE); - case rpcn::ErrorType::ScoreHasData: return trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_GAME_DATA_ALREADY_EXISTS); - default: return false; - } + case rpcn::ErrorType::NoError: trans->set_result_and_wake(CELL_OK); break; + case rpcn::ErrorType::NotFound: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_STORE_NOT_FOUND); break; + case rpcn::ErrorType::ScoreInvalid: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_SCORE); break; + case rpcn::ErrorType::ScoreHasData: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_GAME_DATA_ALREADY_EXISTS); break; + default: fmt::throw_exception("Unexpected error in reply to RecordScoreData: %d", static_cast(error)); ; } - - return set_result_and_wake(CELL_OK); } void np_handler::get_score_data(shared_ptr& trans_ctx, SceNpScoreBoardId boardId, const SceNpId& npId, vm::ptr totalSize, u32 recvSize, vm::ptr score_data, bool async) @@ -1053,40 +1117,31 @@ namespace np trans_ctx->result = not_an_error(to_copy); } - bool np_handler::reply_get_score_data(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_score_data(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { std::lock_guard lock_trans(mutex_async_transactions); if (!async_transactions.count(req_id)) { rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); - return false; + return; } auto score_trans = idm::get_unlocked(::at32(async_transactions, req_id)->idm_id); ensure(score_trans); std::lock_guard lock(score_trans->mutex); - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - switch (reply_data[0]) - { - case rpcn::ErrorType::NotFound: return score_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_GAME_DATA_MASTER_NOT_FOUND); - default: return false; - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::NotFound: score_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_GAME_DATA_MASTER_NOT_FOUND); return; + default: fmt::throw_exception("Unexpected error in reply to GetScoreData: %d", static_cast(error)); ; } - vec_stream reply(reply_data, 1); - auto* tdata = std::get_if(&score_trans->tdata); ensure(tdata); tdata->game_data = reply.get_rawdata(); - - if (reply.is_error()) - { - rpcn_log.error("Error parsing response in reply_get_score_data"); - return false; - } + ensure(!reply.is_error(), "Error parsing response in reply_get_score_data"); tdata->game_data_size = ::size32(tdata->game_data); @@ -1095,7 +1150,7 @@ namespace np tdata->game_data.erase(tdata->game_data.begin(), tdata->game_data.begin() + to_copy); *tdata->totalSize = tdata->game_data_size; - return score_trans->set_result_and_wake(not_an_error(to_copy)); + score_trans->set_result_and_wake(not_an_error(to_copy)); } void np_handler::get_score_range(shared_ptr& trans_ctx, SceNpScoreBoardId boardId, SceNpScoreRankNumber startSerialRank, vm::ptr rankArray, u32 rankArraySize, vm::ptr commentArray, [[maybe_unused]] u32 commentArraySize, vm::ptr infoArray, u32 infoArraySize, u32 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async, bool deprecated) @@ -1143,32 +1198,27 @@ namespace np cur_rank.recordDate.tick = fb_rankdata->recordDate(); } - bool np_handler::handle_GetScoreResponse(u32 req_id, std::vector& reply_data, bool simple_result) + void np_handler::handle_GetScoreResponse(u32 req_id, rpcn::ErrorType error, vec_stream& reply, bool simple_result) { std::lock_guard lock_trans(mutex_async_transactions); if (!async_transactions.count(req_id)) { rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); - return false; + return; } auto score_trans = idm::get_unlocked(::at32(async_transactions, req_id)->idm_id); ensure(score_trans); std::lock_guard lock(score_trans->mutex); - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - return false; + case rpcn::ErrorType::NoError: break; + default: fmt::throw_exception("Unexpected error in GetScoreResponse: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - { - rpcn_log.error("Error parsing response in handle_GetScoreResponse"); - return false; - } + ensure(!reply.is_error(), "Error parsing response in handle_GetScoreResponse"); const auto* tdata = std::get_if(&score_trans->tdata); ensure(tdata); @@ -1272,12 +1322,11 @@ namespace np score_trans->result = SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND; score_trans->wake_cond.notify_one(); - return true; } - bool np_handler::reply_get_score_range(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_score_range(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_GetScoreResponse(req_id, reply_data); + handle_GetScoreResponse(req_id, error, reply); } void np_handler::get_score_friend(shared_ptr& trans_ctx, SceNpScoreBoardId boardId, bool include_self, vm::ptr rankArray, u32 rankArraySize, vm::ptr commentArray, [[maybe_unused]] u32 commentArraySize, vm::ptr infoArray, u32 infoArraySize, u32 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async, bool deprecated) @@ -1304,9 +1353,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_get_score_friends(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_score_friends(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_GetScoreResponse(req_id, reply_data); + handle_GetScoreResponse(req_id, error, reply); } void np_handler::get_score_npid(shared_ptr& trans_ctx, SceNpScoreBoardId boardId, const std::vector>& npid_vec, vm::ptr rankArray, u32 rankArraySize, vm::ptr commentArray, [[maybe_unused]] u32 commentArraySize, vm::ptr infoArray, u32 infoArraySize, u32 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async, bool deprecated) @@ -1333,76 +1382,57 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_get_score_npid(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_score_npid(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_GetScoreResponse(req_id, reply_data, true); + handle_GetScoreResponse(req_id, error, reply, true); } - bool np_handler::handle_tus_no_data(u32 req_id, std::vector& reply_data) + void np_handler::handle_tus_no_data(u32 req_id, rpcn::ErrorType error) { std::lock_guard lock_trans(mutex_async_transactions); if (!async_transactions.count(req_id)) { rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); - return false; + return; } auto trans = ::at32(async_transactions, req_id); std::lock_guard lock(trans->mutex); - auto set_result_and_wake = [&](error_code err) -> bool + switch (error) { - trans->result = err; - trans->wake_cond.notify_one(); - return true; - }; - - if (rpcn::is_error(static_cast(reply_data[0]))) - { - switch (reply_data[0]) - { - case rpcn::ErrorType::NotFound: return trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); - case rpcn::ErrorType::Unauthorized: return trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); - case rpcn::ErrorType::CondFail: return trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); - default: return false; - } + case rpcn::ErrorType::NoError: trans->set_result_and_wake(CELL_OK); break; + case rpcn::ErrorType::NotFound: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); break; + case rpcn::ErrorType::Unauthorized: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); break; + case rpcn::ErrorType::CondFail: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); break; + default: fmt::throw_exception("Unexpected error in handle_tus_no_data: %d", static_cast(error)); } - - return set_result_and_wake(CELL_OK); } - bool np_handler::handle_TusVarResponse(u32 req_id, std::vector& reply_data) + void np_handler::handle_TusVarResponse(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { std::lock_guard lock_trans(mutex_async_transactions); if (!async_transactions.count(req_id)) { rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); - return false; + return; } auto tus_trans = idm::get_unlocked(::at32(async_transactions, req_id)->idm_id); ensure(tus_trans); std::lock_guard lock(tus_trans->mutex); - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - switch (reply_data[0]) - { - case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); - case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); - case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); - default: return false; - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); + case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); + case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); + default: fmt::throw_exception("Unexpected error in handle_TusVarResponse: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - { - rpcn_log.error("Error parsing response in handle_TusVarResponse"); - return false; - } + ensure(!reply.is_error(), "Error parsing response in handle_TusVarResponse"); const auto* tdata = std::get_if(&tus_trans->tdata); ensure(tdata); @@ -1435,42 +1465,32 @@ namespace np tus_trans->result = not_an_error(fb_vars->size()); tus_trans->wake_cond.notify_one(); - - return true; } - bool np_handler::handle_TusVariable(u32 req_id, std::vector& reply_data) + void np_handler::handle_TusVariable(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { std::lock_guard lock_trans(mutex_async_transactions); if (!async_transactions.count(req_id)) { rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); - return false; + return; } auto tus_trans = idm::get_unlocked(::at32(async_transactions, req_id)->idm_id); ensure(tus_trans); std::lock_guard lock(tus_trans->mutex); - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - switch (reply_data[0]) - { - case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); - case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); - case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); - default: return false; - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); + case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); + case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); + default: fmt::throw_exception("Unexpected error in handle_TusVariable: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); const auto* fb_var = reply.get_flatbuffer(); - - if (reply.is_error()) - { - rpcn_log.error("Error parsing response in handle_TusVariable"); - return false; - } + ensure(!reply.is_error(), "Error parsing response in handle_TusVariable"); const auto* tdata = std::get_if(&tus_trans->tdata); ensure(tdata); @@ -1493,42 +1513,32 @@ namespace np tus_trans->result = CELL_OK; tus_trans->wake_cond.notify_one(); - - return true; } - bool np_handler::handle_TusDataStatusResponse(u32 req_id, std::vector& reply_data) + void np_handler::handle_TusDataStatusResponse(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { std::lock_guard lock_trans(mutex_async_transactions); if (!async_transactions.count(req_id)) { rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); - return false; + return; } auto tus_trans = idm::get_unlocked(::at32(async_transactions, req_id)->idm_id); ensure(tus_trans); std::lock_guard lock(tus_trans->mutex); - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - switch (reply_data[0]) - { - case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); - case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); - case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); - default: return false; - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); + case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); + case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); + default: fmt::throw_exception("Unexpected error in handle_TusDataStatusResponse: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - { - rpcn_log.error("Error parsing response in handle_TusDataStatusReponse"); - return false; - } + ensure(!reply.is_error(), "Error parsing response in handle_TusDataStatusReponse"); const auto* tdata = std::get_if(&tus_trans->tdata); ensure(tdata); @@ -1564,8 +1574,6 @@ namespace np tus_trans->result = not_an_error(fb_status->size()); tus_trans->wake_cond.notify_one(); - - return true; } void np_handler::tus_set_multislot_variable(shared_ptr& trans_ctx, const SceNpOnlineId& targetNpId, vm::cptr slotIdArray, vm::cptr variableArray, s32 arrayNum, bool vuser, bool async) @@ -1577,9 +1585,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_set_multislot_variable(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_set_multislot_variable(u32 req_id, rpcn::ErrorType error) { - return handle_tus_no_data(req_id, reply_data); + return handle_tus_no_data(req_id, error); } void np_handler::tus_get_multislot_variable(shared_ptr& trans_ctx, const SceNpOnlineId& targetNpId, vm::cptr slotIdArray, vm::ptr variableArray, s32 arrayNum, bool vuser, bool async) @@ -1596,9 +1604,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_get_multislot_variable(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_get_multislot_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_TusVarResponse(req_id, reply_data); + return handle_TusVarResponse(req_id, error, reply); } void np_handler::tus_get_multiuser_variable(shared_ptr& trans_ctx, std::vector targetNpIdArray, SceNpTusSlotId slotId, vm::ptr variableArray, s32 arrayNum, bool vuser, bool async) @@ -1615,9 +1623,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_get_multiuser_variable(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_get_multiuser_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_TusVarResponse(req_id, reply_data); + return handle_TusVarResponse(req_id, error, reply); } void np_handler::tus_get_friends_variable(shared_ptr& trans_ctx, SceNpTusSlotId slotId, s32 includeSelf, s32 sortType, vm::ptr variableArray,s32 arrayNum, bool async) @@ -1634,9 +1642,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_get_friends_variable(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_get_friends_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_TusVarResponse(req_id, reply_data); + return handle_TusVarResponse(req_id, error, reply); } void np_handler::tus_add_and_get_variable(shared_ptr& trans_ctx, const SceNpOnlineId& targetNpId, SceNpTusSlotId slotId, s64 inVariable, vm::ptr outVariable, vm::ptr option, bool vuser, bool async) @@ -1652,9 +1660,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_add_and_get_variable(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_add_and_get_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_TusVariable(req_id, reply_data); + return handle_TusVariable(req_id, error, reply); } void np_handler::tus_try_and_set_variable(shared_ptr& trans_ctx, const SceNpOnlineId& targetNpId, SceNpTusSlotId slotId, s32 opeType, s64 variable, vm::ptr resultVariable, vm::ptr option, bool vuser, bool async) @@ -1670,9 +1678,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_try_and_set_variable(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_try_and_set_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_TusVariable(req_id, reply_data); + return handle_TusVariable(req_id, error, reply); } void np_handler::tus_delete_multislot_variable(shared_ptr& trans_ctx, const SceNpOnlineId& targetNpId, vm::cptr slotIdArray, s32 arrayNum, bool vuser, bool async) @@ -1684,9 +1692,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_delete_multislot_variable(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_delete_multislot_variable(u32 req_id, rpcn::ErrorType error) { - return handle_tus_no_data(req_id, reply_data); + return handle_tus_no_data(req_id, error); } void np_handler::tus_set_data(shared_ptr& trans_ctx, const SceNpOnlineId& targetNpId, SceNpTusSlotId slotId, u32 totalSize, u32 sendSize, vm::cptr data, vm::cptr info, vm::ptr option, bool vuser, bool async) @@ -1718,9 +1726,9 @@ namespace np } } - bool np_handler::reply_tus_set_data(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_set_data(u32 req_id, rpcn::ErrorType error) { - return handle_tus_no_data(req_id, reply_data); + return handle_tus_no_data(req_id, error); } void np_handler::tus_get_data(shared_ptr& trans_ctx, const SceNpOnlineId& targetNpId, SceNpTusSlotId slotId, vm::ptr dataStatus, vm::ptr data, u32 recvSize, bool vuser, bool async) @@ -1753,45 +1761,38 @@ namespace np trans_ctx->result = not_an_error(to_copy); } - bool np_handler::reply_tus_get_data(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_get_data(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { std::lock_guard lock_trans(mutex_async_transactions); if (!async_transactions.count(req_id)) { rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id); - return false; + return; } auto tus_trans = idm::get_unlocked(::at32(async_transactions, req_id)->idm_id); ensure(tus_trans); std::lock_guard lock(tus_trans->mutex); - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - switch (reply_data[0]) - { - case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); - case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); - case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); - default: return false; - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::NotFound: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_USER_NOT_ASSIGNED); + case rpcn::ErrorType::Unauthorized: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_FORBIDDEN); + case rpcn::ErrorType::CondFail: return tus_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_CONDITIONS_NOT_SATISFIED); + default: fmt::throw_exception("Unexpected error in reply to TusGetData: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); const auto* fb_data = reply.get_flatbuffer(); - - if (reply.is_error()) - { - rpcn_log.error("Error parsing response in reply_tus_get_data"); - return false; - } + ensure(!reply.is_error(), "Error parsing response in reply_tus_get_data"); auto* tdata = std::get_if(&tus_trans->tdata); ensure(tdata); const auto* fb_status = fb_data->status(); ensure(fb_status && fb_status->ownerId()); - if (!fb_status) return false; // Sanity check to make compiler happy + if (!fb_status) + return; // Sanity check to make compiler happy auto* data_status = tdata->dataStatus.get_ptr(); auto* data = static_cast(tdata->data.get_ptr()); @@ -1832,7 +1833,6 @@ namespace np } tus_trans->wake_cond.notify_one(); - return true; } void np_handler::tus_get_multislot_data_status(shared_ptr& trans_ctx, const SceNpOnlineId& targetNpId, vm::cptr slotIdArray, vm::ptr statusArray, s32 arrayNum, bool vuser, bool async) @@ -1849,9 +1849,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_get_multislot_data_status(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_get_multislot_data_status(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_TusDataStatusResponse(req_id, reply_data); + return handle_TusDataStatusResponse(req_id, error, reply); } void np_handler::tus_get_multiuser_data_status(shared_ptr& trans_ctx, std::vector targetNpIdArray, SceNpTusSlotId slotId, vm::ptr statusArray, s32 arrayNum, bool vuser, bool async) @@ -1868,9 +1868,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_get_multiuser_data_status(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_get_multiuser_data_status(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_TusDataStatusResponse(req_id, reply_data); + return handle_TusDataStatusResponse(req_id, error, reply); } void np_handler::tus_get_friends_data_status(shared_ptr& trans_ctx, SceNpTusSlotId slotId, s32 includeSelf, s32 sortType, vm::ptr statusArray, s32 arrayNum, bool async) @@ -1887,9 +1887,9 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_get_friends_data_status(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_get_friends_data_status(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { - return handle_TusDataStatusResponse(req_id, reply_data); + return handle_TusDataStatusResponse(req_id, error, reply); } void np_handler::tus_delete_multislot_data(shared_ptr& trans_ctx, const SceNpOnlineId& targetNpId, vm::cptr slotIdArray, s32 arrayNum, bool vuser, bool async) @@ -1901,8 +1901,8 @@ namespace np transaction_async_handler(std::move(lock), trans_ctx, req_id, async); } - bool np_handler::reply_tus_delete_multislot_data(u32 req_id, std::vector& reply_data) + void np_handler::reply_tus_delete_multislot_data(u32 req_id, rpcn::ErrorType error) { - return handle_tus_no_data(req_id, reply_data); + return handle_tus_no_data(req_id, error); } } // namespace np diff --git a/rpcs3/Emu/NP/np_requests_gui.cpp b/rpcs3/Emu/NP/np_requests_gui.cpp index f46307b384..7782287053 100644 --- a/rpcs3/Emu/NP/np_requests_gui.cpp +++ b/rpcs3/Emu/NP/np_requests_gui.cpp @@ -1,12 +1,10 @@ +#include "Emu/NP/rpcn_types.h" #include "stdafx.h" -#include "Emu/Cell/PPUModule.h" -#include "Emu/Cell/lv2/sys_sync.h" -#include "Emu/Cell/Modules/cellSysutil.h" +#include "Emu/Cell/PPUCallback.h" #include "Emu/Memory/vm_ptr.h" #include "Emu/IdManager.h" #include "np_handler.h" #include "np_contexts.h" -#include "np_helpers.h" #include "np_structs_extra.h" #include "fb_helpers.h" @@ -159,20 +157,16 @@ namespace np return CELL_OK; } - bool np_handler::reply_create_room_gui(u32 req_id, std::vector& reply_data) + void np_handler::reply_create_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto ctx = take_pending_gui_request(req_id); if (!ctx) - return true; + return; - ensure(!rpcn::is_error(static_cast(reply_data[0])), "Unexpected error in CreateRoomGUI reply"); - - vec_stream reply(reply_data, 1); + ensure(error == rpcn::ErrorType::NoError, "Unexpected error in CreateRoomGUI reply"); const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to CreateRoomGUI command"); + ensure(!reply.is_error(), "Malformed reply to CreateRoomGUI command"); event_data edata(np_memory.allocate(MAX_SceNpMatchingJoinedRoomInfo_SIZE), sizeof(SceNpMatchingJoinedRoomInfo), MAX_SceNpMatchingJoinedRoomInfo_SIZE); auto* room_info = reinterpret_cast(edata.data()); @@ -185,8 +179,6 @@ namespace np set_gui_result(SCE_NP_MATCHING_GUI_EVENT_CREATE_ROOM, std::move(edata)); ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_CREATE_ROOM, 0); gui_epilog(ctx); - - return true; } error_code np_handler::join_room_gui(u32 ctx_id, vm::ptr roomid, vm::ptr handler, vm::ptr arg) @@ -204,44 +196,42 @@ namespace np return CELL_OK; } - bool np_handler::reply_join_room_gui(u32 req_id, std::vector& reply_data) + void np_handler::reply_join_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto ctx = take_pending_gui_request(req_id); if (!ctx) - return true; + return; - if (rpcn::is_error(static_cast(reply_data[0]))) + s32 error_code = CELL_OK; + + switch (error) { - s32 error = -1; - - switch (static_cast(reply_data[0])) - { - case rpcn::ErrorType::RoomMissing: - error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; - break; - case rpcn::ErrorType::RoomFull: - // Might also be SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN or SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED ? - error = SCE_NP_MATCHING_SERVER_ERROR_ROOM_CLOSED; - break; - case rpcn::ErrorType::RoomAlreadyJoined: - error = SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN; - break; - default: - fmt::throw_exception("Unexpected error in JoinRoomGUI reply: %d", reply_data[0]); - break; - } - - ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, error); - gui_epilog(ctx); - return true; + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: + error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; + break; + case rpcn::ErrorType::RoomFull: + // Might also be SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN or SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED ? + error_code = SCE_NP_MATCHING_SERVER_ERROR_ROOM_CLOSED; + break; + case rpcn::ErrorType::RoomAlreadyJoined: + error_code = SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN; + break; + default: + fmt::throw_exception("Unexpected error in JoinRoomGUI reply: %d", static_cast(error)); + break; } - vec_stream reply(reply_data, 1); - const auto* resp = reply.get_flatbuffer(); + if (error_code != 0) + { + ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, error_code); + gui_epilog(ctx); + return; + } - if (reply.is_error()) - return error_and_disconnect("Malformed reply to JoinRoomGUI command"); + const auto* resp = reply.get_flatbuffer(); + ensure(!reply.is_error(), "Malformed reply to JoinRoomGUI command"); event_data edata(np_memory.allocate(MAX_SceNpMatchingJoinedRoomInfo_SIZE), sizeof(SceNpMatchingJoinedRoomInfo), MAX_SceNpMatchingJoinedRoomInfo_SIZE); auto* room_info = reinterpret_cast(edata.data()); @@ -260,8 +250,6 @@ namespace np set_gui_result(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, std::move(edata)); ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, 0); gui_epilog(ctx); - - return true; } error_code np_handler::leave_room_gui(u32 ctx_id, vm::cptr roomid) @@ -281,36 +269,30 @@ namespace np return not_an_error(req_id); } - bool np_handler::reply_leave_room_gui(u32 req_id, std::vector& reply_data) + void np_handler::reply_leave_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto ctx = take_pending_gui_request(req_id); if (!ctx) - return true; + return; - if (rpcn::is_error(static_cast(reply_data[0]))) + s32 error_code = CELL_OK; + + switch (error) { - s32 error = -1; - - switch (static_cast(reply_data[0])) - { - case rpcn::ErrorType::NotFound: - error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; - break; - default: - fmt::throw_exception("Unexpected error in LeaveRoomGUI reply: %d", reply_data[0]); - break; - } - - ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, error); - return true; + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; break; + default: fmt::throw_exception("Unexpected error in LeaveRoomGUI reply: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); - const auto* resp = reply.get_flatbuffer(); + if (error_code != CELL_OK) + { + ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, error_code); + return; + } - if (reply.is_error()) - return error_and_disconnect("Malformed reply to LeaveRoomGUI command"); + const auto* resp = reply.get_flatbuffer(); + ensure(!reply.is_error(), "Malformed reply to LeaveRoomGUI command"); event_data edata(np_memory.allocate(MAX_SceNpMatchingRoomStatus_SIZE), sizeof(SceNpMatchingRoomStatus), MAX_SceNpMatchingRoomStatus_SIZE); auto* room_status = reinterpret_cast(edata.data()); @@ -323,7 +305,6 @@ namespace np gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, .edata = std::move(edata)}); ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, 0); - return true; } error_code np_handler::get_room_list_gui(u32 ctx_id, vm::cptr communicationId, vm::ptr range, vm::ptr cond, vm::ptr attr, vm::ptr handler, vm::ptr arg, bool limit) @@ -354,20 +335,16 @@ namespace np return CELL_OK; } - bool np_handler::reply_get_room_list_gui(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_room_list_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto ctx = take_pending_gui_request(req_id); if (!ctx) - return true; + return; - ensure(!rpcn::is_error(static_cast(reply_data[0])), "Unexpected error in GetRoomListGUI reply"); - - vec_stream reply(reply_data, 1); + ensure(error == rpcn::ErrorType::NoError, "Unexpected error in GetRoomListGUI reply"); const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to GetRoomListGUI command"); + ensure(!reply.is_error(), "Malformed reply to GetRoomListGUI command"); event_data edata(np_memory.allocate(MAX_SceNpMatchingRoomList_SIZE), sizeof(SceNpMatchingRoomList), MAX_SceNpMatchingRoomList_SIZE); auto* room_list = reinterpret_cast(edata.data()); @@ -388,7 +365,6 @@ namespace np } gui_epilog(ctx); - return true; } error_code np_handler::set_room_search_flag_gui(u32 ctx_id, vm::ptr /* lobby_id */, vm::ptr room_id, s32 flag) @@ -408,33 +384,24 @@ namespace np return not_an_error(req_id); } - bool np_handler::reply_set_room_search_flag_gui(u32 req_id, std::vector& reply_data) + void np_handler::reply_set_room_search_flag_gui(u32 req_id, rpcn::ErrorType error) { auto ctx = take_pending_gui_request(req_id); if (!ctx) - return true; + return; - s32 error = 0; + s32 error_code = CELL_OK; - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - switch (static_cast(reply_data[0])) - { - case rpcn::ErrorType::NotFound: - error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; - break; - case rpcn::ErrorType::Unauthorized: - error = SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED; - break; - default: - fmt::throw_exception("Unexpected error in SetRoomSearchFlagGUI reply: %d", reply_data[0]); - break; - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; break; + case rpcn::ErrorType::Unauthorized: error_code = SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED; break; + default: fmt::throw_exception("Unexpected error in SetRoomSearchFlagGUI reply: %d", static_cast(error)); } - ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_SET_ROOM_SEARCH_FLAG_DONE, error); - return true; + ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_SET_ROOM_SEARCH_FLAG_DONE, error_code); } error_code np_handler::get_room_search_flag_gui(u32 ctx_id, vm::ptr /* lobby_id */, vm::ptr room_id) @@ -454,36 +421,30 @@ namespace np return not_an_error(req_id); } - bool np_handler::reply_get_room_search_flag_gui(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_room_search_flag_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto ctx = take_pending_gui_request(req_id); if (!ctx) - return true; + return; - if (rpcn::is_error(static_cast(reply_data[0]))) + s32 error_code = CELL_OK; + + switch (error) { - s32 error = -1; - - switch (static_cast(reply_data[0])) - { - case rpcn::ErrorType::NotFound: - error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; - break; - default: - fmt::throw_exception("Unexpected error in GetRoomSearchFlagGUI reply: %d", reply_data[0]); - break; - } - - ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, error); - return true; + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; break; + default: fmt::throw_exception("Unexpected error in GetRoomSearchFlagGUI reply: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); - const auto* resp = reply.get_flatbuffer(); + if (error_code != CELL_OK) + { + ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, error_code); + return; + } - if (reply.is_error()) - return error_and_disconnect("Malformed reply to GetRoomSearchFlagGUI command"); + const auto* resp = reply.get_flatbuffer(); + ensure(!reply.is_error(), "Malformed reply to GetRoomSearchFlagGUI command"); event_data edata(np_memory.allocate(MAX_SceNpMatchingRoom_SIZE), sizeof(SceNpMatchingRoom), MAX_SceNpMatchingRoom_SIZE); auto* room_info = reinterpret_cast(edata.data()); @@ -494,7 +455,6 @@ namespace np gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, .edata = std::move(edata)}); ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, 0); - return true; } error_code np_handler::set_room_info_gui(u32 ctx_id, vm::ptr /* lobby_id */, vm::ptr room_id, vm::ptr attr) @@ -519,33 +479,24 @@ namespace np return not_an_error(req_id); } - bool np_handler::reply_set_room_info_gui(u32 req_id, std::vector& reply_data) + void np_handler::reply_set_room_info_gui(u32 req_id, rpcn::ErrorType error) { auto ctx = take_pending_gui_request(req_id); if (!ctx) - return true; + return; - s32 error = 0; + s32 error_code = CELL_OK; - if (rpcn::is_error(static_cast(reply_data[0]))) + switch (error) { - switch (static_cast(reply_data[0])) - { - case rpcn::ErrorType::NotFound: - error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; - break; - case rpcn::ErrorType::Unauthorized: - error = SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED; - break; - default: - fmt::throw_exception("Unexpected error in SetRoomInfoGUI reply: %d", reply_data[0]); - break; - } + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; break; + case rpcn::ErrorType::Unauthorized: error_code = SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED; break; + default: fmt::throw_exception("Unexpected error in SetRoomInfoGUI reply: %d", static_cast(error)); } - ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_SET_ROOM_INFO_DONE, error); - return true; + ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_SET_ROOM_INFO_DONE, error_code); } error_code np_handler::get_room_info_gui(u32 ctx_id, vm::ptr /* lobby_id */, vm::ptr room_id, vm::ptr attr) @@ -565,36 +516,30 @@ namespace np return not_an_error(req_id); } - bool np_handler::reply_get_room_info_gui(u32 req_id, std::vector& reply_data) + void np_handler::reply_get_room_info_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto ctx = take_pending_gui_request(req_id); if (!ctx) - return true; + return; - if (rpcn::is_error(static_cast(reply_data[0]))) + s32 error_code = CELL_OK; + + switch (error) { - s32 error = -1; - - switch (static_cast(reply_data[0])) - { - case rpcn::ErrorType::NotFound: - error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; - break; - default: - fmt::throw_exception("Unexpected error in GetRoomInfoGUI reply: %d", reply_data[0]); - break; - } - - ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, error); - return true; + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; break; + default: fmt::throw_exception("Unexpected error in GetRoomInfoGUI reply: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); - const auto* resp = reply.get_flatbuffer(); + if (error_code != CELL_OK) + { + ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, error_code); + return; + } - if (reply.is_error()) - return error_and_disconnect("Malformed reply to GetRoomInfoGUI command"); + const auto* resp = reply.get_flatbuffer(); + ensure(!reply.is_error(), "Malformed reply to GetRoomInfoGUI command"); event_data edata(np_memory.allocate(MAX_SceNpMatchingRoom_SIZE), sizeof(SceNpMatchingRoom), MAX_SceNpMatchingRoom_SIZE); auto* room_info = reinterpret_cast(edata.data()); @@ -605,7 +550,6 @@ namespace np gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, .edata = std::move(edata)}); ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, 0); - return true; } error_code np_handler::quickmatch_gui(u32 ctx_id, vm::cptr communicationId, vm::cptr cond, s32 available_num, s32 timeout, vm::ptr handler, vm::ptr arg) @@ -624,20 +568,16 @@ namespace np return CELL_OK; } - bool np_handler::reply_quickmatch_gui(u32 req_id, std::vector& reply_data) + void np_handler::reply_quickmatch_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto ctx = take_pending_gui_request(req_id); if (!ctx) - return true; + return; - ensure(!rpcn::is_error(static_cast(reply_data[0])), "Unexpected error in QuickMatchGUI reply"); - - vec_stream reply(reply_data, 1); + ensure(error == rpcn::ErrorType::NoError, "Unexpected error in QuickMatchGUI reply"); const auto* resp = reply.get_flatbuffer(); - - if (reply.is_error()) - return error_and_disconnect("Malformed reply to QuickMatchGUI command"); + ensure(!reply.is_error(), "Malformed reply to QuickMatchGUI command"); SceNpRoomId room_id{}; ensure(resp->id() && resp->id()->size() == sizeof(SceNpRoomId::opt)); @@ -678,7 +618,6 @@ namespace np ctx->wakey = 0; auto& thread = *ctx->thread; thread(room_id); - return true; } error_code np_handler::searchjoin_gui(u32 ctx_id, vm::cptr communicationId, vm::cptr cond, vm::cptr attr, vm::ptr handler, vm::ptr arg) @@ -695,37 +634,31 @@ namespace np return CELL_OK; } - bool np_handler::reply_searchjoin_gui(u32 req_id, std::vector& reply_data) + void np_handler::reply_searchjoin_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply) { auto ctx = take_pending_gui_request(req_id); if (!ctx) - return true; + return; - if (rpcn::is_error(static_cast(reply_data[0]))) + s32 error_code = CELL_OK; + + switch (error) { - s32 error = -1; - - switch (static_cast(reply_data[0])) - { - case rpcn::ErrorType::NotFound: - error = SCE_NP_MATCHING_ERROR_SEARCH_JOIN_ROOM_NOT_FOUND; - break; - default: - fmt::throw_exception("Unexpected error in SearchJoinRoomGUI reply: %d", reply_data[0]); - break; - } - - ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, error); - gui_epilog(ctx); - return true; + case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::NotFound: error_code = SCE_NP_MATCHING_ERROR_SEARCH_JOIN_ROOM_NOT_FOUND; break; + default: fmt::throw_exception("Unexpected error in SearchJoinRoomGUI reply: %d", static_cast(error)); } - vec_stream reply(reply_data, 1); - const auto* resp = reply.get_flatbuffer(); + if (error_code != CELL_OK) + { + ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, error_code); + gui_epilog(ctx); + return; + } - if (reply.is_error()) - return error_and_disconnect("Malformed reply to SearchJoinRoomGUI command"); + const auto* resp = reply.get_flatbuffer(); + ensure(!reply.is_error(), "Malformed reply to SearchJoinRoomGUI command"); event_data edata(np_memory.allocate(MAX_SceNpMatchingSearchJoinRoomInfo_SIZE), sizeof(SceNpMatchingSearchJoinRoomInfo), MAX_SceNpMatchingSearchJoinRoomInfo_SIZE); auto* room_info = reinterpret_cast(edata.data()); @@ -744,8 +677,6 @@ namespace np set_gui_result(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, std::move(edata)); ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, 0); gui_epilog(ctx); - - return true; } // Local cache requests diff --git a/rpcs3/Emu/NP/np_structs_extra.cpp b/rpcs3/Emu/NP/np_structs_extra.cpp index 66e7a342f7..2acba3d045 100644 --- a/rpcs3/Emu/NP/np_structs_extra.cpp +++ b/rpcs3/Emu/NP/np_structs_extra.cpp @@ -1,5 +1,5 @@ +#include "Utilities/StrUtil.h" #include "stdafx.h" -#include "Emu/System.h" #include #include "np_structs_extra.h" @@ -118,9 +118,13 @@ namespace extra_nps print_SceNpMatching2BinAttr(&req->roomBinAttrExternal[i]); sceNp2.warning("roomPassword: *0x%x", req->roomPassword); + + if (req->roomPassword) + sceNp2.warning("data: %s", fmt::buf_to_hexstring(req->roomPassword->data, sizeof(req->roomPassword->data))); + sceNp2.warning("groupConfig: *0x%x", req->groupConfig); sceNp2.warning("groupConfigNum: %d", req->groupConfigNum); - sceNp2.warning("passwordSlotMask: *0x%x", req->passwordSlotMask); + sceNp2.warning("passwordSlotMask: *0x%x, value: 0x%x", req->passwordSlotMask, req->passwordSlotMask ? static_cast(*req->passwordSlotMask) : 0ull); sceNp2.warning("allowedUser: *0x%x", req->allowedUser); sceNp2.warning("allowedUserNum: %d", req->allowedUserNum); sceNp2.warning("blockedUser: *0x%x", req->blockedUser); diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index dc46881b22..0f0c9d871c 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -1,14 +1,14 @@ +#include "Emu/Cell/lv2/sys_net/sys_net_helpers.h" +#include "Emu/NP/ip_address.h" #include "stdafx.h" #include #include #include #include -#include #include "rpcn_client.h" -#include "np_structs_extra.h" #include "Utilities/StrUtil.h" +#include "Utilities/StrFmt.h" #include "Utilities/Thread.h" -#include "Emu/IdManager.h" #include "Emu/System.h" #include "Emu/NP/rpcn_config.h" #include "Emu/NP/np_helpers.h" @@ -16,8 +16,6 @@ #include "Emu/system_config.h" #include "Emu/RSX/Overlays/overlay_message.h" -#include "util/asm.hpp" - #include "generated/np2_structs_generated.h" #ifdef _WIN32 @@ -28,7 +26,6 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" #endif -#include #include #include #include @@ -48,6 +45,160 @@ LOG_CHANNEL(rpcn_log, "rpcn"); int get_native_error(); +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto value) + { + switch (value) + { + case rpcn::ErrorType::NoError: return "NoError"; + case rpcn::ErrorType::Malformed: return "Malformed"; + case rpcn::ErrorType::Invalid: return "Invalid"; + case rpcn::ErrorType::InvalidInput: return "InvalidInput"; + case rpcn::ErrorType::TooSoon: return "TooSoon"; + case rpcn::ErrorType::LoginError: return "LoginError"; + case rpcn::ErrorType::LoginAlreadyLoggedIn: return "LoginAlreadyLoggedIn"; + case rpcn::ErrorType::LoginInvalidUsername: return "LoginInvalidUsername"; + case rpcn::ErrorType::LoginInvalidPassword: return "LoginInvalidPassword"; + case rpcn::ErrorType::LoginInvalidToken: return "LoginInvalidToken"; + case rpcn::ErrorType::CreationError: return "CreationError"; + case rpcn::ErrorType::CreationExistingUsername: return "CreationExistingUsername"; + case rpcn::ErrorType::CreationBannedEmailProvider: return "CreationBannedEmailProvider"; + case rpcn::ErrorType::CreationExistingEmail: return "CreationExistingEmail"; + case rpcn::ErrorType::RoomMissing: return "RoomMissing"; + case rpcn::ErrorType::RoomAlreadyJoined: return "RoomAlreadyJoined"; + case rpcn::ErrorType::RoomFull: return "RoomFull"; + case rpcn::ErrorType::RoomPasswordMismatch: return "RoomPasswordMismatch"; + case rpcn::ErrorType::RoomPasswordMissing: return "RoomPasswordMissing"; + case rpcn::ErrorType::RoomGroupNoJoinLabel: return "RoomGroupNoJoinLabel"; + case rpcn::ErrorType::RoomGroupFull: return "RoomGroupFull"; + case rpcn::ErrorType::RoomGroupJoinLabelNotFound: return "RoomGroupJoinLabelNotFound"; + case rpcn::ErrorType::RoomGroupMaxSlotMismatch: return "RoomGroupMaxSlotMismatch"; + case rpcn::ErrorType::Unauthorized: return "Unauthorized"; + case rpcn::ErrorType::DbFail: return "DbFail"; + case rpcn::ErrorType::EmailFail: return "EmailFail"; + case rpcn::ErrorType::NotFound: return "NotFound"; + case rpcn::ErrorType::Blocked: return "Blocked"; + case rpcn::ErrorType::AlreadyFriend: return "AlreadyFriend"; + case rpcn::ErrorType::ScoreNotBest: return "ScoreNotBest"; + case rpcn::ErrorType::ScoreInvalid: return "ScoreInvalid"; + case rpcn::ErrorType::ScoreHasData: return "ScoreHasData"; + case rpcn::ErrorType::CondFail: return "CondFail"; + case rpcn::ErrorType::Unsupported: return "Unsupported"; + default: break; + } + + return unknown; + }); +} + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto value) + { + switch (value) + { + case rpcn::CommandType::Login: return "Login"; + case rpcn::CommandType::Terminate: return "Terminate"; + case rpcn::CommandType::Create: return "Create"; + case rpcn::CommandType::SendToken: return "SendToken"; + case rpcn::CommandType::SendResetToken: return "SendResetToken"; + case rpcn::CommandType::ResetPassword: return "ResetPassword"; + case rpcn::CommandType::ResetState: return "ResetState"; + case rpcn::CommandType::AddFriend: return "AddFriend"; + case rpcn::CommandType::RemoveFriend: return "RemoveFriend"; + case rpcn::CommandType::AddBlock: return "AddBlock"; + case rpcn::CommandType::RemoveBlock: return "RemoveBlock"; + case rpcn::CommandType::GetServerList: return "GetServerList"; + case rpcn::CommandType::GetWorldList: return "GetWorldList"; + case rpcn::CommandType::CreateRoom: return "CreateRoom"; + case rpcn::CommandType::JoinRoom: return "JoinRoom"; + case rpcn::CommandType::LeaveRoom: return "LeaveRoom"; + case rpcn::CommandType::SearchRoom: return "SearchRoom"; + case rpcn::CommandType::GetRoomDataExternalList: return "GetRoomDataExternalList"; + case rpcn::CommandType::SetRoomDataExternal: return "SetRoomDataExternal"; + case rpcn::CommandType::GetRoomDataInternal: return "GetRoomDataInternal"; + case rpcn::CommandType::SetRoomDataInternal: return "SetRoomDataInternal"; + case rpcn::CommandType::GetRoomMemberDataInternal: return "GetRoomMemberDataInternal"; + case rpcn::CommandType::SetRoomMemberDataInternal: return "SetRoomMemberDataInternal"; + case rpcn::CommandType::SetUserInfo: return "SetUserInfo"; + case rpcn::CommandType::PingRoomOwner: return "PingRoomOwner"; + case rpcn::CommandType::SendRoomMessage: return "SendRoomMessage"; + case rpcn::CommandType::RequestSignalingInfos: return "RequestSignalingInfos"; + case rpcn::CommandType::RequestTicket: return "RequestTicket"; + case rpcn::CommandType::SendMessage: return "SendMessage"; + case rpcn::CommandType::GetBoardInfos: return "GetBoardInfos"; + case rpcn::CommandType::RecordScore: return "RecordScore"; + case rpcn::CommandType::RecordScoreData: return "RecordScoreData"; + case rpcn::CommandType::GetScoreData: return "GetScoreData"; + case rpcn::CommandType::GetScoreRange: return "GetScoreRange"; + case rpcn::CommandType::GetScoreFriends: return "GetScoreFriends"; + case rpcn::CommandType::GetScoreNpid: return "GetScoreNpid"; + case rpcn::CommandType::GetNetworkTime: return "GetNetworkTime"; + case rpcn::CommandType::TusSetMultiSlotVariable: return "TusSetMultiSlotVariable"; + case rpcn::CommandType::TusGetMultiSlotVariable: return "TusGetMultiSlotVariable"; + case rpcn::CommandType::TusGetMultiUserVariable: return "TusGetMultiUserVariable"; + case rpcn::CommandType::TusGetFriendsVariable: return "TusGetFriendsVariable"; + case rpcn::CommandType::TusAddAndGetVariable: return "TusAddAndGetVariable"; + case rpcn::CommandType::TusTryAndSetVariable: return "TusTryAndSetVariable"; + case rpcn::CommandType::TusDeleteMultiSlotVariable: return "TusDeleteMultiSlotVariable"; + case rpcn::CommandType::TusSetData: return "TusSetData"; + case rpcn::CommandType::TusGetData: return "TusGetData"; + case rpcn::CommandType::TusGetMultiSlotDataStatus: return "TusGetMultiSlotDataStatus"; + case rpcn::CommandType::TusGetMultiUserDataStatus: return "TusGetMultiUserDataStatus"; + case rpcn::CommandType::TusGetFriendsDataStatus: return "TusGetFriendsDataStatus"; + case rpcn::CommandType::TusDeleteMultiSlotData: return "TusDeleteMultiSlotData"; + case rpcn::CommandType::SetPresence: return "SetPresence"; + case rpcn::CommandType::CreateRoomGUI: return "CreateRoomGUI"; + case rpcn::CommandType::JoinRoomGUI: return "JoinRoomGUI"; + case rpcn::CommandType::LeaveRoomGUI: return "LeaveRoomGUI"; + case rpcn::CommandType::GetRoomListGUI: return "GetRoomListGUI"; + case rpcn::CommandType::SetRoomSearchFlagGUI: return "SetRoomSearchFlagGUI"; + case rpcn::CommandType::GetRoomSearchFlagGUI: return "GetRoomSearchFlagGUI"; + case rpcn::CommandType::SetRoomInfoGUI: return "SetRoomInfoGUI"; + case rpcn::CommandType::GetRoomInfoGUI: return "GetRoomInfoGUI"; + case rpcn::CommandType::QuickMatchGUI: return "QuickMatchGUI"; + case rpcn::CommandType::SearchJoinRoomGUI: return "SearchJoinRoomGUI"; + } + + return unknown; + }); +} + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto value) + { + switch (value) + { + case rpcn::NotificationType::UserJoinedRoom: return "UserJoinedRoom"; + case rpcn::NotificationType::UserLeftRoom: return "UserLeftRoom"; + case rpcn::NotificationType::RoomDestroyed: return "RoomDestroyed"; + case rpcn::NotificationType::UpdatedRoomDataInternal: return "UpdatedRoomDataInternal"; + case rpcn::NotificationType::UpdatedRoomMemberDataInternal: return "UpdatedRoomMemberDataInternal"; + case rpcn::NotificationType::FriendQuery: return "FriendQuery"; + case rpcn::NotificationType::FriendNew: return "FriendNew"; + case rpcn::NotificationType::FriendLost: return "FriendLost"; + case rpcn::NotificationType::FriendStatus: return "FriendStatus"; + case rpcn::NotificationType::RoomMessageReceived: return "RoomMessageReceived"; + case rpcn::NotificationType::MessageReceived: return "MessageReceived"; + case rpcn::NotificationType::FriendPresenceChanged: return "FriendPresenceChanged"; + case rpcn::NotificationType::SignalingHelper: return "SignalingHelper"; + case rpcn::NotificationType::MemberJoinedRoomGUI: return "MemberJoinedRoomGUI"; + case rpcn::NotificationType::MemberLeftRoomGUI: return "MemberLeftRoomGUI"; + case rpcn::NotificationType::RoomDisappearedGUI: return "RoomDisappearedGUI"; + case rpcn::NotificationType::RoomOwnerChangedGUI: return "RoomOwnerChangedGUI"; + case rpcn::NotificationType::UserKickedGUI: return "UserKickedGUI"; + case rpcn::NotificationType::QuickMatchCompleteGUI: return "QuickMatchCompleteGUI"; + } + + return unknown; + }); +} + void vec_stream::dump() const { rpcn_log.error("vec_stream dump:\n%s", fmt::buf_to_hexstring(vec.data(), vec.size())); @@ -105,51 +256,64 @@ namespace rpcn rpcn_log.notice("online: %s, pr_com_id: %s, pr_title: %s, pr_status: %s, pr_comment: %s, pr_data: %s", online ? "true" : "false", pr_com_id.data, pr_title, pr_status, pr_comment, fmt::buf_to_hexstring(pr_data.data(), pr_data.size())); } - constexpr u32 RPCN_PROTOCOL_VERSION = 25; - constexpr usz RPCN_HEADER_SIZE = 15; + constexpr u32 RPCN_PROTOCOL_VERSION = 26; + constexpr usz RPCN_HEADER_SIZE = 15; - bool is_error(ErrorType err) + const char* error_to_explanation(rpcn::ErrorType error) { - if (err >= ErrorType::__error_last) + switch (error) { - rpcn_log.error("Invalid error returned!"); - return true; + case rpcn::ErrorType::NoError: return "No error"; + case rpcn::ErrorType::Malformed: return "Sent packet was malformed!"; + case rpcn::ErrorType::Invalid: return "Sent command was invalid!"; + case rpcn::ErrorType::InvalidInput: return "Sent data was invalid!"; + case rpcn::ErrorType::TooSoon: return "Request happened too soon!"; + case rpcn::ErrorType::LoginError: return "Unknown login error!"; + case rpcn::ErrorType::LoginAlreadyLoggedIn: return "User is already logged in!"; + case rpcn::ErrorType::LoginInvalidUsername: return "Login error: invalid username!"; + case rpcn::ErrorType::LoginInvalidPassword: return "Login error: invalid password!"; + case rpcn::ErrorType::LoginInvalidToken: return "Login error: invalid token!"; + case rpcn::ErrorType::CreationError: return "Error creating an account!"; + case rpcn::ErrorType::CreationExistingUsername: return "Error creating an account: existing username!"; + case rpcn::ErrorType::CreationBannedEmailProvider: return "Error creating an account: banned email provider!"; + case rpcn::ErrorType::CreationExistingEmail: return "Error creating an account: an account with that email already exist!"; + case rpcn::ErrorType::RoomMissing: return "User tried to join a non-existent room!"; + case rpcn::ErrorType::RoomAlreadyJoined: return "User has already joined!"; + case rpcn::ErrorType::RoomFull: return "User tried to join a full room!"; + case rpcn::ErrorType::RoomPasswordMismatch: return "Room password used was invalid"; + case rpcn::ErrorType::RoomPasswordMissing: return "Room password was missing during room creation"; + case rpcn::ErrorType::RoomGroupNoJoinLabel: return "Tried to join a group room without a label"; + case rpcn::ErrorType::RoomGroupFull: return "Room group is full"; + case rpcn::ErrorType::RoomGroupJoinLabelNotFound: return "Join label was invalid"; + case rpcn::ErrorType::RoomGroupMaxSlotMismatch: return "Mismatch between max_slot and the listed slots in groups"; + case rpcn::ErrorType::Unauthorized: return "User attempted an unauthorized operation!"; + case rpcn::ErrorType::DbFail: return "A db query failed on the server!"; + case rpcn::ErrorType::EmailFail: return "An email action failed on the server!"; + case rpcn::ErrorType::NotFound: return "A request replied not found!"; + case rpcn::ErrorType::Blocked: return "You're blocked!"; + case rpcn::ErrorType::AlreadyFriend: return "You're already friends!"; + case rpcn::ErrorType::ScoreNotBest: return "Attempted to register a score that is not better!"; + case rpcn::ErrorType::ScoreInvalid: return "Score for player was found but wasn't what was expected!"; + case rpcn::ErrorType::ScoreHasData: return "Score already has game data associated with it!"; + case rpcn::ErrorType::CondFail: return "Condition related to the query failed!"; + case rpcn::ErrorType::Unsupported: return "An unsupported operation was attempted!"; } - switch (err) - { - case NoError: return false; - case Malformed: rpcn_log.error("Sent packet was malformed!"); break; - case Invalid: rpcn_log.error("Sent command was invalid!"); break; - case InvalidInput: rpcn_log.error("Sent data was invalid!"); break; - case TooSoon: rpcn_log.error("Request happened too soon!"); break; - case LoginError: rpcn_log.error("Unknown login error!"); break; - case LoginAlreadyLoggedIn: rpcn_log.error("User is already logged in!"); break; - case LoginInvalidUsername: rpcn_log.error("Login error: invalid username!"); break; - case LoginInvalidPassword: rpcn_log.error("Login error: invalid password!"); break; - case LoginInvalidToken: rpcn_log.error("Login error: invalid token!"); break; - case CreationError: rpcn_log.error("Error creating an account!"); break; - case CreationExistingUsername: rpcn_log.error("Error creating an account: existing username!"); break; - case CreationBannedEmailProvider: rpcn_log.error("Error creating an account: banned email provider!"); break; - case CreationExistingEmail: rpcn_log.error("Error creating an account: an account with that email already exist!"); break; - case RoomMissing: rpcn_log.error("User tried to join a non-existent room!"); break; - case RoomAlreadyJoined: rpcn_log.error("User has already joined!"); break; - case RoomFull: rpcn_log.error("User tried to join a full room!"); break; - case Unauthorized: rpcn_log.error("User attempted an unauthorized operation!"); break; - case DbFail: rpcn_log.error("A db query failed on the server!"); break; - case EmailFail: rpcn_log.error("An email action failed on the server!"); break; - case NotFound: rpcn_log.error("A request replied not found!"); break; - case Blocked: rpcn_log.error("You're blocked!"); break; - case AlreadyFriend: rpcn_log.error("You're already friends!"); break; - case ScoreNotBest: rpcn_log.error("Attempted to register a score that is not better!"); break; - case ScoreInvalid: rpcn_log.error("Score for player was found but wasn't what was expected!"); break; - case ScoreHasData: rpcn_log.error("Score already has game data associated with it!"); break; - case CondFail: rpcn_log.error("Condition related to the query failed!"); break; - case Unsupported: rpcn_log.error("An unsupported operation was attempted!"); break; - default: rpcn_log.fatal("Unhandled ErrorType reached the switch?"); break; - } + fmt::throw_exception("Unknown error returned: %d", static_cast(error)); + } - return true; + void print_error(rpcn::CommandType command, rpcn::ErrorType error) + { + const std::string error_message = fmt::format("command: %s result: %s, explanation: %s", command, error, error_to_explanation(error)); + + if (error == rpcn::ErrorType::NoError) + { + rpcn_log.trace("%s", error_message); + } + else + { + rpcn_log.warning("%s", error_message); + } } // Constructor, destructor & singleton manager @@ -273,6 +437,10 @@ namespace rpcn { thread_base::set_name("RPCN Client"); + // UDP Signaling related + steady_clock::time_point last_ping_time_ipv4{}, last_pong_time_ipv4{}; + steady_clock::time_point last_ping_time_ipv6{}, last_pong_time_ipv6{}; + while (true) { sem_rpcn.acquire(); @@ -327,16 +495,15 @@ namespace rpcn if (authentified && !Emu.IsStopped()) { // Ping the UDP Signaling Server if we're authentified & ingame - auto now = steady_clock::now(); - - auto rpcn_msgs = get_rpcn_msgs(); + const auto now = steady_clock::now(); + const auto rpcn_msgs = get_rpcn_msgs(); for (const auto& msg : rpcn_msgs) { if (msg.size() == 6) { const u32 new_addr_sig = read_from_ptr>(&msg[0]); - const u32 new_port_sig = read_from_ptr>(&msg[4]); + const u16 new_port_sig = read_from_ptr>(&msg[4]); const u32 old_addr_sig = addr_sig; const u32 old_port_sig = port_sig; @@ -358,7 +525,16 @@ namespace rpcn } } - last_pong_time = now; + last_pong_time_ipv4 = now; + } + else if (msg.size() == 18) + { + // We don't really need ipv6 info stored so we just update the pong data + // std::array new_ipv6_addr; + // std::memcpy(new_ipv6_addr.data(), &msg[3], 16); + // const u32 new_ipv6_port = read_from_ptr>(&msg[16]); + + last_pong_time_ipv6 = now; } else { @@ -366,36 +542,64 @@ namespace rpcn } } - // Send a packet every 5 seconds and then every 500 ms until reply is received - if (now - last_pong_time >= 5s && now - last_ping_time > 500ms) + const std::chrono::nanoseconds time_since_last_ipv4_ping = now - last_ping_time_ipv4; + const std::chrono::nanoseconds time_since_last_ipv4_pong = now - last_pong_time_ipv4; + const std::chrono::nanoseconds time_since_last_ipv6_ping = now - last_ping_time_ipv6; + const std::chrono::nanoseconds time_since_last_ipv6_pong = now - last_pong_time_ipv6; + + auto forge_ping_packet = [&]() -> std::vector { std::vector ping(13); - ping[0] = 1; + ping[0] = 1; write_to_ptr>(ping, 1, user_id); write_to_ptr>(ping, 9, +local_addr_sig); - if (send_packet_from_p2p_port(ping, addr_rpcn_udp) == -1) - { - rpcn_log.error("Failed to send ping to RPCN!"); - } - last_ping_time = now; - } - else + return ping; + }; + + // Send a packet every 5 seconds and then every 500 ms until reply is received + if (time_since_last_ipv4_pong >= 5s && time_since_last_ipv4_ping > 500ms) + { + const auto ping = forge_ping_packet(); + + if (!send_packet_from_p2p_port_ipv4(ping, addr_rpcn_udp_ipv4)) + rpcn_log.error("Failed to send IPv4 ping to RPCN!"); + + last_ping_time_ipv4 = now; + continue; + } + + if (np::is_ipv6_supported() && time_since_last_ipv6_pong >= 5s && time_since_last_ipv6_ping > 500ms) + { + const auto ping = forge_ping_packet(); + + if (!send_packet_from_p2p_port_ipv6(ping, addr_rpcn_udp_ipv6)) + rpcn_log.error("Failed to send IPv6 ping to RPCN!"); + + last_ping_time_ipv6 = now; + continue; + } + + auto min_duration_for = [&](const auto last_ping_time, const auto last_pong_time) -> std::chrono::nanoseconds { - std::chrono::nanoseconds duration; if ((now - last_pong_time) < 5s) { - duration = 5s - (now - last_pong_time); + return (5s - (now - last_pong_time)); } else { - duration = 500ms - (now - last_ping_time); + return (500ms - (now - last_ping_time)); } + }; - if (!sem_rpcn.try_acquire_for(duration)) - { - // TODO - } + auto duration = min_duration_for(last_ping_time_ipv4, last_pong_time_ipv4); + + if (np::is_ipv6_supported()) + { + const auto duration_ipv6 = min_duration_for(last_ping_time_ipv6, last_pong_time_ipv6); + duration = std::min(duration, duration_ipv6); } + + sem_rpcn.try_acquire_for(duration); } } } @@ -416,10 +620,10 @@ namespace rpcn case recvn_result::recvn_terminate: return error_and_disconnect_notice("Recvn was forcefully aborted"); } - const u8 packet_type = header[0]; - const u16 command = read_from_ptr>(&header[1]); + const u8 packet_type = header[0]; + const auto command = static_cast(static_cast(read_from_ptr>(&header[1]))); const u32 packet_size = read_from_ptr>(&header[3]); - const u64 packet_id = read_from_ptr>(&header[7]); + const u64 packet_id = read_from_ptr>(&header[7]); if (packet_size < RPCN_HEADER_SIZE) return error_and_disconnect("Invalid packet size"); @@ -441,6 +645,13 @@ namespace rpcn if (data.empty()) return error_and_disconnect("Reply packet without result"); + // Internal commands without feedback + if (command == CommandType::ResetState) + { + ensure(data[0] == static_cast(ErrorType::NoError)); + break; + } + // Those commands are handled synchronously and won't be forwarded to NP Handler if (command == CommandType::Login || command == CommandType::GetServerList || command == CommandType::Create || command == CommandType::AddFriend || command == CommandType::RemoveFriend || @@ -469,7 +680,9 @@ namespace rpcn } case PacketType::Notification: { - switch (command) + auto notif_type = static_cast(command); + + switch (notif_type) { case NotificationType::FriendNew: case NotificationType::FriendLost: @@ -477,7 +690,7 @@ namespace rpcn case NotificationType::FriendStatus: case NotificationType::FriendPresenceChanged: { - handle_friend_notification(command, std::move(data)); + handle_friend_notification(notif_type, std::move(data)); break; } case NotificationType::MessageReceived: @@ -488,7 +701,7 @@ namespace rpcn default: { std::lock_guard lock(mutex_notifs); - notifications.emplace_back(std::make_pair(command, std::move(data))); + notifications.emplace_back(std::make_pair(notif_type, std::move(data))); break; } } @@ -499,7 +712,7 @@ namespace rpcn if (data.size() != 4) return error_and_disconnect("Invalid size of ServerInfo packet"); - received_version = reinterpret_cast&>(data[0]); + received_version = reinterpret_cast&>(data[0]); server_info_received = true; break; } @@ -629,7 +842,7 @@ namespace rpcn bool rpcn_client::send_packet(const std::vector& packet) { u32 num_timeouts = 0; - usz n_sent = 0; + usz n_sent = 0; while (n_sent != packet.size()) { if (terminate) @@ -688,7 +901,7 @@ namespace rpcn // Helper functions - bool rpcn_client::forge_send(u16 command, u64 packet_id, const std::vector& data) + bool rpcn_client::forge_send(rpcn::CommandType command, u64 packet_id, const std::vector& data) { // TODO: add a check for status? @@ -697,7 +910,7 @@ namespace rpcn return true; } - bool rpcn_client::forge_send_reply(u16 command, u64 packet_id, const std::vector& data, std::vector& reply_data) + bool rpcn_client::forge_send_reply(rpcn::CommandType command, u64 packet_id, const std::vector& data, std::vector& reply_data) { if (!forge_send(command, packet_id, data)) return false; @@ -744,8 +957,8 @@ namespace rpcn sockfd = 0; } - connected = false; - authentified = false; + connected = false; + authentified = false; server_info_received = false; } @@ -817,28 +1030,63 @@ namespace rpcn memset(&addr_rpcn, 0, sizeof(addr_rpcn)); - addr_rpcn.sin_port = std::bit_cast>(port); // htons + addr_rpcn.sin_port = std::bit_cast>(port); // htons addr_rpcn.sin_family = AF_INET; -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4996) -#endif - hostent* host_addr = gethostbyname(splithost[0].c_str()); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - if (!host_addr) + addrinfo* addr_info{}; + + if (getaddrinfo(splithost[0].c_str(), nullptr, nullptr, &addr_info)) { - rpcn_log.error("connect: Failed to resolve %s", host); + rpcn_log.error("connect: Failed to getaddrinfo %s", host); state = rpcn_state::failure_resolve; return false; } - addr_rpcn.sin_addr.s_addr = *reinterpret_cast(host_addr->h_addr_list[0]); + bool found_ipv4 = false, found_ipv6 = false; + addrinfo* found = addr_info; - memcpy(&addr_rpcn_udp, &addr_rpcn, sizeof(addr_rpcn_udp)); - addr_rpcn_udp.sin_port = std::bit_cast>(3657); // htons + while (found != nullptr) + { + switch (found->ai_family) + { + case AF_INET: + { + addr_rpcn.sin_addr = reinterpret_cast(found->ai_addr)->sin_addr; + found_ipv4 = true; + break; + } + case AF_INET6: + { + if (np::is_ipv6_supported()) + { + addr_rpcn_udp_ipv6.sin6_family = AF_INET6; + addr_rpcn_udp_ipv6.sin6_port = std::bit_cast>(3657); + addr_rpcn_udp_ipv6.sin6_addr = reinterpret_cast(found->ai_addr)->sin6_addr; + found_ipv6 = true; + } + break; + } + default: break; + } + + found = found->ai_next; + } + + if (!found_ipv4) + { + rpcn_log.error("connect: Failed to find IPv4 for %s", host); + state = rpcn_state::failure_resolve; + return false; + } + + if (np::is_ipv6_supported() && !found_ipv6) + { + rpcn_log.warning("IPv6 seems supported but no IPv6 could be found for the RPCN server, IPv6 is disabled!"); + is_ipv6_supported(np::IPV6_SUPPORT::IPV6_UNSUPPORTED); + } + + memcpy(&addr_rpcn_udp_ipv4, &addr_rpcn, sizeof(addr_rpcn_udp_ipv4)); + addr_rpcn_udp_ipv4.sin_port = std::bit_cast>(3657); // htons sockfd = socket(AF_INET, SOCK_STREAM, 0); #ifdef _WIN32 @@ -932,10 +1180,6 @@ namespace rpcn } rpcn_log.notice("connect: Protocol version matches"); - - last_ping_time = steady_clock::now() - 5s; - last_pong_time = last_ping_time; - return true; } @@ -974,10 +1218,10 @@ namespace rpcn } vec_stream reply(packet_data); - auto error = static_cast(reply.get()); + auto error = static_cast(reply.get()); online_name = reply.get_string(false); - avatar_url = reply.get_string(false); - user_id = reply.get(); + avatar_url = reply.get_string(false); + user_id = reply.get(); auto get_usernames_and_status = [](vec_stream& stream, std::map& friends) { @@ -1022,15 +1266,15 @@ namespace rpcn get_usernames(reply, friend_infos.blocked); } - if (is_error(error)) + if (error != rpcn::ErrorType::NoError) { switch (error) { - case LoginError: state = rpcn_state::failure_id; break; - case LoginAlreadyLoggedIn: state = rpcn_state::failure_id_already_logged_in; break; - case LoginInvalidUsername: state = rpcn_state::failure_id_username; break; - case LoginInvalidPassword: state = rpcn_state::failure_id_password; break; - case LoginInvalidToken: state = rpcn_state::failure_id_token; break; + case rpcn::ErrorType::LoginError: state = rpcn_state::failure_id; break; + case rpcn::ErrorType::LoginAlreadyLoggedIn: state = rpcn_state::failure_id_already_logged_in; break; + case rpcn::ErrorType::LoginInvalidUsername: state = rpcn_state::failure_id_username; break; + case rpcn::ErrorType::LoginInvalidPassword: state = rpcn_state::failure_id_password; break; + case rpcn::ErrorType::LoginInvalidToken: state = rpcn_state::failure_id_token; break; default: state = rpcn_state::failure_id; break; } @@ -1062,6 +1306,15 @@ namespace rpcn return true; } + void rpcn_client::reset_state() + { + if (!connected || !authentified) + return; + + std::vector data; + forge_send(CommandType::ResetState, rpcn_request_counter.fetch_add(1), data); + } + ErrorType rpcn_client::create_user(std::string_view npid, std::string_view password, std::string_view online_name, std::string_view avatar_url, std::string_view email) { std::vector data; @@ -1088,14 +1341,12 @@ namespace rpcn vec_stream reply(packet_data); auto error = static_cast(reply.get()); - if (is_error(error)) + if (error == rpcn::ErrorType::NoError) { - return error; + rpcn_log.success("You have successfully created a RPCN account(%s | %s)!", npid, online_name); } - rpcn_log.success("You have successfully created a RPCN account(%s | %s)!", npid, online_name); - - return ErrorType::NoError; + return error; } ErrorType rpcn_client::resend_token(const std::string& npid, const std::string& password) @@ -1123,14 +1374,12 @@ namespace rpcn vec_stream reply(packet_data); auto error = static_cast(reply.get()); - if (is_error(error)) + if (error == rpcn::ErrorType::NoError) { - return error; + rpcn_log.success("Token has successfully been resent!"); } - rpcn_log.success("Token has successfully been resent!"); - - return ErrorType::NoError; + return error; } ErrorType rpcn_client::send_reset_token(std::string_view npid, std::string_view email) @@ -1158,14 +1407,12 @@ namespace rpcn vec_stream reply(packet_data); auto error = static_cast(reply.get()); - if (is_error(error)) + if (error == rpcn::ErrorType::NoError) { - return error; + rpcn_log.success("Password reset token has successfully been sent!"); } - rpcn_log.success("Password reset token has successfully been sent!"); - - return ErrorType::NoError; + return error; } ErrorType rpcn_client::reset_password(std::string_view npid, std::string_view token, std::string_view password) @@ -1195,14 +1442,12 @@ namespace rpcn vec_stream reply(packet_data); auto error = static_cast(reply.get()); - if (is_error(error)) + if (error == rpcn::ErrorType::NoError) { - return error; + rpcn_log.success("Password has successfully been reset!"); } - rpcn_log.success("Password has successfully been reset!"); - - return ErrorType::NoError; + return error; } bool rpcn_client::add_friend(const std::string& friend_username) @@ -1222,7 +1467,7 @@ namespace rpcn vec_stream reply(packet_data); auto error = static_cast(reply.get()); - if (is_error(error)) + if (error != rpcn::ErrorType::NoError) { return false; } @@ -1248,7 +1493,7 @@ namespace rpcn vec_stream reply(packet_data); auto error = static_cast(reply.get()); - if (is_error(error)) + if (error != rpcn::ErrorType::NoError) { return false; } @@ -1257,18 +1502,18 @@ namespace rpcn return true; } - std::vector>> rpcn_client::get_notifications() + std::vector>> rpcn_client::get_notifications() { std::lock_guard lock(mutex_notifs); - std::vector>> notifs = std::move(notifications); + auto notifs = std::move(notifications); notifications.clear(); return notifs; } - std::unordered_map>> rpcn_client::get_replies() + std::unordered_map>> rpcn_client::get_replies() { std::lock_guard lock(mutex_replies); - std::unordered_map>> ret_replies = std::move(replies); + auto ret_replies = std::move(replies); replies.clear(); return ret_replies; } @@ -1337,7 +1582,7 @@ namespace rpcn vec_stream reply(reply_data); auto error = static_cast(reply.get()); - if (is_error(error)) + if (error != rpcn::ErrorType::NoError) { return false; } @@ -1370,7 +1615,7 @@ namespace rpcn vec_stream reply(reply_data); auto error = static_cast(reply.get()); - if (is_error(error)) + if (error != rpcn::ErrorType::NoError) { return 0; } @@ -1386,7 +1631,6 @@ namespace rpcn return network_time; } - bool rpcn_client::get_world_list(u32 req_id, const SceNpCommunicationId& communication_id, u16 server_id) { std::vector data(COMMUNICATION_ID_SIZE + sizeof(u16)); @@ -1453,7 +1697,7 @@ namespace rpcn std::vector> davec; for (u32 i = 0; i < req->groupConfigNum; i++) { - auto bin = CreateGroupConfig(builder, req->groupConfig[i].slotNum, req->groupConfig[i].withLabel, builder.CreateVector(req->groupConfig[i].label.data, 8), req->groupConfig[i].withPassword); + auto bin = CreateGroupConfig(builder, req->groupConfig[i].slotNum, req->groupConfig[i].withLabel ? builder.CreateVector(req->groupConfig[i].label.data, 8) : 0, req->groupConfig[i].withPassword); davec.push_back(bin); } final_groupconfigs_vec = builder.CreateVector(davec); @@ -1555,7 +1799,7 @@ namespace rpcn { flatbuffers::FlatBufferBuilder builder(1024); flatbuffers::Offset final_optdata = CreatePresenceOptionData(builder, builder.CreateVector(req->optData.data, 16), req->optData.length); - auto req_finished = CreateLeaveRoomRequest(builder, req->roomId, final_optdata); + auto req_finished = CreateLeaveRoomRequest(builder, req->roomId, final_optdata); builder.Finish(req_finished); return forge_request_with_com_id(builder, communication_id, CommandType::LeaveRoom, req_id); @@ -1571,7 +1815,7 @@ namespace rpcn for (u32 i = 0; i < req->intFilterNum; i++) { auto int_attr = CreateIntAttr(builder, req->intFilter[i].attr.id, req->intFilter[i].attr.num); - auto bin = CreateIntSearchFilter(builder, req->intFilter[i].searchOperator, int_attr); + auto bin = CreateIntSearchFilter(builder, req->intFilter[i].searchOperator, int_attr); davec.push_back(bin); } final_intfilter_vec = builder.CreateVector(davec); @@ -1583,7 +1827,7 @@ namespace rpcn for (u32 i = 0; i < req->binFilterNum; i++) { auto bin_attr = CreateBinAttr(builder, req->binFilter[i].attr.id, builder.CreateVector(req->binFilter[i].attr.ptr.get_ptr(), req->binFilter[i].attr.size)); - auto bin = CreateBinSearchFilter(builder, req->binFilter[i].searchOperator, bin_attr); + auto bin = CreateBinSearchFilter(builder, req->binFilter[i].searchOperator, bin_attr); davec.push_back(bin); } final_binfilter_vec = builder.CreateVector(davec); @@ -1898,7 +2142,7 @@ namespace rpcn auto fb_sendmessage = CreateSendMessageRequest(builder, nested_flatbuffer_vector, npids_vector); builder.Finish(fb_sendmessage); - const u8* buf = builder.GetBufferPointer(); + const u8* buf = builder.GetBufferPointer(); const usz bufsize = builder.GetSize(); std::vector data(bufsize + sizeof(u32)); @@ -1969,7 +2213,7 @@ namespace rpcn auto req_finished = CreateRecordScoreGameDataRequest(builder, board_id, pc_id, score); builder.Finish(req_finished); - const u8* buf = builder.GetBufferPointer(); + const u8* buf = builder.GetBufferPointer(); const usz bufsize = builder.GetSize(); std::vector data(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize + sizeof(u32) + score_data.size()); @@ -2391,11 +2635,7 @@ namespace rpcn num = cur_attr->value.num; break; } - default: - { - fmt::throw_exception("Invalid attr type reached set_room_info_gui"); - break; - } + default: fmt::throw_exception("Invalid attr type reached set_room_info_gui"); } auto fb_attr = CreateMatchingAttr(builder, cur_attr->type, cur_attr->id, num, fb_vec_data); @@ -2474,7 +2714,7 @@ namespace rpcn bool rpcn_client::forge_request_with_com_id(const flatbuffers::FlatBufferBuilder& builder, const SceNpCommunicationId& com_id, CommandType command, u64 packet_id) { - const u8* buf = builder.GetBufferPointer(); + const u8* buf = builder.GetBufferPointer(); const usz bufsize = builder.GetSize(); std::vector data(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize); @@ -2498,13 +2738,13 @@ namespace rpcn return forge_send(command, packet_id, data); } - std::vector rpcn_client::forge_request(u16 command, u64 packet_id, const std::vector& data) const + std::vector rpcn_client::forge_request(rpcn::CommandType command, u64 packet_id, const std::vector& data) const { const usz packet_size = data.size() + RPCN_HEADER_SIZE; std::vector packet(packet_size); - packet[0] = PacketType::Request; - reinterpret_cast&>(packet[1]) = command; + packet[0] = static_cast(PacketType::Request); + reinterpret_cast&>(packet[1]) = static_cast(command); reinterpret_cast&>(packet[3]) = ::narrow(packet_size); reinterpret_cast&>(packet[7]) = packet_id; @@ -2607,11 +2847,10 @@ namespace rpcn } } - void rpcn_client::handle_friend_notification(u16 command, std::vector data) + void rpcn_client::handle_friend_notification(rpcn::NotificationType ntype, std::vector data) { std::lock_guard lock(mutex_friends); - NotificationType ntype = static_cast(command); vec_stream vdata(data); const auto call_callbacks = [&](NotificationType ntype, const std::string& username, bool status) @@ -2672,7 +2911,7 @@ namespace rpcn case NotificationType::FriendStatus: // Set status of friend to Offline or Online { const bool online = !!vdata.get(); - const u64 timestamp = vdata.get(); + const u64 timestamp = vdata.get(); const std::string username = vdata.get_string(false); if (vdata.is_error()) { @@ -2746,7 +2985,7 @@ namespace rpcn // Unserialize the message vec_stream sdata(data); std::string sender = sdata.get_string(false); - auto* fb_mdata = sdata.get_flatbuffer(); + auto* fb_mdata = sdata.get_flatbuffer(); if (sdata.is_error()) { @@ -2761,12 +3000,12 @@ namespace rpcn } message_data mdata = { - .msgId = message_counter, - .mainType = fb_mdata->mainType(), - .subType = fb_mdata->subType(), + .msgId = message_counter, + .mainType = fb_mdata->mainType(), + .subType = fb_mdata->subType(), .msgFeatures = fb_mdata->msgFeatures(), - .subject = fb_mdata->subject()->str(), - .body = fb_mdata->body()->str()}; + .subject = fb_mdata->subject()->str(), + .body = fb_mdata->body()->str()}; strcpy_trunc(mdata.commId.data, fb_mdata->communicationId()->str()); mdata.data.assign(fb_mdata->data()->Data(), fb_mdata->data()->Data() + fb_mdata->data()->size()); @@ -2775,7 +3014,7 @@ namespace rpcn { std::lock_guard lock(mutex_messages); const u64 msg_id = message_counter++; - auto id_and_msg = stx::make_shared>(std::make_pair(std::move(sender), std::move(mdata))); + auto id_and_msg = stx::make_shared>(std::make_pair(std::move(sender), std::move(mdata))); messages.emplace(msg_id, id_and_msg); new_messages.push_back(msg_id); active_messages.insert(msg_id); @@ -2811,7 +3050,7 @@ namespace rpcn for (auto id : active_messages) { const auto& entry = ::at32(messages, id); - const auto& msg = entry->second; + const auto& msg = entry->second; if (msg.mainType == type_filter && (include_bootable || !(msg.msgFeatures & SCE_NP_BASIC_MESSAGE_FEATURES_BOOTABLE))) { vec_messages.push_back(std::make_pair(id, entry)); @@ -2819,9 +3058,9 @@ namespace rpcn } message_cbs.insert(message_cb_t{ - .cb_func = cb_func, - .cb_param = cb_param, - .type_filter = type_filter, + .cb_func = cb_func, + .cb_param = cb_param, + .type_filter = type_filter, .inc_bootable = include_bootable, }); } diff --git a/rpcs3/Emu/NP/rpcn_client.h b/rpcs3/Emu/NP/rpcn_client.h index fb8921076a..2ada34b581 100644 --- a/rpcs3/Emu/NP/rpcn_client.h +++ b/rpcs3/Emu/NP/rpcn_client.h @@ -5,9 +5,7 @@ #include #include #include "Utilities/mutex.h" -#include "Emu/localized_string.h" - -#include "util/asm.hpp" +#include "Emu/localized_string_id.h" #ifdef _WIN32 #include @@ -27,18 +25,20 @@ #include "Emu/Cell/Modules/sceNp.h" #include "Emu/Cell/Modules/sceNp2.h" #include "Emu/Cell/Modules/sceNpTus.h" - -#include "generated/np2_structs_generated.h" +#include #ifdef __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" +#pragma GCC diagnostic ignored "-Wextern-c-compat" #endif #include #ifdef __clang__ #pragma GCC diagnostic pop #endif +#include "rpcn_types.h" + // COMID is sent as 9 chars - + '_' + 2 digits constexpr usz COMMUNICATION_ID_COMID_COMPONENT_SIZE = 9; constexpr usz COMMUNICATION_ID_SUBID_COMPONENT_SIZE = 2; @@ -183,153 +183,6 @@ protected: namespace rpcn { - enum CommandType : u16 - { - Login, - Terminate, - Create, - SendToken, - SendResetToken, - ResetPassword, - AddFriend, - RemoveFriend, - AddBlock, - RemoveBlock, - GetServerList, - GetWorldList, - CreateRoom, - JoinRoom, - LeaveRoom, - SearchRoom, - GetRoomDataExternalList, - SetRoomDataExternal, - GetRoomDataInternal, - SetRoomDataInternal, - GetRoomMemberDataInternal, - SetRoomMemberDataInternal, - SetUserInfo, - PingRoomOwner, - SendRoomMessage, - RequestSignalingInfos, - RequestTicket, - SendMessage, - GetBoardInfos, - RecordScore, - RecordScoreData, - GetScoreData, - GetScoreRange, - GetScoreFriends, - GetScoreNpid, - GetNetworkTime, - TusSetMultiSlotVariable, - TusGetMultiSlotVariable, - TusGetMultiUserVariable, - TusGetFriendsVariable, - TusAddAndGetVariable, - TusTryAndSetVariable, - TusDeleteMultiSlotVariable, - TusSetData, - TusGetData, - TusGetMultiSlotDataStatus, - TusGetMultiUserDataStatus, - TusGetFriendsDataStatus, - TusDeleteMultiSlotData, - ClearPresence, - SetPresence, - CreateRoomGUI, - JoinRoomGUI, - LeaveRoomGUI, - GetRoomListGUI, - SetRoomSearchFlagGUI, - GetRoomSearchFlagGUI, - SetRoomInfoGUI, - GetRoomInfoGUI, - QuickMatchGUI, - SearchJoinRoomGUI, - }; - - enum NotificationType : u16 - { - UserJoinedRoom, - UserLeftRoom, - RoomDestroyed, - UpdatedRoomDataInternal, - UpdatedRoomMemberDataInternal, - SignalP2PConnect, - _SignalP2PDisconnect, - FriendQuery, // Other user sent a friend request - FriendNew, // Add a friend to the friendlist(either accepted a friend request or friend accepted it) - FriendLost, // Remove friend from the friendlist(user removed friend or friend removed friend) - FriendStatus, // Set status of friend to Offline or Online - RoomMessageReceived, - MessageReceived, - FriendPresenceChanged, - SignalingInfo, - MemberJoinedRoomGUI, - MemberLeftRoomGUI, - RoomDisappearedGUI, - RoomOwnerChangedGUI, - UserKickedGUI, - QuickMatchCompleteGUI, - }; - - enum class rpcn_state - { - failure_no_failure, - failure_input, - failure_wolfssl, - failure_resolve, - failure_connect, - failure_id, - failure_id_already_logged_in, - failure_id_username, - failure_id_password, - failure_id_token, - failure_protocol, - failure_other, - }; - - enum PacketType : u8 - { - Request, - Reply, - Notification, - ServerInfo, - }; - - enum ErrorType : u8 - { - NoError, // No error - Malformed, // Query was malformed, critical error that should close the connection - Invalid, // The request type is invalid(wrong stage?) - InvalidInput, // The Input doesn't fit the constraints of the request - TooSoon, // Time limited operation attempted too soon - LoginError, // An error happened related to login - LoginAlreadyLoggedIn, // Can't log in because you're already logged in - LoginInvalidUsername, // Invalid username - LoginInvalidPassword, // Invalid password - LoginInvalidToken, // Invalid token - CreationError, // An error happened related to account creation - CreationExistingUsername, // Specific to Account Creation: username exists already - CreationBannedEmailProvider, // Specific to Account Creation: the email provider is banned - CreationExistingEmail, // Specific to Account Creation: that email is already registered to an account - RoomMissing, // User tried to join a non existing room - RoomAlreadyJoined, // User tried to join a room he's already part of - RoomFull, // User tried to join a full room - Unauthorized, // User attempted an unauthorized operation - DbFail, // Generic failure on db side - EmailFail, // Generic failure related to email - NotFound, // Object of the query was not found(room, user, etc) - Blocked, // The operation can't complete because you've been blocked - AlreadyFriend, // Can't add friend because already friend - ScoreNotBest, // A better score is already registered for that user/character_id - ScoreInvalid, // Score for player was found but wasn't what was expected - ScoreHasData, // Score already has data - CondFail, // Condition related to query failed - Unsupported, - __error_last - }; - using friend_cb_func = void (*)(void* param, NotificationType ntype, const std::string& username, bool status); using message_cb_func = void (*)(void* param, const shared_ptr> new_msg, u64 msg_id); @@ -362,7 +215,7 @@ namespace rpcn localized_string_id rpcn_state_to_localized_string_id(rpcn::rpcn_state state); std::string rpcn_state_to_string(rpcn::rpcn_state state); - bool is_error(ErrorType err); + void print_error(rpcn::CommandType command, rpcn::ErrorType error); class rpcn_client { @@ -395,7 +248,7 @@ namespace rpcn std::set> friend_cbs; friend_data friend_infos; - void handle_friend_notification(u16 command, std::vector data); + void handle_friend_notification(rpcn::NotificationType ntype, std::vector data); void handle_message(std::vector data); @@ -438,6 +291,7 @@ namespace rpcn rpcn_state wait_for_connection(); rpcn_state wait_for_authentified(); bool terminate_connection(); + void reset_state(); void get_friends(friend_data& friend_infos); void get_friends_and_register_cb(friend_data& friend_infos, friend_cb_func cb_func, void* cb_param); @@ -457,8 +311,8 @@ namespace rpcn std::optional> get_friend_presence_by_index(u32 index); std::optional> get_friend_presence_by_npid(const std::string& npid); - std::vector>> get_notifications(); - std::unordered_map>> get_replies(); + std::vector>> get_notifications(); + std::unordered_map>> get_replies(); std::unordered_map get_presence_updates(); std::map get_presence_states(); @@ -540,11 +394,11 @@ namespace rpcn static void write_communication_id(const SceNpCommunicationId& com_id, std::vector& data); - std::vector forge_request(u16 command, u64 packet_id, const std::vector& data) const; - bool forge_send(u16 command, u64 packet_id, const std::vector& data); + std::vector forge_request(rpcn::CommandType command, u64 packet_id, const std::vector& data) const; + bool forge_send(rpcn::CommandType command, u64 packet_id, const std::vector& data); bool forge_request_with_com_id(const flatbuffers::FlatBufferBuilder& builder, const SceNpCommunicationId& com_id, CommandType command, u64 packet_id); bool forge_request_with_data(const flatbuffers::FlatBufferBuilder& builder, CommandType command, u64 packet_id); - bool forge_send_reply(u16 command, u64 packet_id, const std::vector& data, std::vector& reply_data); + bool forge_send_reply(rpcn::CommandType command, u64 packet_id, const std::vector& data, std::vector& reply_data); bool error_and_disconnect(const std::string& error_mgs); bool error_and_disconnect_notice(const std::string& error_msg); @@ -559,11 +413,9 @@ namespace rpcn atomic_t server_info_received = false; u32 received_version = 0; - // UDP Signaling related - steady_clock::time_point last_ping_time{}, last_pong_time{}; - sockaddr_in addr_rpcn{}; - sockaddr_in addr_rpcn_udp{}; + sockaddr_in addr_rpcn_udp_ipv4{}; + sockaddr_in6 addr_rpcn_udp_ipv6{}; #ifdef _WIN32 SOCKET sockfd = 0; #else @@ -573,10 +425,10 @@ namespace rpcn atomic_t rpcn_request_counter = 0x100000001; // Counter used for commands whose result is not forwarded to NP handler(login, create, sendmessage, etc) shared_mutex mutex_notifs, mutex_replies, mutex_replies_sync, mutex_presence_updates; - std::vector>> notifications; // notif type / data - std::unordered_map>> replies; // req id / (command / data) - std::unordered_map>> replies_sync; // same but for sync replies(see handle_input()) - std::unordered_map presence_updates; // npid / presence data + std::vector>> notifications; // notif type / data + std::unordered_map>> replies; // req id / (command / data) + std::unordered_map>> replies_sync; // same but for sync replies(see handle_input()) + std::unordered_map presence_updates; // npid / presence data // Messages struct message_cb_t diff --git a/rpcs3/Emu/NP/rpcn_config.cpp b/rpcs3/Emu/NP/rpcn_config.cpp index 1f52d60a05..a112084da7 100644 --- a/rpcs3/Emu/NP/rpcn_config.cpp +++ b/rpcs3/Emu/NP/rpcn_config.cpp @@ -124,6 +124,11 @@ std::string cfg_rpcn::get_token() const return token.to_string(); } +bool cfg_rpcn::get_ipv6_support() const +{ + return ipv6_support.get(); +} + void cfg_rpcn::set_host(std::string_view host) { this->host.from_string(host); @@ -144,6 +149,11 @@ void cfg_rpcn::set_token(std::string_view token) this->token.from_string(token); } +void cfg_rpcn::set_ipv6_support(bool ipv6_support) +{ + this->ipv6_support.set(ipv6_support); +} + void cfg_rpcn::set_hosts(const std::vector>& vec_hosts) { std::string final_string; diff --git a/rpcs3/Emu/NP/rpcn_config.h b/rpcs3/Emu/NP/rpcn_config.h index b03bdd3acb..7c1317fbe0 100644 --- a/rpcs3/Emu/NP/rpcn_config.h +++ b/rpcs3/Emu/NP/rpcn_config.h @@ -10,6 +10,7 @@ struct cfg_rpcn : cfg::node cfg::string password{this, "Password", ""}; cfg::string token{this, "Token", ""}; cfg::string hosts{this, "Hosts", "Official RPCN Server|np.rpcs3.net|||RPCN Test Server|test-np.rpcs3.net"}; + cfg::_bool ipv6_support{this, "IPv6 support", true}; void load(); void save() const; @@ -18,12 +19,14 @@ struct cfg_rpcn : cfg::node std::string get_npid(); // not const because it can save if npid is requested and it has never been set std::string get_password() const; std::string get_token() const; + bool get_ipv6_support() const; std::vector> get_hosts(); // saves default if no valid server in the list void set_host(std::string_view host); void set_npid(std::string_view npid); void set_password(std::string_view password); void set_token(std::string_view token); + void set_ipv6_support(bool ipv6_support); bool add_host(std::string_view description, std::string_view host); bool del_host(std::string_view description, std::string_view host); diff --git a/rpcs3/Emu/NP/rpcn_countries.h b/rpcs3/Emu/NP/rpcn_countries.h index 2551ced9c1..5c7e00fb4d 100644 --- a/rpcs3/Emu/NP/rpcn_countries.h +++ b/rpcs3/Emu/NP/rpcn_countries.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace countries { diff --git a/rpcs3/Emu/NP/rpcn_types.h b/rpcs3/Emu/NP/rpcn_types.h new file mode 100644 index 0000000000..1e9fb9acf4 --- /dev/null +++ b/rpcs3/Emu/NP/rpcn_types.h @@ -0,0 +1,156 @@ +#pragma once + +#include "util/types.hpp" + +namespace rpcn +{ + enum class CommandType : u16 + { + Login, + Terminate, + Create, + SendToken, + SendResetToken, + ResetPassword, + ResetState, + AddFriend, + RemoveFriend, + AddBlock, + RemoveBlock, + GetServerList, + GetWorldList, + CreateRoom, + JoinRoom, + LeaveRoom, + SearchRoom, + GetRoomDataExternalList, + SetRoomDataExternal, + GetRoomDataInternal, + SetRoomDataInternal, + GetRoomMemberDataInternal, + SetRoomMemberDataInternal, + SetUserInfo, + PingRoomOwner, + SendRoomMessage, + RequestSignalingInfos, + RequestTicket, + SendMessage, + GetBoardInfos, + RecordScore, + RecordScoreData, + GetScoreData, + GetScoreRange, + GetScoreFriends, + GetScoreNpid, + GetNetworkTime, + TusSetMultiSlotVariable, + TusGetMultiSlotVariable, + TusGetMultiUserVariable, + TusGetFriendsVariable, + TusAddAndGetVariable, + TusTryAndSetVariable, + TusDeleteMultiSlotVariable, + TusSetData, + TusGetData, + TusGetMultiSlotDataStatus, + TusGetMultiUserDataStatus, + TusGetFriendsDataStatus, + TusDeleteMultiSlotData, + SetPresence, + CreateRoomGUI, + JoinRoomGUI, + LeaveRoomGUI, + GetRoomListGUI, + SetRoomSearchFlagGUI, + GetRoomSearchFlagGUI, + SetRoomInfoGUI, + GetRoomInfoGUI, + QuickMatchGUI, + SearchJoinRoomGUI, + }; + + enum class NotificationType : u16 + { + UserJoinedRoom, + UserLeftRoom, + RoomDestroyed, + UpdatedRoomDataInternal, + UpdatedRoomMemberDataInternal, + FriendQuery, // Other user sent a friend request + FriendNew, // Add a friend to the friendlist(either accepted a friend request or friend accepted it) + FriendLost, // Remove friend from the friendlist(user removed friend or friend removed friend) + FriendStatus, // Set status of friend to Offline or Online + RoomMessageReceived, + MessageReceived, + FriendPresenceChanged, + SignalingHelper, + MemberJoinedRoomGUI, + MemberLeftRoomGUI, + RoomDisappearedGUI, + RoomOwnerChangedGUI, + UserKickedGUI, + QuickMatchCompleteGUI, + }; + + enum class rpcn_state + { + failure_no_failure, + failure_input, + failure_wolfssl, + failure_resolve, + failure_connect, + failure_id, + failure_id_already_logged_in, + failure_id_username, + failure_id_password, + failure_id_token, + failure_protocol, + failure_other, + }; + + enum class PacketType : u8 + { + Request, + Reply, + Notification, + ServerInfo, + }; + + enum class ErrorType : u8 + { + NoError, // No error + Malformed, // Query was malformed, critical error that should close the connection + Invalid, // The request type is invalid(wrong stage?) + InvalidInput, // The Input doesn't fit the constraints of the request + TooSoon, // Time limited operation attempted too soon + LoginError, // An error happened related to login + LoginAlreadyLoggedIn, // Can't log in because you're already logged in + LoginInvalidUsername, // Invalid username + LoginInvalidPassword, // Invalid password + LoginInvalidToken, // Invalid token + CreationError, // An error happened related to account creation + CreationExistingUsername, // Specific to Account Creation: username exists already + CreationBannedEmailProvider, // Specific to Account Creation: the email provider is banned + CreationExistingEmail, // Specific to Account Creation: that email is already registered to an account + RoomMissing, // User tried to interact with a non existing room + RoomAlreadyJoined, // User tried to join a room he's already part of + RoomFull, // User tried to join a full room + RoomPasswordMismatch, // Room password didn't match + RoomPasswordMissing, // A password was missing during room creation + RoomGroupNoJoinLabel, // Tried to join a group room without a label + RoomGroupFull, // Room group is full + RoomGroupJoinLabelNotFound, // Join label was invalid in some way + RoomGroupMaxSlotMismatch, // Mismatch between max_slot and the listed slots in groups + Unauthorized, // User attempted an unauthorized operation + DbFail, // Generic failure on db side + EmailFail, // Generic failure related to email + NotFound, // Object of the query was not found(user, etc), use RoomMissing for rooms instead + Blocked, // The operation can't complete because you've been blocked + AlreadyFriend, // Can't add friend because already friend + ScoreNotBest, // A better score is already registered for that user/character_id + ScoreInvalid, // Score for player was found but wasn't what was expected + ScoreHasData, // Score already has data + CondFail, // Condition related to query failed + Unsupported, + }; +} // namespace rpcn diff --git a/rpcs3/Emu/NP/signaling_handler.cpp b/rpcs3/Emu/NP/signaling_handler.cpp index d463b15676..2e4ac5df56 100644 --- a/rpcs3/Emu/NP/signaling_handler.cpp +++ b/rpcs3/Emu/NP/signaling_handler.cpp @@ -1,5 +1,6 @@ +#include "Emu/NP/ip_address.h" #include "stdafx.h" -#include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/PPUCallback.h" #include "signaling_handler.h" #include "Emu/IdManager.h" #include "Emu/Cell/Modules/cellSysutil.h" @@ -547,6 +548,13 @@ void signaling_handler::update_si_mapped_addr(std::shared_ptr& s { ensure(si); + // If the address given to us by op is a translation IP, just replace it with our public ip(v4) + if (np::is_ipv6_supported() && np::ip_address_translator::is_ipv6(new_addr)) + { + auto& nph = g_fxo->get>(); + new_addr = nph.get_public_ip_addr(); + } + if (si->mapped_addr != new_addr || si->mapped_port != new_port) { if (sign_log.trace) @@ -640,7 +648,15 @@ void signaling_handler::send_signaling_packet(signaling_packet& sp, u32 addr, u1 sign_log.trace("Sending %s packet to %s:%d", sp.command, ip_str, port); - if (send_packet_from_p2p_port(packet, dest) == -1) + if (np::is_ipv6_supported() && np::ip_address_translator::is_ipv6(dest.sin_addr.s_addr)) + { + auto& translator = g_fxo->get(); + const auto addr6 = translator.get_ipv6_sockaddr(dest.sin_addr.s_addr, dest.sin_port); + + if (!send_packet_from_p2p_port_ipv6(packet, addr6)) + sign_log.error("Failed to send signaling packet to %s:%d", ip_str, port); + } + else if (!send_packet_from_p2p_port_ipv4(packet, dest)) { sign_log.error("Failed to send signaling packet to %s:%d", ip_str, port); } diff --git a/rpcs3/Emu/NP/signaling_handler.h b/rpcs3/Emu/NP/signaling_handler.h index 209daa9cac..b4ec8229b8 100644 --- a/rpcs3/Emu/NP/signaling_handler.h +++ b/rpcs3/Emu/NP/signaling_handler.h @@ -1,9 +1,8 @@ #pragma once -#include "Emu/Memory/vm.h" -#include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/Modules/sceNp.h" #include "Emu/Cell/Modules/sceNp2.h" #include "Utilities/Thread.h" +#include "Utilities/mutex.h" #include #include #include diff --git a/rpcs3/Emu/NP/vport0.h b/rpcs3/Emu/NP/vport0.h index a3ca3bbdf3..927e1a5390 100644 --- a/rpcs3/Emu/NP/vport0.h +++ b/rpcs3/Emu/NP/vport0.h @@ -10,7 +10,8 @@ #include "Emu/Cell/lv2/sys_net/nt_p2p_port.h" -s32 send_packet_from_p2p_port(const std::vector& data, const sockaddr_in& addr); +bool send_packet_from_p2p_port_ipv4(const std::vector& data, const sockaddr_in& addr); +bool send_packet_from_p2p_port_ipv6(const std::vector& data, const sockaddr_in6& addr); std::vector get_sign_msgs(); std::vector> get_rpcn_msgs(); diff --git a/rpcs3/Emu/RSX/Capture/rsx_capture.cpp b/rpcs3/Emu/RSX/Capture/rsx_capture.cpp index 0eeea99db9..bfd47df73d 100644 --- a/rpcs3/Emu/RSX/Capture/rsx_capture.cpp +++ b/rpcs3/Emu/RSX/Capture/rsx_capture.cpp @@ -2,7 +2,6 @@ #include "rsx_capture.h" #include "Emu/RSX/Common/BufferUtils.h" #include "Emu/RSX/Common/TextureUtils.h" -#include "Emu/RSX/Common/surface_store.h" #include "Emu/RSX/GCM.h" #include "Emu/RSX/RSXThread.h" #include "Emu/Memory/vm.h" diff --git a/rpcs3/Emu/RSX/Capture/rsx_replay.cpp b/rpcs3/Emu/RSX/Capture/rsx_replay.cpp index 1e9a35601c..88bd6f9028 100644 --- a/rpcs3/Emu/RSX/Capture/rsx_replay.cpp +++ b/rpcs3/Emu/RSX/Capture/rsx_replay.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "rsx_replay.h" +#include "Emu/System.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/lv2/sys_rsx.h" #include "Emu/Cell/lv2/sys_memory.h" @@ -8,6 +9,8 @@ #include "util/asm.hpp" +#include + namespace rsx { be_t rsx_replay_thread::allocate_context() diff --git a/rpcs3/Emu/RSX/Common/BufferUtils.cpp b/rpcs3/Emu/RSX/Common/BufferUtils.cpp index ba19fdb461..0250daead2 100644 --- a/rpcs3/Emu/RSX/Common/BufferUtils.cpp +++ b/rpcs3/Emu/RSX/Common/BufferUtils.cpp @@ -1,12 +1,8 @@ #include "stdafx.h" #include "BufferUtils.h" -#include "../rsx_methods.h" -#include "../RSXThread.h" - #include "util/to_endian.hpp" #include "util/sysinfo.hpp" #include "Utilities/JIT.h" -#include "util/asm.hpp" #include "util/v128.hpp" #include "util/simd.hpp" diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.h b/rpcs3/Emu/RSX/Common/TextureUtils.h index 0481ded4f2..f03667042c 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.h +++ b/rpcs3/Emu/RSX/Common/TextureUtils.h @@ -4,7 +4,6 @@ #include "../color_utils.h" #include "../RSXTexture.h" -#include #include #include @@ -119,7 +118,8 @@ namespace rsx RSX_FORMAT_CLASS_DEPTH24_UNORM_X8_PACK32 = 8, RSX_FORMAT_CLASS_DEPTH24_FLOAT_X8_PACK32 = 16, - RSX_FORMAT_CLASS_DEPTH_FLOAT_MASK = (RSX_FORMAT_CLASS_DEPTH16_FLOAT | RSX_FORMAT_CLASS_DEPTH24_FLOAT_X8_PACK32) + RSX_FORMAT_CLASS_DEPTH_FLOAT_MASK = (RSX_FORMAT_CLASS_DEPTH16_FLOAT | RSX_FORMAT_CLASS_DEPTH24_FLOAT_X8_PACK32), + RSX_FORMAT_CLASS_DONT_CARE = RSX_FORMAT_CLASS_UNDEFINED, }; } diff --git a/rpcs3/Emu/RSX/Common/bitfield.hpp b/rpcs3/Emu/RSX/Common/bitfield.hpp index 35906909d2..af02792fc7 100644 --- a/rpcs3/Emu/RSX/Common/bitfield.hpp +++ b/rpcs3/Emu/RSX/Common/bitfield.hpp @@ -1,5 +1,6 @@ #pragma once +#include "util/atomic.hpp" #include #include diff --git a/rpcs3/Emu/RSX/Common/buffer_stream.hpp b/rpcs3/Emu/RSX/Common/buffer_stream.hpp index c978ccca0a..ff68d6008d 100644 --- a/rpcs3/Emu/RSX/Common/buffer_stream.hpp +++ b/rpcs3/Emu/RSX/Common/buffer_stream.hpp @@ -1,7 +1,6 @@ #pragma once #include "util/types.hpp" -#include "util/asm.hpp" #if defined(ARCH_X64) #include "emmintrin.h" diff --git a/rpcs3/Emu/RSX/Common/io_buffer.h b/rpcs3/Emu/RSX/Common/io_buffer.h index 88e30f0972..29397d1136 100644 --- a/rpcs3/Emu/RSX/Common/io_buffer.h +++ b/rpcs3/Emu/RSX/Common/io_buffer.h @@ -2,7 +2,6 @@ #include #include #include -#include #include namespace rsx diff --git a/rpcs3/Emu/RSX/Common/profiling_timer.hpp b/rpcs3/Emu/RSX/Common/profiling_timer.hpp index 88851626cc..f5ecacb4eb 100644 --- a/rpcs3/Emu/RSX/Common/profiling_timer.hpp +++ b/rpcs3/Emu/RSX/Common/profiling_timer.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include "time.hpp" +#include "Emu/Cell/timers.hpp" namespace rsx { diff --git a/rpcs3/Emu/RSX/Common/simple_array.hpp b/rpcs3/Emu/RSX/Common/simple_array.hpp index e791af0274..8ae4179a0c 100644 --- a/rpcs3/Emu/RSX/Common/simple_array.hpp +++ b/rpcs3/Emu/RSX/Common/simple_array.hpp @@ -404,18 +404,19 @@ namespace rsx return ret; } - void sort(std::predicate auto predicate) + simple_array& sort(std::predicate auto predicate) { if (_size < 2) { - return; + return *this; } std::sort(begin(), end(), predicate); + return *this; } template > - requires std::is_invocable_v + requires (std::is_invocable_v && std::is_trivially_destructible_v) simple_array map(F&& xform) const { simple_array result; @@ -428,6 +429,20 @@ namespace rsx return result; } + template > + requires (std::is_invocable_v && !std::is_trivially_destructible_v) + std::vector map(F&& xform) const + { + std::vector result; + result.reserve(size()); + + for (auto it = begin(); it != end(); ++it) + { + result.push_back(xform(*it)); + } + return result; + } + template requires std::is_invocable_r_v U reduce(U initial_value, F&& reducer) const diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h index e4d3f2d3fb..35813965e0 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.h +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -10,8 +10,6 @@ #include "util/asm.hpp" -#include - namespace rsx { namespace utility diff --git a/rpcs3/Emu/RSX/Common/surface_utils.h b/rpcs3/Emu/RSX/Common/surface_utils.h index bcecfb8db8..2fb55810e4 100644 --- a/rpcs3/Emu/RSX/Common/surface_utils.h +++ b/rpcs3/Emu/RSX/Common/surface_utils.h @@ -2,7 +2,6 @@ #include "util/types.hpp" #include "Utilities/geometry.h" -#include "Utilities/address_range.h" #include "TextureUtils.h" #include "../rsx_utils.h" #include "Emu/Memory/vm.h" diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 2b426e05b6..e367d46e6f 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -9,8 +9,6 @@ #include -#include "Emu/Cell/timers.hpp" - #define RSX_GCM_FORMAT_IGNORED 0 namespace rsx diff --git a/rpcs3/Emu/RSX/Common/texture_cache_checker.h b/rpcs3/Emu/RSX/Common/texture_cache_checker.h index 0325dda596..094220ac33 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_checker.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_checker.h @@ -1,8 +1,9 @@ #pragma once +#ifdef TEXTURE_CACHE_DEBUG + #include "../rsx_utils.h" -#ifdef TEXTURE_CACHE_DEBUG namespace rsx { class tex_cache_checker_t { diff --git a/rpcs3/Emu/RSX/Common/texture_cache_predictor.h b/rpcs3/Emu/RSX/Common/texture_cache_predictor.h index 7b5421481d..a18fc93c76 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_predictor.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_predictor.h @@ -1,6 +1,5 @@ #pragma once -#include "../rsx_cache.h" #include "../rsx_utils.h" #include "TextureUtils.h" diff --git a/rpcs3/Emu/RSX/Common/texture_cache_utils.h b/rpcs3/Emu/RSX/Common/texture_cache_utils.h index d01660775e..3a87deceb7 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_utils.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_utils.h @@ -1,5 +1,6 @@ #pragma once +#include "Emu/System.h" #include "texture_cache_types.h" #include "texture_cache_predictor.h" #include "TextureUtils.h" diff --git a/rpcs3/Emu/RSX/Common/time.hpp b/rpcs3/Emu/RSX/Common/time.hpp index ad3aa56067..db176fb4cc 100644 --- a/rpcs3/Emu/RSX/Common/time.hpp +++ b/rpcs3/Emu/RSX/Common/time.hpp @@ -2,5 +2,3 @@ #include #include - -#include "Emu/Cell/timers.hpp" diff --git a/rpcs3/Emu/RSX/Core/RSXDisplay.cpp b/rpcs3/Emu/RSX/Core/RSXDisplay.cpp new file mode 100644 index 0000000000..e263a945ef --- /dev/null +++ b/rpcs3/Emu/RSX/Core/RSXDisplay.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" +#include "RSXDisplay.h" + +#include "../Common/simple_array.hpp" +#include "../rsx_utils.h" + +namespace rsx +{ + std::string framebuffer_dimensions_t::to_string(bool skip_aa_suffix) const + { + std::string suffix = ""; + const auto spp = samples_x * samples_y; + + if (!skip_aa_suffix && spp > 1) + { + suffix = std::string(" @MSAA ") + std::to_string(spp) + "x"; + } + + return std::to_string(width) + "x" + std::to_string(height) + suffix; + } + + framebuffer_dimensions_t framebuffer_dimensions_t::make(u16 width, u16 height, rsx::surface_antialiasing aa) + { + framebuffer_dimensions_t result { .width = width, .height = height }; + switch (aa) + { + case rsx::surface_antialiasing::center_1_sample: + result.samples_x = result.samples_y = 1; + break; + case rsx::surface_antialiasing::diagonal_centered_2_samples: + result.samples_x = 2; + result.samples_y = 1; + break; + case rsx::surface_antialiasing::square_centered_4_samples: + case rsx::surface_antialiasing::square_rotated_4_samples: + result.samples_x = result.samples_y = 2; + break; + } + return result; + } + + void framebuffer_statistics_t::add(u16 width, u16 height, rsx::surface_antialiasing aa) + { + auto& stashed = data[aa]; + const auto& incoming = framebuffer_dimensions_t::make(width, height, aa); + if (incoming > stashed) + { + stashed = incoming; + } + } + + std::string framebuffer_statistics_t::to_string(bool squash) const + { + // Format is sorted by sample count + struct sorted_message_t + { + u32 id; + surface_antialiasing aa_mode; + u32 samples; + }; + + if (data.size() == 0) + { + return "None"; + } + + rsx::simple_array messages; + rsx::simple_array real_stats; + + for (const auto& [aa_mode, stat] : data) + { + auto real_stat = stat; + std::tie(real_stat.width, real_stat.height) = apply_resolution_scale(stat.width, stat.height); + real_stats.push_back(real_stat); + + sorted_message_t msg; + msg.id = real_stats.size() - 1; + msg.aa_mode = aa_mode; + msg.samples = real_stat.samples_total(); + messages.push_back(msg); + } + + if (squash) + { + messages.sort(FN(x.samples > y.samples)); + return real_stats[messages.front().id] + .to_string(g_cfg.video.antialiasing_level == msaa_level::none); + } + + if (messages.size() > 1) + { + // Should we bother showing the No-AA entry? + // This heurestic ignores pointless no-AA surfaces usually used as compositing buffers for output. + messages.sort(FN(x.samples > y.samples)); + if (messages.back().aa_mode == rsx::surface_antialiasing::center_1_sample) + { + // Drop the last entry if it has no AA. + messages.resize(messages.size() - 1); + } + } + + const auto text = messages + .sort(FN(static_cast(x.aa_mode) > static_cast(y.aa_mode))) + .map(FN(real_stats[x.id].to_string())); + return fmt::merge(text, ", "); + } +} diff --git a/rpcs3/Emu/RSX/Core/RSXDisplay.h b/rpcs3/Emu/RSX/Core/RSXDisplay.h index 5628ce77a2..4d4c90cd0d 100644 --- a/rpcs3/Emu/RSX/Core/RSXDisplay.h +++ b/rpcs3/Emu/RSX/Core/RSXDisplay.h @@ -3,9 +3,48 @@ #include #include #include +#include + +template +class named_thread; namespace rsx { + enum class surface_antialiasing : u8; + + struct framebuffer_dimensions_t + { + u16 width; + u16 height; + u8 samples_x; + u8 samples_y; + + inline u32 samples_total() const + { + return static_cast(width) * height * samples_x * samples_y; + } + + inline bool operator > (const framebuffer_dimensions_t& that) const + { + return samples_total() > that.samples_total(); + } + + std::string to_string(bool skip_aa_suffix = false) const; + + static framebuffer_dimensions_t make(u16 width, u16 height, rsx::surface_antialiasing aa); + }; + + struct framebuffer_statistics_t + { + std::unordered_map data; + + // Replace the existing data with this input if it is greater than what is already known + void add(u16 width, u16 height, rsx::surface_antialiasing aa); + + // Returns a formatted string representing the statistics collected over the frame. + std::string to_string(bool squash) const; + }; + struct frame_statistics_t { u32 draw_calls; @@ -19,6 +58,8 @@ namespace rsx u32 vertex_cache_request_count; u32 vertex_cache_miss_count; + + framebuffer_statistics_t framebuffer_stats; }; struct frame_time_t diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp index 389efe0a2e..9d218edb89 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp @@ -667,7 +667,23 @@ namespace rsx rop_control.enable_polygon_stipple(); } - if (REGS(m_ctx)->msaa_alpha_to_coverage_enabled() && !RSX(m_ctx)->get_backend_config().supports_hw_a2c) + auto can_use_hw_a2c = [&]() -> bool + { + const auto& config = RSX(m_ctx)->get_backend_config(); + if (!config.supports_hw_a2c) + { + return false; + } + + if (config.supports_hw_a2c_1spp) + { + return true; + } + + return REGS(m_ctx)->surface_antialias() != rsx::surface_antialiasing::center_1_sample; + }; + + if (REGS(m_ctx)->msaa_alpha_to_coverage_enabled() && !can_use_hw_a2c()) { // TODO: Properly support alpha-to-coverage and alpha-to-one behavior in shaders // Alpha values generate a coverage mask for order independent blending diff --git a/rpcs3/Emu/RSX/GCM.h b/rpcs3/Emu/RSX/GCM.h index 695b0a7611..c61e9907a4 100644 --- a/rpcs3/Emu/RSX/GCM.h +++ b/rpcs3/Emu/RSX/GCM.h @@ -1,9 +1,6 @@ #pragma once #include "Emu/Memory/vm_ptr.h" -#include "gcm_enums.h" -#include "util/atomic.hpp" - struct CellGcmControl { diff --git a/rpcs3/Emu/RSX/GL/GLCompute.cpp b/rpcs3/Emu/RSX/GL/GLCompute.cpp index 12ed12344c..5607c149ed 100644 --- a/rpcs3/Emu/RSX/GL/GLCompute.cpp +++ b/rpcs3/Emu/RSX/GL/GLCompute.cpp @@ -58,6 +58,8 @@ namespace gl if (!compiled) { + ensure(!m_src.empty(), "Compute shader is not initialized!"); + m_shader.create(::glsl::program_domain::glsl_compute_program, m_src); m_shader.compile(); @@ -82,6 +84,7 @@ namespace gl void compute_task::run(gl::command_context& cmd, u32 invocations_x, u32 invocations_y) { + ensure(compiled && m_program.id() != GL_NONE); bind_resources(); cmd->use_program(m_program.id()); diff --git a/rpcs3/Emu/RSX/GL/GLDMA.cpp b/rpcs3/Emu/RSX/GL/GLDMA.cpp index 6f1e7f7fbe..758205214e 100644 --- a/rpcs3/Emu/RSX/GL/GLDMA.cpp +++ b/rpcs3/Emu/RSX/GL/GLDMA.cpp @@ -2,6 +2,7 @@ #include "GLDMA.h" #include "Emu/Memory/vm.h" +#include "Emu/RSX/GL/glutils/common.h" namespace gl { diff --git a/rpcs3/Emu/RSX/GL/GLDraw.cpp b/rpcs3/Emu/RSX/GL/GLDraw.cpp index a3cdd06986..b2de00e3b7 100644 --- a/rpcs3/Emu/RSX/GL/GLDraw.cpp +++ b/rpcs3/Emu/RSX/GL/GLDraw.cpp @@ -3,6 +3,8 @@ #include "../rsx_methods.h" #include "../Common/BufferUtils.h" +#include "Emu/RSX/NV47/HW/context_accessors.define.h" + namespace gl { GLenum comparison_op(rsx::comparison_function op) @@ -256,6 +258,32 @@ void GLGSRender::update_draw_state() gl_state.enablei(mrt_blend_enabled[2], GL_BLEND, 2); gl_state.enablei(mrt_blend_enabled[3], GL_BLEND, 3); } + + // Antialias control + if (backend_config.supports_hw_msaa) + { + gl_state.enable(/*REGS(m_ctx)->msaa_enabled()*/GL_MULTISAMPLE); + + gl_state.enable(GL_SAMPLE_MASK); + gl_state.sample_mask(REGS(m_ctx)->msaa_sample_mask()); + + gl_state.enable(GL_SAMPLE_SHADING); + gl_state.min_sample_shading_rate(1.f); + + gl_state.enable(GL_SAMPLE_COVERAGE); + gl_state.sample_coverage(1.f); + } + + if (backend_config.supports_hw_a2c) + { + const bool hw_enable = backend_config.supports_hw_a2c_1spp || REGS(m_ctx)->surface_antialias() != rsx::surface_antialiasing::center_1_sample; + gl_state.enable(hw_enable && REGS(m_ctx)->msaa_alpha_to_coverage_enabled(), GL_SAMPLE_ALPHA_TO_COVERAGE); + } + + if (backend_config.supports_hw_a2one) + { + gl_state.enable(REGS(m_ctx)->msaa_alpha_to_one_enabled(), GL_SAMPLE_ALPHA_TO_ONE); + } } switch (rsx::method_registers.current_draw_clause.primitive) @@ -307,12 +335,6 @@ void GLGSRender::update_draw_state() // Clip planes gl_state.clip_planes((current_vertex_program.output_mask >> CELL_GCM_ATTRIB_OUTPUT_UC0) & 0x3F); - // Sample control - // TODO: MinSampleShading - //gl_state.enable(rsx::method_registers.msaa_enabled(), GL_MULTISAMPLE); - //gl_state.enable(rsx::method_registers.msaa_alpha_to_coverage_enabled(), GL_SAMPLE_ALPHA_TO_COVERAGE); - //gl_state.enable(rsx::method_registers.msaa_alpha_to_one_enabled(), GL_SAMPLE_ALPHA_TO_ONE); - //TODO //NV4097_SET_ANISO_SPREAD //NV4097_SET_SPECULAR_ENABLE @@ -694,6 +716,7 @@ void GLGSRender::begin() if (m_graphics_state & rsx::pipeline_state::invalidate_pipeline_bits) { // Shaders need to be reloaded. + m_prev_program = m_program; m_program = nullptr; } } diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp index 7980670ca4..1731772911 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp @@ -3,8 +3,8 @@ #include "Emu/system_config.h" #include "GLCommonDecompiler.h" -#include "../GCM.h" #include "../Program/GLSLCommon.h" +#include "../RSXThread.h" std::string GLFragmentDecompilerThread::getFloatTypeName(usz elementCount) { @@ -44,6 +44,21 @@ void GLFragmentDecompilerThread::insertHeader(std::stringstream & OS) } } + if (properties.multisampled_sampler_mask) + { + // Requires this extension or GLSL 450 + const auto driver_caps = gl::get_driver_caps(); + if (driver_caps.glsl_version.version >= 450) + { + gl_version = 450; + } + else + { + ensure(driver_caps.ARB_shader_texture_image_samples, "MSAA support on OpenGL requires a driver running OpenGL 4.5 or supporting GL_ARB_shader_texture_image_samples."); + required_extensions.push_back("GL_ARB_shader_texture_image_samples"); + } + } + if (m_prog.ctrl & RSX_SHADER_CONTROL_ATTRIBUTE_INTERPOLATION) { gl_version = std::max(gl_version, 450); @@ -110,10 +125,14 @@ void GLFragmentDecompilerThread::insertConstants(std::stringstream & OS) const auto mask = (1 << index); - if (properties.redirected_sampler_mask & mask) + if (properties.multisampled_sampler_mask & mask) { - // Provide a stencil view of the main resource for the S channel - OS << "uniform u" << samplerType << " " << PI.name << "_stencil;\n"; + if (samplerType != "sampler1D" && samplerType != "sampler2D") + { + rsx_log.error("Unexpected multisampled image type '%s'", samplerType); + } + + samplerType = "sampler2DMS"; } else if (properties.shadow_sampler_mask & mask) { @@ -127,6 +146,12 @@ void GLFragmentDecompilerThread::insertConstants(std::stringstream & OS) } } + if (properties.redirected_sampler_mask & mask) + { + // Provide a stencil view of the main resource for the S channel + OS << "uniform u" << samplerType << " " << PI.name << "_stencil;\n"; + } + OS << "uniform " << samplerType << " " << PI.name << ";\n"; } } @@ -188,11 +213,12 @@ void GLFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS) m_shader_props.require_wpos = !!(properties.in_register_mask & in_wpos); m_shader_props.require_texture_ops = properties.has_tex_op; m_shader_props.require_tex_shadow_ops = properties.shadow_sampler_mask != 0; + m_shader_props.require_msaa_ops = properties.multisampled_sampler_mask != 0; m_shader_props.require_texture_expand = properties.has_exp_tex_op; m_shader_props.require_srgb_to_linear = properties.has_upg; m_shader_props.require_linear_to_srgb = properties.has_pkg; m_shader_props.require_fog_read = properties.in_register_mask & in_fogc; - m_shader_props.emulate_coverage_tests = true; // g_cfg.video.antialiasing_level == msaa_level::none; + m_shader_props.emulate_coverage_tests = !rsx::get_renderer_backend_config().supports_hw_a2c_1spp; m_shader_props.emulate_shadow_compare = device_props.emulate_depth_compare; m_shader_props.low_precision_tests = ::gl::get_driver_caps().vendor_NVIDIA && !(m_prog.ctrl & RSX_SHADER_CONTROL_ATTRIBUTE_INTERPOLATION); m_shader_props.disable_early_discard = !::gl::get_driver_caps().vendor_NVIDIA; diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.h b/rpcs3/Emu/RSX/GL/GLFragmentProgram.h index fa955f22eb..c6b5244389 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.h +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.h @@ -1,7 +1,6 @@ #pragma once #include "../Program/FragmentProgramDecompiler.h" #include "../Program/GLSLTypes.h" -#include "GLHelpers.h" #include "glutils/program.h" namespace glsl diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index c37228afe9..6dbd7f919c 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -4,6 +4,7 @@ #include "GLGSRender.h" #include "GLCompute.h" #include "GLDMA.h" +#include "GLResolveHelper.h" #include "Emu/Memory/vm_locking.h" #include "Emu/RSX/rsx_methods.h" @@ -46,10 +47,16 @@ GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar) else m_vertex_cache = std::make_unique(); - backend_config.supports_hw_a2c = false; - backend_config.supports_hw_a2one = false; backend_config.supports_multidraw = true; backend_config.supports_normalized_barycentrics = true; + + if (g_cfg.video.antialiasing_level != msaa_level::none) + { + backend_config.supports_hw_msaa = true; + backend_config.supports_hw_a2c = true; + backend_config.supports_hw_a2c_1spp = false; // In OGL A2C is implicitly disabled at 1spp + backend_config.supports_hw_a2one = true; + } } GLGSRender::~GLGSRender() @@ -229,13 +236,13 @@ void GLGSRender::on_init_thread() // Array stream buffer { - m_gl_persistent_stream_buffer = std::make_unique(GL_TEXTURE_BUFFER, 0, 0, 0, 0, GL_R8UI); + m_gl_persistent_stream_buffer = std::make_unique(GL_TEXTURE_BUFFER, 0, 0, 0, 0, 0, GL_R8UI, RSX_FORMAT_CLASS_DONT_CARE); gl_state.bind_texture(GL_STREAM_BUFFER_START + 0, GL_TEXTURE_BUFFER, m_gl_persistent_stream_buffer->id()); } // Register stream buffer { - m_gl_volatile_stream_buffer = std::make_unique(GL_TEXTURE_BUFFER, 0, 0, 0, 0, GL_R8UI); + m_gl_volatile_stream_buffer = std::make_unique(GL_TEXTURE_BUFFER, 0, 0, 0, 0, 0, GL_R8UI, RSX_FORMAT_CLASS_DONT_CARE); gl_state.bind_texture(GL_STREAM_BUFFER_START + 1, GL_TEXTURE_BUFFER, m_gl_volatile_stream_buffer->id()); } @@ -244,19 +251,19 @@ void GLGSRender::on_init_thread() std::array pixeldata = { 0, 0, 0, 0, 0, 0, 0, 0 }; // 1D - auto tex1D = std::make_unique(GL_TEXTURE_1D, 1, 1, 1, 1, GL_RGBA8); + auto tex1D = std::make_unique(GL_TEXTURE_1D, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); tex1D->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); // 2D - auto tex2D = std::make_unique(GL_TEXTURE_2D, 1, 1, 1, 1, GL_RGBA8); + auto tex2D = std::make_unique(GL_TEXTURE_2D, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); tex2D->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); // 3D - auto tex3D = std::make_unique(GL_TEXTURE_3D, 1, 1, 1, 1, GL_RGBA8); + auto tex3D = std::make_unique(GL_TEXTURE_3D, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); tex3D->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); // CUBE - auto texCUBE = std::make_unique(GL_TEXTURE_CUBE_MAP, 1, 1, 1, 1, GL_RGBA8); + auto texCUBE = std::make_unique(GL_TEXTURE_CUBE_MAP, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); texCUBE->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); m_null_textures[GL_TEXTURE_1D] = std::move(tex1D); @@ -423,6 +430,7 @@ void GLGSRender::on_exit() gl::destroy_compute_tasks(); gl::destroy_overlay_passes(); gl::clear_dma_resources(); + gl::clear_resolve_helpers(); gl::destroy_global_texture_resources(); @@ -767,7 +775,6 @@ bool GLGSRender::load_program() } } - const bool was_interpreter = m_shader_interpreter.is_interpreter(m_program); m_vertex_prog = nullptr; m_fragment_prog = nullptr; @@ -796,15 +803,32 @@ bool GLGSRender::load_program() m_program = nullptr; } - if (!m_program && (shadermode == shader_mode::async_with_interpreter || shadermode == shader_mode::interpreter_only)) + if (shadermode == shader_mode::async_with_interpreter || shadermode == shader_mode::interpreter_only) { - // Fall back to interpreter - m_program = m_shader_interpreter.get(current_fp_metadata); - if (was_interpreter != m_shader_interpreter.is_interpreter(m_program)) + const bool is_interpreter = !m_program; + const bool was_interpreter = m_shader_interpreter.is_interpreter(m_prev_program); + + // First load the next program if not available + if (!m_program) { + m_program = m_shader_interpreter.get(current_fp_metadata); + // Program has changed, reupload m_interpreter_state = rsx::invalidate_pipeline_bits; } + + // If swapping between interpreter and recompiler, we need to adjust some flags to reupload data as needed. + if (is_interpreter != was_interpreter) + { + // Always reupload transform constants when going between interpreter and recompiler + m_graphics_state |= rsx::transform_constants_dirty; + + // Always reload fragment constansts when moving from interpreter back to recompiler. + if (was_interpreter) + { + m_graphics_state |= rsx::fragment_constants_dirty; + } + } } return m_program != nullptr; @@ -989,6 +1013,11 @@ void GLGSRender::load_program_env() handled_flags |= rsx::pipeline_state::fragment_constants_dirty; } + if (m_shader_interpreter.is_interpreter(m_program)) + { + ensure(m_transform_constants_buffer->bound_range().second >= 468 * 16); + } + m_graphics_state.clear(handled_flags); } @@ -999,7 +1028,9 @@ bool GLGSRender::is_current_program_interpreted() const void GLGSRender::upload_transform_constants(const rsx::io_buffer& buffer) { - const usz transform_constants_size = (!m_vertex_prog || m_vertex_prog->has_indexed_constants) ? 8192 : m_vertex_prog->constant_ids.size() * 16; + const bool is_interpreter = m_shader_interpreter.is_interpreter(m_program); + const usz transform_constants_size = (is_interpreter || m_vertex_prog->has_indexed_constants) ? 8192 : m_vertex_prog->constant_ids.size() * 16; + if (transform_constants_size) { const auto constant_ids = (transform_constants_size == 8192) diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 01579a445b..1c84233934 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -1,15 +1,15 @@ #pragma once #include "Emu/RSX/GSRender.h" -#include "GLHelpers.h" -#include "GLTexture.h" #include "GLTextureCache.h" #include "GLRenderTargets.h" #include "GLProgramBuffer.h" #include "GLOverlays.h" #include "GLShaderInterpreter.h" +#include "Emu/RSX/rsx_cache.h" #include #include +#include #include "glutils/ring_buffer.h" #include "upscalers/upscaling.h" @@ -78,6 +78,7 @@ class GLGSRender : public GSRender, public ::rsx::reports::ZCULL_control gl::sampler_state m_vs_sampler_states[rsx::limits::vertex_textures_count]; // Vertex textures gl::glsl::program *m_program = nullptr; + gl::glsl::program* m_prev_program = nullptr; const GLFragmentProgram *m_fragment_prog = nullptr; const GLVertexProgram *m_vertex_prog = nullptr; diff --git a/rpcs3/Emu/RSX/GL/GLHelpers.cpp b/rpcs3/Emu/RSX/GL/GLHelpers.cpp index 37ec98f188..da95960f40 100644 --- a/rpcs3/Emu/RSX/GL/GLHelpers.cpp +++ b/rpcs3/Emu/RSX/GL/GLHelpers.cpp @@ -1,10 +1,7 @@ #include "stdafx.h" #include "GLHelpers.h" -#include "GLTexture.h" #include "GLCompute.h" #include "util/logs.hpp" - -#include "../Common/simple_array.hpp" #include namespace gl diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.cpp b/rpcs3/Emu/RSX/GL/GLOverlays.cpp index 5a2b8803f4..b9ea62f2f9 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.cpp +++ b/rpcs3/Emu/RSX/GL/GLOverlays.cpp @@ -1,8 +1,8 @@ #include "GLOverlays.h" -#include "Emu/system_config.h" -#include "../rsx_utils.h" +#include "Utilities/StrUtil.h" #include "../Program/RSXOverlay.h" +#include "Emu/Cell/timers.hpp" namespace gl { @@ -23,6 +23,8 @@ namespace gl { if (!compiled) { + ensure(!fs_src.empty() && !vs_src.empty(), "Shaders have not been initialized."); + fs.create(::glsl::program_domain::glsl_fragment_program, fs_src); fs.compile(); @@ -34,6 +36,8 @@ namespace gl program_handle.attach(fs); program_handle.link(); + ensure(program_handle.id()); + fbo.create(); m_sampler.create(); @@ -75,7 +79,7 @@ namespace gl } } - void overlay_pass::emit_geometry() + void overlay_pass::emit_geometry(gl::command_context& /*cmd*/) { int old_vao; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao); @@ -88,11 +92,7 @@ namespace gl void overlay_pass::run(gl::command_context& cmd, const areau& region, GLuint target_texture, GLuint image_aspect_bits, bool enable_blending) { - if (!compiled) - { - rsx_log.error("You must initialize overlay passes with create() before calling run()"); - return; - } + ensure(compiled && program_handle.id() != GL_NONE, "You must initialize overlay passes with create() before calling run()"); GLint viewport[4]; std::unique_ptr save_fbo; @@ -111,6 +111,10 @@ namespace gl fbo.draw_buffer(fbo.no_color); fbo.depth = target_texture; break; + case gl::image_aspect::stencil: + fbo.draw_buffer(fbo.no_color); + fbo.depth_stencil = target_texture; + break; case gl::image_aspect::depth | gl::image_aspect::stencil: fbo.draw_buffer(fbo.no_color); fbo.depth_stencil = target_texture; @@ -176,7 +180,7 @@ namespace gl cmd->use_program(program_handle.id()); on_load(); bind_resources(); - emit_geometry(); + emit_geometry(cmd); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); @@ -216,7 +220,7 @@ namespace gl gl::texture_view* ui_overlay_renderer::load_simple_image(rsx::overlays::image_info* desc, bool temp_resource, u32 owner_uid) { - auto tex = std::make_unique(GL_TEXTURE_2D, desc->w, desc->h, 1, 1, GL_RGBA8); + auto tex = std::make_unique(GL_TEXTURE_2D, desc->w, desc->h, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); tex->copy_from(desc->get_data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); GLenum remap[] = { GL_RED, GL_ALPHA, GL_BLUE, GL_GREEN }; @@ -301,7 +305,7 @@ namespace gl // Create font file const std::vector glyph_data = font->get_glyph_data(); - auto tex = std::make_unique(GL_TEXTURE_2D_ARRAY, font_size.width, font_size.height, font_size.depth, 1, GL_R8); + auto tex = std::make_unique(GL_TEXTURE_2D_ARRAY, font_size.width, font_size.height, font_size.depth, 1, 1, GL_R8, RSX_FORMAT_CLASS_COLOR); tex->copy_from(glyph_data.data(), gl::texture::format::r, gl::texture::type::ubyte, {}); GLenum remap[] = { GL_RED, GL_RED, GL_RED, GL_RED }; @@ -350,7 +354,7 @@ namespace gl } } - void ui_overlay_renderer::emit_geometry() + void ui_overlay_renderer::emit_geometry(gl::command_context& cmd) { if (m_current_primitive_type == rsx::overlays::primitive_type::quad_list) { @@ -378,7 +382,7 @@ namespace gl } else { - overlay_pass::emit_geometry(); + overlay_pass::emit_geometry(cmd); } } diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.h b/rpcs3/Emu/RSX/GL/GLOverlays.h index 92f76160a8..f70e885d2e 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.h +++ b/rpcs3/Emu/RSX/GL/GLOverlays.h @@ -1,6 +1,7 @@ #pragma once #include "Emu/system_config_types.h" +#include "Emu/IdManager.h" #include "util/types.hpp" #include "../Common/simple_array.hpp" #include "../Overlays/overlays.h" @@ -57,7 +58,7 @@ namespace gl m_vertex_data_buffer.data(elements_count * sizeof(T), data); } - virtual void emit_geometry(); + virtual void emit_geometry(gl::command_context& cmd); void run(gl::command_context& cmd, const areau& region, GLuint target_texture, GLuint image_aspect_bits, bool enable_blending = false); }; @@ -87,7 +88,7 @@ namespace gl void set_primitive_type(rsx::overlays::primitive_type type); - void emit_geometry() override; + void emit_geometry(gl::command_context& cmd) override; void run(gl::command_context& cmd, const areau& viewport, GLuint target, rsx::overlays::overlay& ui); }; diff --git a/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp b/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp index 445841b44b..bdf03c6fff 100644 --- a/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp +++ b/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp @@ -1,9 +1,6 @@ #include "stdafx.h" #include "GLPipelineCompiler.h" #include "Utilities/Thread.h" - -#include - #include "util/sysinfo.hpp" namespace gl diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index 3ad9ed1c4d..1eaa3a6193 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -26,7 +26,7 @@ namespace gl { const auto target = static_cast(visual->get_target()); const auto ifmt = static_cast(visual->get_internal_format()); - g_vis_texture.reset(new texture(target, visual->width(), visual->height(), 1, 1, ifmt, visual->format_class())); + g_vis_texture.reset(new texture(target, visual->width(), visual->height(), 1, 1, 1, ifmt, visual->format_class())); glCopyImageSubData(visual->id(), target, 0, 0, 0, 0, g_vis_texture->id(), target, 0, 0, 0, 0, visual->width(), visual->height(), 1); } } @@ -115,7 +115,7 @@ gl::texture* GLGSRender::get_present_source(gl::present_surface_info* info, cons { if (!flip_image || flip_image->size2D() != sizeu{ info->width, info->height }) { - flip_image = std::make_unique(GL_TEXTURE_2D, info->width, info->height, 1, 1, expected_format); + flip_image = std::make_unique(GL_TEXTURE_2D, info->width, info->height, 1, 1, 1, expected_format, RSX_FORMAT_CLASS_COLOR); } }; @@ -402,6 +402,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) : 0; rsx::overlays::set_debug_overlay_text(fmt::format( + "Internal Resolution: %s\n" "RSX Load: %3d%%\n" "draw calls: %16d\n" "draw call setup: %11dus\n" @@ -413,6 +414,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) "Flush requests: %12d = %2d (%3d%%) hard faults, %2d unavoidable, %2d misprediction(s), %2d speculation(s)\n" "Texture uploads: %11u (%u from CPU - %02u%%, %u copies avoided)\n" "Vertex cache hits: %9u/%u (%u%%)", + info.stats.framebuffer_stats.to_string(!backend_config.supports_hw_msaa), get_load(), info.stats.draw_calls, info.stats.setup_time, info.stats.vertex_upload_time, info.stats.textures_upload_time, info.stats.draw_exec_time, num_dirty_textures, texture_memory_size, num_flushes, num_misses, cache_miss_ratio, num_unavoidable, num_mispredict, num_speculate, diff --git a/rpcs3/Emu/RSX/GL/GLProcTable.h b/rpcs3/Emu/RSX/GL/GLProcTable.h index 20da00944f..783701e4dd 100644 --- a/rpcs3/Emu/RSX/GL/GLProcTable.h +++ b/rpcs3/Emu/RSX/GL/GLProcTable.h @@ -259,6 +259,13 @@ OPENGL_PROC(PFNGLTEXSTORAGE1DPROC, TexStorage1D); OPENGL_PROC(PFNGLTEXSTORAGE2DPROC, TexStorage2D); OPENGL_PROC(PFNGLTEXSTORAGE3DPROC, TexStorage3D); +// ARB_texture_multisample +OPENGL_PROC(PFNGLTEXSTORAGE2DMULTISAMPLEPROC, TexStorage2DMultisample); +OPENGL_PROC(PFNGLTEXSTORAGE3DMULTISAMPLEPROC, TexStorage3DMultisample); +OPENGL_PROC(PFNGLSAMPLEMASKIPROC, SampleMaski); +OPENGL_PROC(PFNGLMINSAMPLESHADINGPROC, MinSampleShading); +OPENGL_PROC(PFNGLSAMPLECOVERAGEPROC, SampleCoverage); + // Texture_View OPENGL_PROC(PFNGLTEXTUREVIEWPROC, TextureView); diff --git a/rpcs3/Emu/RSX/GL/GLProgramBuffer.h b/rpcs3/Emu/RSX/GL/GLProgramBuffer.h index fafb7d6f64..2d6f19f271 100644 --- a/rpcs3/Emu/RSX/GL/GLProgramBuffer.h +++ b/rpcs3/Emu/RSX/GL/GLProgramBuffer.h @@ -1,7 +1,6 @@ #pragma once #include "GLVertexProgram.h" #include "GLFragmentProgram.h" -#include "GLHelpers.h" #include "GLPipelineCompiler.h" #include "../Program/ProgramStateCache.h" #include "../rsx_utils.h" diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp index e703dd31dd..87e5ee5645 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "GLGSRender.h" +#include "GLResolveHelper.h" #include "Emu/RSX/rsx_methods.h" #include @@ -417,15 +418,16 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool /* } // Render target helpers -void gl::render_target::clear_memory(gl::command_context& cmd) +void gl::render_target::clear_memory(gl::command_context& cmd, gl::texture* surface) { + auto dst = surface ? surface : this; if (aspect() & gl::image_aspect::depth) { - gl::g_hw_blitter->fast_clear_image(cmd, this, 1.f, 255); + gl::g_hw_blitter->fast_clear_image(cmd, dst, 1.f, 255); } else { - gl::g_hw_blitter->fast_clear_image(cmd, this, {}); + gl::g_hw_blitter->fast_clear_image(cmd, dst, {}); } state_flags &= ~rsx::surface_state_flags::erase_bkgnd; @@ -449,18 +451,26 @@ void gl::render_target::load_memory(gl::command_context& cmd) } else { - auto tmp = std::make_unique(GL_TEXTURE_2D, subres.width_in_block, subres.height_in_block, 1, 1, static_cast(get_internal_format()), format_class()); + auto tmp = std::make_unique(GL_TEXTURE_2D, subres.width_in_block, subres.height_in_block, 1, 1, 1, static_cast(get_internal_format()), format_class()); + auto dst = samples() > 1 ? get_resolve_target_safe(cmd) : this; gl::upload_texture(cmd, tmp.get(), get_gcm_format(), is_swizzled, { subres }); - gl::g_hw_blitter->scale_image(cmd, tmp.get(), this, + gl::g_hw_blitter->scale_image(cmd, tmp.get(), dst, { 0, 0, subres.width_in_block, subres.height_in_block }, { 0, 0, static_cast(width()), static_cast(height()) }, !is_depth_surface(), {}); + + if (samples() > 1) + { + msaa_flags = rsx::surface_state_flags::require_unresolve; + } } + + state_flags &= ~rsx::surface_state_flags::erase_bkgnd; } -void gl::render_target::initialize_memory(gl::command_context& cmd, rsx::surface_access /*access*/) +void gl::render_target::initialize_memory(gl::command_context& cmd, rsx::surface_access access) { const bool memory_load = is_depth_surface() ? !!g_cfg.video.read_depth_buffer : @@ -469,6 +479,14 @@ void gl::render_target::initialize_memory(gl::command_context& cmd, rsx::surface if (!memory_load) { clear_memory(cmd); + + if (samples() > 1 && access.is_transfer_or_read()) + { + // Only clear the resolve surface if reading from it, otherwise it's a waste + clear_memory(cmd, get_resolve_target_safe(cmd)); + } + + msaa_flags = rsx::surface_state_flags::ready; } else { @@ -476,8 +494,28 @@ void gl::render_target::initialize_memory(gl::command_context& cmd, rsx::surface } } +gl::viewable_image* gl::render_target::get_surface(rsx::surface_access access_type) +{ + if (samples() == 1 || !access_type.is_transfer()) + { + return this; + } + + // A read barrier should have been called before this! + ensure(resolve_surface, "Read access without explicit barrier"); + ensure(!(msaa_flags & rsx::surface_state_flags::require_resolve)); + return static_cast(resolve_surface.get()); +} + void gl::render_target::memory_barrier(gl::command_context& cmd, rsx::surface_access access) { + if (access == rsx::surface_access::gpu_reference) + { + // In OpenGL, resources are always assumed to be visible to the GPU. + // We don't manage memory spilling, so just return. + return; + } + const bool read_access = access.is_read(); const bool is_depth = is_depth_surface(); const bool should_read_buffers = is_depth ? !!g_cfg.video.read_depth_buffer : !!g_cfg.video.read_color_buffers; @@ -504,12 +542,33 @@ void gl::render_target::memory_barrier(gl::command_context& cmd, rsx::surface_ac on_write(); } + if (msaa_flags & rsx::surface_state_flags::require_resolve) + { + if (access.is_transfer()) + { + // Only do this step when read access is required + get_resolve_target_safe(cmd); + resolve(cmd); + } + } + else if (msaa_flags & rsx::surface_state_flags::require_unresolve) + { + if (access == rsx::surface_access::shader_write) + { + // Only do this step when it is needed to start rendering + ensure(resolve_surface); + unresolve(cmd); + } + } + return; } + auto dst_img = (samples() > 1) ? get_resolve_target_safe(cmd) : this; const bool dst_is_depth = !!(aspect() & gl::image_aspect::depth); const auto dst_bpp = get_bpp(); unsigned first = prepare_rw_barrier_for_transfer(this); + bool optimize_copy = true; u64 newest_tag = 0; for (auto i = first; i < old_contents.size(); ++i) @@ -519,6 +578,8 @@ void gl::render_target::memory_barrier(gl::command_context& cmd, rsx::surface_ac const auto src_bpp = src_texture->get_bpp(); rsx::typeless_xfer typeless_info{}; + src_texture->memory_barrier(cmd, rsx::surface_access::transfer_read); + if (get_internal_format() == src_texture->get_internal_format()) { // Copy data from old contents onto this one @@ -538,29 +599,106 @@ void gl::render_target::memory_barrier(gl::command_context& cmd, rsx::surface_ac } section.init_transfer(this); + auto src_area = section.src_rect(); + auto dst_area = section.dst_rect(); - if (state_flags & rsx::surface_state_flags::erase_bkgnd) + if (g_cfg.video.antialiasing_level != msaa_level::none) { - const auto area = section.dst_rect(); - if (area.x1 > 0 || area.y1 > 0 || unsigned(area.x2) < width() || unsigned(area.y2) < height()) - { - initialize_memory(cmd, access); - } - else - { - state_flags &= ~rsx::surface_state_flags::erase_bkgnd; - } + src_texture->transform_pixels_to_samples(src_area); + this->transform_pixels_to_samples(dst_area); } - gl::g_hw_blitter->scale_image(cmd, section.source, this, - section.src_rect(), - section.dst_rect(), + bool memory_load = true; + if (dst_area.x1 == 0 && dst_area.y1 == 0 && + unsigned(dst_area.x2) == dst_img->width() && unsigned(dst_area.y2) == dst_img->height()) + { + // Skip a bunch of useless work + state_flags &= ~(rsx::surface_state_flags::erase_bkgnd); + msaa_flags = rsx::surface_state_flags::ready; + + memory_load = false; + stencil_init_flags = src_texture->stencil_init_flags; + } + else if (state_flags & rsx::surface_state_flags::erase_bkgnd) + { + // Might introduce MSAA flags + initialize_memory(cmd, rsx::surface_access::memory_write); + ensure(state_flags == rsx::surface_state_flags::ready); + } + + if (msaa_flags & rsx::surface_state_flags::require_resolve) + { + // Need to forward resolve this + resolve(cmd); + } + + if (src_texture->samples() > 1) + { + // Ensure a readable surface exists for the source + src_texture->get_resolve_target_safe(cmd); + } + + gl::g_hw_blitter->scale_image( + cmd, + src_texture->get_surface(rsx::surface_access::transfer_read), + this->get_surface(rsx::surface_access::transfer_write), + src_area, + dst_area, !dst_is_depth, typeless_info); + optimize_copy = optimize_copy && !memory_load; newest_tag = src_texture->last_use_tag; } - // Memory has been transferred, discard old contents and update memory flags - // TODO: Preserve memory outside surface clip region - on_write(newest_tag); + if (!newest_tag) [[unlikely]] + { + // Underlying memory has been modified and we could not find valid data to fill it + clear_rw_barrier(); + + state_flags |= rsx::surface_state_flags::erase_bkgnd; + initialize_memory(cmd, access); + ensure(state_flags == rsx::surface_state_flags::ready); + } + + // NOTE: Optimize flag relates to stencil resolve/unresolve for NVIDIA. + on_write_copy(newest_tag, optimize_copy); + + if (access == rsx::surface_access::shader_write && samples() > 1) + { + // Write barrier, must initialize + unresolve(cmd); + } +} + +// MSAA support +gl::viewable_image* gl::render_target::get_resolve_target_safe(gl::command_context& /*cmd*/) +{ + if (!resolve_surface) + { + // Create a resolve surface + const auto resolve_w = width() * samples_x; + const auto resolve_h = height() * samples_y; + + resolve_surface.reset(new gl::viewable_image( + GL_TEXTURE_2D, + resolve_w, resolve_h, + 1, 1, 1, + static_cast(get_internal_format()), + format_class() + )); + } + + return static_cast(resolve_surface.get()); +} + +void gl::render_target::resolve(gl::command_context& cmd) +{ + gl::resolve_image(cmd, get_resolve_target_safe(cmd), this); + msaa_flags &= ~(rsx::surface_state_flags::require_resolve); +} + +void gl::render_target::unresolve(gl::command_context& cmd) +{ + gl::unresolve_image(cmd, this, get_resolve_target_safe(cmd)); + msaa_flags &= ~(rsx::surface_state_flags::require_unresolve); } diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.h b/rpcs3/Emu/RSX/GL/GLRenderTargets.h index 20cefe206f..9b01746a87 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.h +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.h @@ -1,6 +1,5 @@ #pragma once #include "../Common/surface_store.h" -#include "GLHelpers.h" #include "../rsx_utils.h" #include "glutils/fbo.h" @@ -49,13 +48,21 @@ namespace gl { class render_target : public viewable_image, public rsx::render_target_descriptor { - void clear_memory(gl::command_context& cmd); + void clear_memory(gl::command_context& cmd, gl::texture* surface = nullptr); void load_memory(gl::command_context& cmd); void initialize_memory(gl::command_context& cmd, rsx::surface_access access); + // MSAA support: + // Get the linear resolve target bound to this surface. Initialize if none exists + gl::viewable_image* get_resolve_target_safe(gl::command_context& cmd); + // Resolve the planar MSAA data into a linear block + void resolve(gl::command_context& cmd); + // Unresolve the linear data into planar MSAA data + void unresolve(gl::command_context& cmd); + public: - render_target(GLuint width, GLuint height, GLenum sized_format, rsx::format_class format_class) - : viewable_image(GL_TEXTURE_2D, width, height, 1, 1, sized_format, format_class) + render_target(GLuint width, GLuint height, GLubyte samples, GLenum sized_format, rsx::format_class format_class) + : viewable_image(GL_TEXTURE_2D, width, height, 1, 1, samples, sized_format, format_class) {} // Internal pitch is the actual row length in bytes of the openGL texture @@ -81,11 +88,7 @@ namespace gl return !!(aspect() & gl::image_aspect::depth); } - viewable_image* get_surface(rsx::surface_access /*access_type*/) override - { - // TODO - return static_cast(this); - } + viewable_image* get_surface(rsx::surface_access /*access_type*/) override; u32 raw_handle() const { @@ -141,7 +144,20 @@ struct gl_render_target_traits auto format = rsx::internals::surface_color_format_to_gl(surface_color_format); const auto [width_, height_] = rsx::apply_resolution_scale(static_cast(width), static_cast(height)); - std::unique_ptr result(new gl::render_target(width_, height_, + u8 samples; + rsx::surface_sample_layout sample_layout; + if (g_cfg.video.antialiasing_level == msaa_level::_auto) + { + samples = get_format_sample_count(antialias); + sample_layout = rsx::surface_sample_layout::ps3; + } + else + { + samples = 1; + sample_layout = rsx::surface_sample_layout::null; + } + + std::unique_ptr result(new gl::render_target(width_, height_, samples, static_cast(format.internal_format), RSX_FORMAT_CLASS_COLOR)); result->set_aa_mode(antialias); @@ -154,6 +170,7 @@ struct gl_render_target_traits result->memory_usage_flags = rsx::surface_usage_flags::attachment; result->state_flags = rsx::surface_state_flags::erase_bkgnd; + result->sample_layout = sample_layout; result->queue_tag(address); result->add_ref(); return result; @@ -170,7 +187,20 @@ struct gl_render_target_traits auto format = rsx::internals::surface_depth_format_to_gl(surface_depth_format); const auto [width_, height_] = rsx::apply_resolution_scale(static_cast(width), static_cast(height)); - std::unique_ptr result(new gl::render_target(width_, height_, + u8 samples; + rsx::surface_sample_layout sample_layout; + if (g_cfg.video.antialiasing_level == msaa_level::_auto) + { + samples = get_format_sample_count(antialias); + sample_layout = rsx::surface_sample_layout::ps3; + } + else + { + samples = 1; + sample_layout = rsx::surface_sample_layout::null; + } + + std::unique_ptr result(new gl::render_target(width_, height_, samples, static_cast(format.internal_format), rsx::classify_format(surface_depth_format))); result->set_aa_mode(antialias); @@ -183,6 +213,7 @@ struct gl_render_target_traits result->memory_usage_flags = rsx::surface_usage_flags::attachment; result->state_flags = rsx::surface_state_flags::erase_bkgnd; + result->sample_layout = sample_layout; result->queue_tag(address); result->add_ref(); return result; @@ -200,7 +231,7 @@ struct gl_render_target_traits const auto [new_w, new_h] = rsx::apply_resolution_scale(prev.width, prev.height, ref->get_surface_width(), ref->get_surface_height()); - sink = std::make_unique(new_w, new_h, internal_format, ref->format_class()); + sink = std::make_unique(new_w, new_h, ref->samples(), internal_format, ref->format_class()); sink->add_ref(); sink->memory_usage_flags = rsx::surface_usage_flags::storage; @@ -255,8 +286,9 @@ struct gl_render_target_traits } static - void prepare_surface_for_drawing(gl::command_context&, gl::render_target* surface) + void prepare_surface_for_drawing(gl::command_context& cmd, gl::render_target* surface) { + surface->memory_barrier(cmd, rsx::surface_access::gpu_reference); surface->memory_usage_flags |= rsx::surface_usage_flags::attachment; } diff --git a/rpcs3/Emu/RSX/GL/GLResolveHelper.cpp b/rpcs3/Emu/RSX/GL/GLResolveHelper.cpp new file mode 100644 index 0000000000..52cdc39c77 --- /dev/null +++ b/rpcs3/Emu/RSX/GL/GLResolveHelper.cpp @@ -0,0 +1,391 @@ +#include "stdafx.h" +#include "GLResolveHelper.h" +#include "GLTexture.h" + +#include +#include + +namespace gl +{ + std::unordered_map> g_resolve_helpers; + std::unordered_map> g_unresolve_helpers; + std::unordered_map> g_depth_resolvers; + std::unordered_map> g_depth_unresolvers; + + void clear_resolve_helpers() + { + g_resolve_helpers.clear(); + g_unresolve_helpers.clear(); + g_depth_resolvers.clear(); + g_depth_unresolvers.clear(); + } + + static const char* get_format_string(gl::texture::internal_format format) + { + switch (format) + { + case texture::internal_format::rgb565: + return "r16"; + case texture::internal_format::rgba8: + case texture::internal_format::bgra8: + return "rgba8"; + case texture::internal_format::rgba16f: + return "rgba16f"; + case texture::internal_format::rgba32f: + return "rgba32f"; + case texture::internal_format::bgr5a1: + return "r16"; + case texture::internal_format::r8: + return "r8"; + case texture::internal_format::rg8: + return "rg8"; + case texture::internal_format::r32f: + return "r32f"; + default: + fmt::throw_exception("Unhandled internal format 0x%x", u32(format)); + } + } + + void resolve_image(gl::command_context& cmd, gl::viewable_image* dst, gl::viewable_image* src) + { + ensure(src->samples() > 1 && dst->samples() == 1); + + if (src->aspect() == gl::image_aspect::color) [[ likely ]] + { + auto& job = g_resolve_helpers[src->get_internal_format()]; + if (!job) + { + const auto fmt = get_format_string(src->get_internal_format()); + job.reset(new cs_resolve_task(fmt)); + } + + job->run(cmd, src, dst); + return; + } + + auto get_resolver_pass = [](GLuint aspect_bits) -> std::unique_ptr& + { + auto& pass = g_depth_resolvers[aspect_bits]; + if (!pass) + { + ds_resolve_pass_base* ptr = nullptr; + switch (aspect_bits) + { + case gl::image_aspect::depth: + ptr = new depth_only_resolver(); + break; + case gl::image_aspect::stencil: + ptr = new stencil_only_resolver(); + break; + case (gl::image_aspect::depth | gl::image_aspect::stencil): + ptr = new depth_stencil_resolver(); + break; + default: + fmt::throw_exception("Unreachable"); + } + + pass.reset(ptr); + } + + return pass; + }; + + if (src->aspect() == (gl::image_aspect::depth | gl::image_aspect::stencil) && + !gl::get_driver_caps().ARB_shader_stencil_export_supported) + { + // Special case, NVIDIA-only fallback + auto& depth_pass = get_resolver_pass(gl::image_aspect::depth); + depth_pass->run(cmd, src, dst); + + auto& stencil_pass = get_resolver_pass(gl::image_aspect::stencil); + stencil_pass->run(cmd, src, dst); + return; + } + + auto& pass = get_resolver_pass(src->aspect()); + pass->run(cmd, src, dst); + } + + void unresolve_image(gl::command_context& cmd, gl::viewable_image* dst, gl::viewable_image* src) + { + ensure(dst->samples() > 1 && src->samples() == 1); + + if (src->aspect() == gl::image_aspect::color) [[ likely ]] + { + auto& job = g_unresolve_helpers[src->get_internal_format()]; + if (!job) + { + const auto fmt = get_format_string(src->get_internal_format()); + job.reset(new cs_unresolve_task(fmt)); + } + + job->run(cmd, dst, src); + return; + } + + auto get_unresolver_pass = [](GLuint aspect_bits) -> std::unique_ptr& + { + auto& pass = g_depth_unresolvers[aspect_bits]; + if (!pass) + { + ds_resolve_pass_base* ptr = nullptr; + switch (aspect_bits) + { + case gl::image_aspect::depth: + ptr = new depth_only_unresolver(); + break; + case gl::image_aspect::stencil: + ptr = new stencil_only_unresolver(); + break; + case (gl::image_aspect::depth | gl::image_aspect::stencil): + ptr = new depth_stencil_unresolver(); + break; + default: + fmt::throw_exception("Unreachable"); + } + + pass.reset(ptr); + } + + return pass; + }; + + if (src->aspect() == (gl::image_aspect::depth | gl::image_aspect::stencil) && + !gl::get_driver_caps().ARB_shader_stencil_export_supported) + { + // Special case, NVIDIA-only fallback + auto& depth_pass = get_unresolver_pass(gl::image_aspect::depth); + depth_pass->run(cmd, dst, src); + + auto& stencil_pass = get_unresolver_pass(gl::image_aspect::stencil); + stencil_pass->run(cmd, dst, src); + return; + } + + auto& pass = get_unresolver_pass(src->aspect()); + pass->run(cmd, dst, src); + } + + // Implementation + + void cs_resolve_base::build(const std::string& format_prefix, bool unresolve) + { + is_unresolve = unresolve; + + switch (optimal_group_size) + { + default: + case 64: + cs_wave_x = 8; + cs_wave_y = 8; + break; + case 32: + cs_wave_x = 8; + cs_wave_y = 4; + break; + } + + static const char* resolve_kernel = + #include "Emu/RSX/Program/MSAA/ColorResolvePass.glsl" + ; + + static const char* unresolve_kernel = + #include "Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl" + ; + + const std::pair syntax_replace[] = + { + { "%WORKGROUP_SIZE_X", std::to_string(cs_wave_x) }, + { "%WORKGROUP_SIZE_Y", std::to_string(cs_wave_y) }, + { "%IMAGE_FORMAT", format_prefix }, + { "%BGRA_SWAP", "0" } + }; + + m_src = unresolve ? unresolve_kernel : resolve_kernel; + m_src = fmt::replace_all(m_src, syntax_replace); + + rsx_log.notice("Resolve shader:\n%s", m_src); + + create(); + } + + void cs_resolve_base::bind_resources() + { + auto msaa_view = multisampled->get_view(rsx::default_remap_vector.with_encoding(GL_REMAP_VIEW_MULTISAMPLED)); + auto resolved_view = resolve->get_view(rsx::default_remap_vector.with_encoding(GL_REMAP_IDENTITY)); + + glBindImageTexture(GL_COMPUTE_IMAGE_SLOT(0), msaa_view->id(), 0, GL_FALSE, 0, is_unresolve ? GL_WRITE_ONLY : GL_READ_ONLY, msaa_view->view_format()); + glBindImageTexture(GL_COMPUTE_IMAGE_SLOT(1), resolved_view->id(), 0, GL_FALSE, 0, is_unresolve ? GL_READ_ONLY : GL_WRITE_ONLY, resolved_view->view_format()); + } + + void cs_resolve_base::run(gl::command_context& cmd, gl::viewable_image* msaa_image, gl::viewable_image* resolve_image) + { + ensure(msaa_image->samples() > 1); + ensure(resolve_image->samples() == 1); + + multisampled = msaa_image; + resolve = resolve_image; + + const u32 invocations_x = utils::align(resolve_image->width(), cs_wave_x) / cs_wave_x; + const u32 invocations_y = utils::align(resolve_image->height(), cs_wave_y) / cs_wave_y; + + compute_task::run(cmd, invocations_x, invocations_y); + } + + void ds_resolve_pass_base::build(bool depth, bool stencil, bool unresolve) + { + m_config.resolve_depth = depth; + m_config.resolve_stencil = stencil; + m_config.is_unresolve = unresolve; + + vs_src = +#include "Emu/RSX/Program/GLSLSnippets/GenericVSPassthrough.glsl" + ; + + static const char* depth_resolver = +#include "Emu/RSX/Program/MSAA/DepthResolvePass.glsl" + ; + + static const char* depth_unresolver = +#include "Emu/RSX/Program/MSAA/DepthUnresolvePass.glsl" + ; + + static const char* stencil_resolver = +#include "Emu/RSX/Program/MSAA/StencilResolvePass.glsl" + ; + + static const char* stencil_unresolver = +#include "Emu/RSX/Program/MSAA/StencilUnresolvePass.glsl" + ; + + static const char* depth_stencil_resolver = +#include "Emu/RSX/Program/MSAA/DepthStencilResolvePass.glsl" + ; + + static const char* depth_stencil_unresolver = +#include "Emu/RSX/Program/MSAA/DepthStencilUnresolvePass.glsl" + ; + + if (m_config.resolve_depth && m_config.resolve_stencil) + { + fs_src = m_config.is_unresolve ? depth_stencil_unresolver : depth_stencil_resolver; + m_write_aspect_mask = gl::image_aspect::depth | gl::image_aspect::stencil; + } + else if (m_config.resolve_depth) + { + fs_src = m_config.is_unresolve ? depth_unresolver : depth_resolver; + m_write_aspect_mask = gl::image_aspect::depth; + } + else if (m_config.resolve_stencil) + { + fs_src = m_config.is_unresolve ? stencil_unresolver : stencil_resolver; + m_write_aspect_mask = gl::image_aspect::stencil; + } + + enable_depth_writes = m_config.resolve_depth; + enable_stencil_writes = m_config.resolve_stencil; + + + create(); + + rsx_log.notice("Resolve shader:\n%s", fs_src); + } + + void ds_resolve_pass_base::update_config() + { + ensure(multisampled && multisampled->samples() > 1); + switch (multisampled->samples()) + { + case 2: + m_config.sample_count.x = 2; + m_config.sample_count.y = 1; + break; + case 4: + m_config.sample_count.x = m_config.sample_count.y = 2; + break; + default: + fmt::throw_exception("Unsupported sample count %d", multisampled->samples()); + } + + program_handle.uniforms["sample_count"] = m_config.sample_count; + } + + void ds_resolve_pass_base::run(gl::command_context& cmd, gl::viewable_image* msaa_image, gl::viewable_image* resolve_image) + { + multisampled = msaa_image; + resolve = resolve_image; + update_config(); + + const auto read_resource = m_config.is_unresolve ? resolve_image : msaa_image; + const auto write_resource = m_config.is_unresolve ? msaa_image : resolve_image; + + // Resource binding + std::stack bind_slots; + std::vector> saved_sampler_states; + auto allocate_slot = [&]() -> int + { + ensure(!bind_slots.empty()); + const int slot = bind_slots.top(); + bind_slots.pop(); + saved_sampler_states.emplace_back(std::make_unique(slot, m_sampler)); + return slot; + }; + + // Reserve 2 slots max + bind_slots.push(GL_TEMP_IMAGE_SLOT(1)); + bind_slots.push(GL_TEMP_IMAGE_SLOT(0)); + + if (m_config.resolve_depth) + { + const int bind_slot = allocate_slot(); + cmd->bind_texture(bind_slot, static_cast(read_resource->get_target()), read_resource->id(), GL_TRUE); + } + + if (m_config.resolve_stencil) + { + const int bind_slot = allocate_slot(); + auto stencil_view = read_resource->get_view(rsx::default_remap_vector.with_encoding(gl::GL_REMAP_IDENTITY), gl::image_aspect::stencil); + cmd->bind_texture(bind_slot, static_cast(read_resource->get_target()), stencil_view->id(), GL_TRUE); + } + + areau viewport{}; + viewport.x2 = write_resource->width(); + viewport.y2 = write_resource->height(); + overlay_pass::run(cmd, viewport, write_resource->id(), m_write_aspect_mask, false); + } + + void stencil_only_resolver_base::emit_geometry(gl::command_context& cmd) + { + // Modified version of the base overlay pass to emit 8 draws instead of 1 + int old_vao; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao); + m_vao.bind(); + + // Clear the target + gl::clear_cmd_info clear_info + { + .aspect_mask = gl::image_aspect::stencil, + .clear_stencil = { + .mask = 0xFF, + .value = 0 + } + }; + gl::clear_attachments(cmd, clear_info); + + // Override stencil settings. Always pass, reference is all one, compare mask doesn't matter. + // For each pass the write mask will be overriden to commit output bitwise + cmd->stencil_func(GL_ALWAYS, 0xFF, 0xFF); + cmd->stencil_op(GL_REPLACE, GL_REPLACE, GL_REPLACE); + + // Start our inner loop + for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1) + { + program_handle.uniforms["stencil_mask"] = write_mask; + cmd->stencil_mask(write_mask); + + glDrawArrays(primitives, 0, num_drawable_elements); + } + + glBindVertexArray(old_vao); + } +} diff --git a/rpcs3/Emu/RSX/GL/GLResolveHelper.h b/rpcs3/Emu/RSX/GL/GLResolveHelper.h new file mode 100644 index 0000000000..8799efbf25 --- /dev/null +++ b/rpcs3/Emu/RSX/GL/GLResolveHelper.h @@ -0,0 +1,129 @@ +#pragma once + +#include "GLCompute.h" +#include "GLOverlays.h" + +namespace gl +{ + void resolve_image(gl::command_context& cmd, gl::viewable_image* dst, gl::viewable_image* src); + void unresolve_image(gl::command_context& cmd, gl::viewable_image* dst, gl::viewable_image* src); + void clear_resolve_helpers(); + + struct cs_resolve_base : compute_task + { + gl::viewable_image* multisampled = nullptr; + gl::viewable_image* resolve = nullptr; + bool is_unresolve = false; + + u32 cs_wave_x = 1; + u32 cs_wave_y = 1; + + cs_resolve_base() + {} + + virtual ~cs_resolve_base() + {} + + void build(const std::string& format_prefix, bool unresolve); + + void bind_resources() override; + + void run(gl::command_context& cmd, gl::viewable_image* msaa_image, gl::viewable_image* resolve_image); + }; + + struct cs_resolve_task : cs_resolve_base + { + cs_resolve_task(const std::string& format_prefix) + { + build(format_prefix, false); + } + }; + + struct cs_unresolve_task : cs_resolve_base + { + cs_unresolve_task(const std::string& format_prefix) + { + build(format_prefix, true); + } + }; + + struct ds_resolve_pass_base : overlay_pass + { + gl::viewable_image* multisampled = nullptr; + gl::viewable_image* resolve = nullptr; + + struct + { + bool resolve_depth = false; + bool resolve_stencil = false; + bool is_unresolve = false; + color2i sample_count; + } m_config; + + void build(bool depth, bool stencil, bool unresolve); + + void update_config(); + + void run(gl::command_context& cmd, gl::viewable_image* msaa_image, gl::viewable_image* resolve_image); + }; + + struct depth_only_resolver : ds_resolve_pass_base + { + depth_only_resolver() + { + build(true, false, false); + } + }; + + struct depth_only_unresolver : ds_resolve_pass_base + { + depth_only_unresolver() + { + build(true, false, true); + } + }; + + struct stencil_only_resolver_base : ds_resolve_pass_base + { + virtual ~stencil_only_resolver_base() = default; + + void build(bool is_unresolver) + { + ds_resolve_pass_base::build(false, true, is_unresolver); + } + + void emit_geometry(gl::command_context& cmd) override; + }; + + struct stencil_only_resolver : stencil_only_resolver_base + { + stencil_only_resolver() + { + build(false); + } + }; + + struct stencil_only_unresolver : stencil_only_resolver_base + { + stencil_only_unresolver() + { + build(true); + } + }; + + struct depth_stencil_resolver : ds_resolve_pass_base + { + depth_stencil_resolver() + { + build(true, true, false); + } + }; + + struct depth_stencil_unresolver : ds_resolve_pass_base + { + depth_stencil_unresolver() + { + build(true, true, true); + } + }; +} diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp index 0687798efd..5d30ae5ab3 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp @@ -1,6 +1,6 @@ #include "stdafx.h" #include "GLShaderInterpreter.h" -#include "GLGSRender.h" +#include "GLTextureCache.h" #include "GLVertexProgram.h" #include "GLFragmentProgram.h" #include "../rsx_methods.h" diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h index 2c15010192..551fa9a8cf 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h @@ -1,5 +1,4 @@ #pragma once -#include "GLHelpers.h" #include "glutils/program.h" #include "../Program/ProgramStateCache.h" #include "../Common/TextureUtils.h" diff --git a/rpcs3/Emu/RSX/GL/GLTexture.cpp b/rpcs3/Emu/RSX/GL/GLTexture.cpp index 3847ca0e3a..74ffd7bf7b 100644 --- a/rpcs3/Emu/RSX/GL/GLTexture.cpp +++ b/rpcs3/Emu/RSX/GL/GLTexture.cpp @@ -1,16 +1,13 @@ #include "stdafx.h" #include "GLTexture.h" #include "GLCompute.h" -#include "GLRenderTargets.h" #include "GLOverlays.h" #include "GLGSRender.h" #include "glutils/blitter.h" #include "glutils/ring_buffer.h" -#include "../GCM.h" #include "../RSXThread.h" -#include "../RSXTexture.h" #include "util/asm.hpp" @@ -429,7 +426,7 @@ namespace gl image_region.height *= dst_region.depth; scratch = std::make_unique( GL_TEXTURE_2D, - image_region.x + image_region.width, image_region.y + image_region.height, 1, 1, + image_region.x + image_region.width, image_region.y + image_region.height, 1, 1, 1, static_cast(dst->get_internal_format()), dst->format_class()); scratch_view = std::make_unique(scratch.get()); @@ -445,7 +442,7 @@ namespace gl { scratch = std::make_unique( GL_TEXTURE_2D, - image_region.x + image_region.width, 1, 1, 1, + image_region.x + image_region.width, 1, 1, 1, 1, static_cast(dst->get_internal_format()), dst->format_class()); scratch_view = std::make_unique(scratch.get()); @@ -576,7 +573,7 @@ namespace gl const GLenum internal_format = get_sized_internal_format(gcm_format); const auto format_class = rsx::classify_format(gcm_format); - return new gl::viewable_image(target, width, height, depth, mipmaps, internal_format, format_class); + return new gl::viewable_image(target, width, height, depth, mipmaps, 1, internal_format, format_class); } void fill_texture(gl::command_context& cmd, texture* dst, int format, diff --git a/rpcs3/Emu/RSX/GL/GLTexture.h b/rpcs3/Emu/RSX/GL/GLTexture.h index bcf52f21fa..dc6d90098a 100644 --- a/rpcs3/Emu/RSX/GL/GLTexture.h +++ b/rpcs3/Emu/RSX/GL/GLTexture.h @@ -1,12 +1,9 @@ #pragma once #include "OpenGL.h" -#include "../GCM.h" #include "../Common/TextureUtils.h" #include "GLHelpers.h" -#include - namespace rsx { class vertex_texture; diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.cpp b/rpcs3/Emu/RSX/GL/GLTextureCache.cpp index 7da909ad97..f3103dac5c 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.cpp +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.cpp @@ -1,5 +1,4 @@ #include "stdafx.h" -#include "Emu/RSX/RSXThread.h" #include "GLTexture.h" #include "GLTextureCache.h" #include "../Common/BufferUtils.h" @@ -149,7 +148,7 @@ namespace gl if (!dst) { - std::unique_ptr data = std::make_unique(dst_target, width, height, depth, mipmaps, sized_internal_fmt, rsx::classify_format(gcm_format)); + std::unique_ptr data = std::make_unique(dst_target, width, height, depth, mipmaps, 1, sized_internal_fmt, rsx::classify_format(gcm_format)); dst = data.get(); dst->properties_encoding = match_key; m_temporary_surfaces.emplace_back(std::move(data)); @@ -223,7 +222,12 @@ namespace gl { const auto src_bpp = slice.src->pitch() / slice.src->width(); const u16 convert_w = u16(slice.src->width() * src_bpp) / dst_bpp; - tmp = std::make_unique(GL_TEXTURE_2D, convert_w, slice.src->height(), 1, 1, static_cast(dst_image->get_internal_format()), dst_image->format_class()); + tmp = std::make_unique( + GL_TEXTURE_2D, + convert_w, slice.src->height(), + 1, 1, 1, + static_cast(dst_image->get_internal_format()), + dst_image->format_class()); src_image = tmp.get(); @@ -264,9 +268,17 @@ namespace gl const areai dst_rect = { slice.dst_x, slice.dst_y, slice.dst_x + slice.dst_w, slice.dst_y + slice.dst_h }; gl::texture* _dst = dst_image; - if (src_image->get_internal_format() != dst_image->get_internal_format() || slice.level != 0 || slice.dst_z != 0) [[ unlikely ]] + if (src_image->get_internal_format() != dst_image->get_internal_format() || + slice.level != 0 || + slice.dst_z != 0) [[ unlikely ]] { - tmp = std::make_unique(GL_TEXTURE_2D, dst_rect.x2, dst_rect.y2, 1, 1, static_cast(slice.src->get_internal_format())); + tmp = std::make_unique( + GL_TEXTURE_2D, + dst_rect.x2, dst_rect.y2, + 1, 1, 1, + static_cast(slice.src->get_internal_format()), + slice.src->format_class()); + _dst = tmp.get(); } diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.h b/rpcs3/Emu/RSX/GL/GLTextureCache.h index fbb9b27a04..7348fd5936 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.h +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.h @@ -262,28 +262,24 @@ namespace gl baseclass::on_miss(); } + gl::texture* target_texture = vram_texture; + u32 transfer_width = width; + u32 transfer_height = height; + if (context == rsx::texture_upload_context::framebuffer_storage) { - auto as_rtt = static_cast(vram_texture); - if (as_rtt->dirty()) as_rtt->read_barrier(cmd); + auto surface = gl::as_rtt(vram_texture); + surface->memory_barrier(cmd, rsx::surface_access::transfer_read); + target_texture = surface->get_surface(rsx::surface_access::transfer_read); + transfer_width *= surface->samples_x; + transfer_height *= surface->samples_y; } - gl::texture* target_texture = vram_texture; if ((rsx::get_resolution_scale_percent() != 100 && context == rsx::texture_upload_context::framebuffer_storage) || (vram_texture->pitch() != rsx_pitch)) { - u32 real_width = width; - u32 real_height = height; - - if (context == rsx::texture_upload_context::framebuffer_storage) - { - auto surface = gl::as_rtt(vram_texture); - real_width *= surface->samples_x; - real_height *= surface->samples_y; - } - areai src_area = { 0, 0, 0, 0 }; - const areai dst_area = { 0, 0, static_cast(real_width), static_cast(real_height) }; + const areai dst_area = { 0, 0, static_cast(transfer_width), static_cast(transfer_height) }; auto ifmt = vram_texture->get_internal_format(); src_area.x2 = vram_texture->width(); @@ -294,22 +290,22 @@ namespace gl if (scaled_texture) { auto sfmt = scaled_texture->get_internal_format(); - if (scaled_texture->width() != real_width || - scaled_texture->height() != real_height || + if (scaled_texture->width() != transfer_width || + scaled_texture->height() != transfer_height || sfmt != ifmt) { - //Discard current scaled texture + // Discard current scaled texture scaled_texture.reset(); } } if (!scaled_texture) { - scaled_texture = std::make_unique(GL_TEXTURE_2D, real_width, real_height, 1, 1, static_cast(ifmt)); + scaled_texture = std::make_unique(GL_TEXTURE_2D, transfer_width, transfer_height, 1, 1, 1, static_cast(ifmt), vram_texture->format_class()); } const bool linear_interp = is_depth_texture() ? false : true; - g_hw_blitter->scale_image(cmd, vram_texture, scaled_texture.get(), src_area, dst_area, linear_interp, {}); + g_hw_blitter->scale_image(cmd, target_texture, scaled_texture.get(), src_area, dst_area, linear_interp, {}); target_texture = scaled_texture.get(); } } diff --git a/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp b/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp index fce7786b30..79286bd69d 100644 --- a/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp @@ -1,14 +1,11 @@ #include "stdafx.h" #include "GLVertexProgram.h" -#include "Emu/System.h" #include "Emu/system_config.h" #include "GLCommonDecompiler.h" #include "../Program/GLSLCommon.h" -#include - std::string GLVertexDecompilerThread::getFloatTypeName(usz elementCount) { return glsl::getFloatTypeNameImpl(elementCount); @@ -73,7 +70,31 @@ void GLVertexDecompilerThread::insertConstants(std::stringstream& OS, const std: continue; } - OS << "uniform " << PT.type << " " << PI.name << ";\n"; + auto type = PT.type; + + if (PT.type == "sampler2D" || + PT.type == "samplerCube" || + PT.type == "sampler1D" || + PT.type == "sampler3D") + { + if (m_prog.texture_state.multisampled_textures) [[ unlikely ]] + { + ensure(PI.name.length() > 3); + int index = atoi(&PI.name[3]); + + if (m_prog.texture_state.multisampled_textures & (1 << index)) + { + if (type != "sampler1D" && type != "sampler2D") + { + rsx_log.error("Unexpected multisampled sampler type '%s'", type); + } + + type = "sampler2DMS"; + } + } + } + + OS << "uniform " << type << " " << PI.name << ";\n"; } } } diff --git a/rpcs3/Emu/RSX/GL/GLVertexProgram.h b/rpcs3/Emu/RSX/GL/GLVertexProgram.h index 7a630d0cd3..631b6ac245 100644 --- a/rpcs3/Emu/RSX/GL/GLVertexProgram.h +++ b/rpcs3/Emu/RSX/GL/GLVertexProgram.h @@ -1,6 +1,5 @@ #pragma once #include "../Program/VertexProgramDecompiler.h" -#include "GLHelpers.h" #include "glutils/program.h" #include diff --git a/rpcs3/Emu/RSX/GL/glutils/blitter.cpp b/rpcs3/Emu/RSX/GL/glutils/blitter.cpp index e5a36d11cf..57998b761d 100644 --- a/rpcs3/Emu/RSX/GL/glutils/blitter.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/blitter.cpp @@ -3,7 +3,6 @@ #include "state_tracker.hpp" #include "../GLTexture.h" // TODO: This system also needs to be refactored -#include "../GLOverlays.h" namespace gl { @@ -67,7 +66,7 @@ namespace gl if (static_cast(internal_fmt) != src->get_internal_format()) { const u16 internal_width = static_cast(src->width() * xfer_info.src_scaling_hint); - typeless_src = std::make_unique(GL_TEXTURE_2D, internal_width, src->height(), 1, 1, internal_fmt); + typeless_src = std::make_unique(GL_TEXTURE_2D, internal_width, src->height(), 1, 1, 1, internal_fmt, RSX_FORMAT_CLASS_DONT_CARE); copy_typeless(cmd, typeless_src.get(), src); real_src = typeless_src.get(); @@ -85,7 +84,7 @@ namespace gl if (static_cast(internal_fmt) != dst->get_internal_format()) { const auto internal_width = static_cast(dst->width() * xfer_info.dst_scaling_hint); - typeless_dst = std::make_unique(GL_TEXTURE_2D, internal_width, dst->height(), 1, 1, internal_fmt); + typeless_dst = std::make_unique(GL_TEXTURE_2D, internal_width, dst->height(), 1, 1, 1, internal_fmt, RSX_FORMAT_CLASS_DONT_CARE); copy_typeless(cmd, typeless_dst.get(), dst); real_dst = typeless_dst.get(); diff --git a/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp b/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp index 284014f613..d049359b84 100644 --- a/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "buffer_object.h" +#include "common.h" namespace gl { diff --git a/rpcs3/Emu/RSX/GL/glutils/buffer_object.h b/rpcs3/Emu/RSX/GL/glutils/buffer_object.h index e559318be9..dccb2a314e 100644 --- a/rpcs3/Emu/RSX/GL/glutils/buffer_object.h +++ b/rpcs3/Emu/RSX/GL/glutils/buffer_object.h @@ -1,6 +1,6 @@ #pragma once -#include "common.h" +#include "Emu/RSX/GL/OpenGL.h" namespace gl { diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp index f19ab3332b..0c8de71786 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp @@ -33,7 +33,7 @@ namespace gl void capabilities::initialize() { - int find_count = 17; + int find_count = 18; int ext_count = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &ext_count); @@ -171,6 +171,13 @@ namespace gl find_count--; continue; } + + if (check(ext_name, "GL_ARB_shader_texture_image_samples")) + { + ARB_shader_texture_image_samples = true; + find_count--; + continue; + } } // Set GLSL version diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.h b/rpcs3/Emu/RSX/GL/glutils/capabilities.h index c6abfaf3b6..801a426d80 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.h +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.h @@ -40,6 +40,7 @@ namespace gl bool ARB_compute_shader_supported = false; bool NV_depth_buffer_float_supported = false; bool NV_fragment_shader_barycentric_supported = false; + bool ARB_shader_texture_image_samples = false; bool vendor_INTEL = false; // has broken GLSL compiler bool vendor_AMD = false; // has broken ARB_multidraw diff --git a/rpcs3/Emu/RSX/GL/glutils/fbo.h b/rpcs3/Emu/RSX/GL/glutils/fbo.h index 9837c425cd..ef71bfa469 100644 --- a/rpcs3/Emu/RSX/GL/glutils/fbo.h +++ b/rpcs3/Emu/RSX/GL/glutils/fbo.h @@ -125,7 +125,9 @@ namespace gl void operator = (const texture& rhs) { - ensure(rhs.get_target() == texture::target::texture2D); + ensure(rhs.get_target() == texture::target::texture2D || + rhs.get_target() == texture::target::texture2DMS); + m_parent.m_resource_bindings[m_id] = rhs.id(); DSA_CALL2(NamedFramebufferTexture, m_parent.id(), m_id, rhs.id(), 0); } diff --git a/rpcs3/Emu/RSX/GL/glutils/image.cpp b/rpcs3/Emu/RSX/GL/glutils/image.cpp index 7876cf6dc3..0e25301afe 100644 --- a/rpcs3/Emu/RSX/GL/glutils/image.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/image.cpp @@ -19,8 +19,24 @@ namespace gl } } - texture::texture(GLenum target, GLuint width, GLuint height, GLuint depth, GLuint mipmaps, GLenum sized_format, rsx::format_class format_class) + texture::texture(GLenum target, GLuint width, GLuint height, GLuint depth, GLuint mipmaps, GLubyte samples, GLenum sized_format, rsx::format_class format_class) { + // Upgrade targets for MSAA + if (samples > 1) + { + switch (target) + { + case GL_TEXTURE_2D: + target = GL_TEXTURE_2D_MULTISAMPLE; + break; + case GL_TEXTURE_2D_ARRAY: + target = GL_TEXTURE_2D_MULTISAMPLE_ARRAY; + break; + default: + fmt::throw_exception("MSAA is only supported on 2D images. Target=0x%x", target); + } + } + glGenTextures(1, &m_id); // Must bind to initialize the new texture @@ -40,30 +56,45 @@ namespace gl glTexStorage2D(target, mipmaps, storage_fmt, width, height); depth = 1; break; + case GL_TEXTURE_2D_MULTISAMPLE: + ensure(mipmaps == 1); + glTexStorage2DMultisample(target, samples, storage_fmt, width, height, GL_TRUE); + depth = 1; + break; case GL_TEXTURE_3D: case GL_TEXTURE_2D_ARRAY: glTexStorage3D(target, mipmaps, storage_fmt, width, height, depth); break; + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + ensure(mipmaps == 1); + glTexStorage3DMultisample(target, samples, storage_fmt, width, height, depth, GL_TRUE); + break; case GL_TEXTURE_BUFFER: break; } if (target != GL_TEXTURE_BUFFER) { - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT); - glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1); + if (samples == 1) + { + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1); + } m_width = width; m_height = height; m_depth = depth; m_mipmaps = mipmaps; + m_samples = samples; m_aspect_flags = image_aspect::color; + ensure(width > 0 && height > 0 && depth > 0 && mipmaps > 0 && samples > 0, "Invalid OpenGL texture definition."); + switch (storage_fmt) { case GL_DEPTH_COMPONENT16: @@ -146,6 +177,8 @@ namespace gl void texture::copy_from(const void* src, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings) { + ensure(m_samples <= 1, "Transfer operations are unsupported on multisampled textures."); + pixel_settings.apply(); switch (const auto target_ = static_cast(m_target)) @@ -190,6 +223,8 @@ namespace gl void texture::copy_from(buffer& buf, u32 gl_format_type, u32 offset, u32 length) { + ensure(m_samples <= 1, "Transfer operations are unsupported on multisampled textures."); + if (get_target() != target::textureBuffer) fmt::throw_exception("OpenGL error: texture cannot copy from buffer"); @@ -203,6 +238,8 @@ namespace gl void texture::copy_to(void* dst, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const { + ensure(m_samples <= 1, "Transfer operations are unsupported on multisampled textures."); + pixel_settings.apply(); const auto& caps = get_driver_caps(); @@ -223,7 +260,7 @@ namespace gl { // Worst case scenario. For some reason, EXT_dsa does not have glGetTextureSubImage const auto target_ = static_cast(m_target); - texture tmp{ target_, region.width, region.height, region.depth, 1, static_cast(m_internal_format) }; + texture tmp{ target_, region.width, region.height, region.depth, 1, 1, static_cast(m_internal_format), m_format_class }; glCopyImageSubData(m_id, target_, level, region.x, region.y, region.z, tmp.id(), target_, 0, 0, 0, 0, region.width, region.height, region.depth); diff --git a/rpcs3/Emu/RSX/GL/glutils/image.h b/rpcs3/Emu/RSX/GL/glutils/image.h index 791762d558..896c3ee088 100644 --- a/rpcs3/Emu/RSX/GL/glutils/image.h +++ b/rpcs3/Emu/RSX/GL/glutils/image.h @@ -45,7 +45,8 @@ namespace gl enum remap_constants : u32 { GL_REMAP_IDENTITY = 0xCAFEBABE, - GL_REMAP_BGRA = 0x0000AA6C + GL_REMAP_BGRA = 0x0000AA6C, + GL_REMAP_VIEW_MULTISAMPLED = 0xDEADBEEF }; struct subresource_range @@ -174,7 +175,8 @@ namespace gl texture3D = GL_TEXTURE_3D, textureCUBE = GL_TEXTURE_CUBE_MAP, textureBuffer = GL_TEXTURE_BUFFER, - texture2DArray = GL_TEXTURE_2D_ARRAY + texture2DArray = GL_TEXTURE_2D_ARRAY, + texture2DMS = GL_TEXTURE_2D_MULTISAMPLE }; protected: @@ -183,6 +185,7 @@ namespace gl GLuint m_height = 0; GLuint m_depth = 0; GLuint m_mipmaps = 0; + GLubyte m_samples = 0; GLuint m_pitch = 0; GLuint m_compressed = GL_FALSE; GLuint m_aspect_flags = 0; @@ -197,7 +200,7 @@ namespace gl texture(const texture&) = delete; texture(texture&& texture_) = delete; - texture(GLenum target, GLuint width, GLuint height, GLuint depth, GLuint mipmaps, GLenum sized_format, rsx::format_class format_class = rsx::RSX_FORMAT_CLASS_UNDEFINED); + texture(GLenum target, GLuint width, GLuint height, GLuint depth, GLuint mipmaps, GLubyte samples, GLenum sized_format, rsx::format_class format_class); virtual ~texture(); // Getters/setters @@ -276,9 +279,9 @@ namespace gl return m_pitch; } - constexpr GLubyte samples() const + GLubyte samples() const { - return 1; + return m_samples; } GLboolean compressed() const diff --git a/rpcs3/Emu/RSX/GL/glutils/sampler.cpp b/rpcs3/Emu/RSX/GL/glutils/sampler.cpp index 144aab97d4..580caf0dc2 100644 --- a/rpcs3/Emu/RSX/GL/glutils/sampler.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/sampler.cpp @@ -2,7 +2,6 @@ #include "sampler.h" #include "Emu/RSX/gcm_enums.h" -#include "Emu/RSX/rsx_utils.h" #include "Emu/RSX/Common/TextureUtils.h" //GLenum wrap_mode(rsx::texture_wrap_mode wrap); diff --git a/rpcs3/Emu/RSX/GL/glutils/state_tracker.hpp b/rpcs3/Emu/RSX/GL/glutils/state_tracker.hpp index 8020b45936..ffc4fe38f1 100644 --- a/rpcs3/Emu/RSX/GL/glutils/state_tracker.hpp +++ b/rpcs3/Emu/RSX/GL/glutils/state_tracker.hpp @@ -309,6 +309,32 @@ namespace gl } } + void sample_mask(GLbitfield mask) + { + if (!test_and_set_property(GL_SAMPLE_MASK_VALUE, mask)) + { + glSampleMaski(0, mask); + } + } + + void sample_coverage(GLclampf coverage) + { + const u32 value = std::bit_cast(coverage); + if (!test_and_set_property(GL_SAMPLE_COVERAGE_VALUE, value)) + { + glSampleCoverage(coverage, GL_FALSE); + } + } + + void min_sample_shading_rate(GLclampf rate) + { + const u32 value = std::bit_cast(rate); + if (!test_and_set_property(GL_MIN_SAMPLE_SHADING_VALUE, value)) + { + glMinSampleShading(rate); + } + } + void clip_planes(GLuint mask) { if (!test_and_set_property(CLIP_PLANES, mask)) diff --git a/rpcs3/Emu/RSX/GL/upscalers/fsr1/fsr_pass.cpp b/rpcs3/Emu/RSX/GL/upscalers/fsr1/fsr_pass.cpp index adf25b3f12..cfd7b1cc08 100644 --- a/rpcs3/Emu/RSX/GL/upscalers/fsr1/fsr_pass.cpp +++ b/rpcs3/Emu/RSX/GL/upscalers/fsr1/fsr_pass.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" +#include "../../glutils/fbo.h" #include "../fsr_pass.h" #if defined(__GNUC__) @@ -177,7 +178,7 @@ namespace gl { return std::make_unique( GL_TEXTURE_2D, - output_w, output_h, 1, 1, + output_w, output_h, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); }; diff --git a/rpcs3/Emu/RSX/GL/upscalers/upscaling.h b/rpcs3/Emu/RSX/GL/upscalers/upscaling.h index 4c90536668..9c8eec6439 100644 --- a/rpcs3/Emu/RSX/GL/upscalers/upscaling.h +++ b/rpcs3/Emu/RSX/GL/upscalers/upscaling.h @@ -1,8 +1,5 @@ #pragma once -#include "util/types.hpp" - -#include "../glutils/fbo.h" #include "../glutils/image.h" #include "../glutils/state_tracker.hpp" diff --git a/rpcs3/Emu/RSX/GSFrameBase.h b/rpcs3/Emu/RSX/GSFrameBase.h index fd91244ffe..1f426f9ef1 100644 --- a/rpcs3/Emu/RSX/GSFrameBase.h +++ b/rpcs3/Emu/RSX/GSFrameBase.h @@ -1,7 +1,6 @@ #pragma once #include "util/types.hpp" -#include "util/atomic.hpp" #include #include "display.h" diff --git a/rpcs3/Emu/RSX/GSRender.cpp b/rpcs3/Emu/RSX/GSRender.cpp index 2025842dab..e9e859952e 100644 --- a/rpcs3/Emu/RSX/GSRender.cpp +++ b/rpcs3/Emu/RSX/GSRender.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" +#include "Emu/System.h" #include "GSRender.h" GSRender::GSRender(utils::serial* ar) noexcept : rsx::thread(ar) diff --git a/rpcs3/Emu/RSX/NV47/FW/GRAPH_backend.h b/rpcs3/Emu/RSX/NV47/FW/GRAPH_backend.h index ab8a02984f..21c78cdaa5 100644 --- a/rpcs3/Emu/RSX/NV47/FW/GRAPH_backend.h +++ b/rpcs3/Emu/RSX/NV47/FW/GRAPH_backend.h @@ -2,8 +2,6 @@ #include "../HW/context.h" -#include - namespace rsx { // GRAPH backend class. Wraps RSX acceleration capabilities for the host. diff --git a/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp b/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp index 3c6d6de4e2..ced6c3f386 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "nv406e.h" -#include "common.h" #include "nv47_sync.hpp" #include "Emu/RSX/RSXThread.h" diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h index f4495d8ae4..862baf5062 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h @@ -1,6 +1,5 @@ #pragma once -#include "Emu/RSX/Overlays/overlays.h" #include "Emu/RSX/Overlays/overlay_list_view.hpp" #include "Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.h" #include "Emu/RSX/Overlays/HomeMenu/overlay_home_menu_message_box.h" diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp index fe82f81253..daaf5cc8ee 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "overlay_home_menu_settings.h" -#include "overlay_home_menu_components.h" #include "Emu/system_config.h" namespace rsx diff --git a/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp b/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp index 12ccdc0b27..69c3c41678 100644 --- a/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp @@ -3,7 +3,6 @@ #include "overlay_recvmessage_dialog.h" #include "Emu/System.h" #include "Emu/NP/rpcn_client.h" -#include "Utilities/Thread.h" namespace rsx { diff --git a/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp b/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp index 4c2ca52e42..aed778e6da 100644 --- a/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp @@ -7,7 +7,6 @@ #include "Emu/Cell/PPUThread.h" // for vm_var #include "Emu/Memory/vm_var.h" #include "Emu/Io/interception.h" -#include "Utilities/Thread.h" namespace rsx { @@ -384,7 +383,7 @@ namespace rsx } } - void sendmessage_dialog::callback_handler(u16 ntype, const std::string& username, bool status) + void sendmessage_dialog::callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status) { std::lock_guard lock(m_mutex); diff --git a/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.h b/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.h index 2e4b09422e..508d296fc4 100644 --- a/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.h @@ -40,7 +40,7 @@ namespace rsx compiled_resource get_compiled() override; error_code Exec(message_data& msg_data, std::set& npids) override; - void callback_handler(u16 ntype, const std::string& username, bool status) override; + void callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status) override; }; } } diff --git a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp index 3546592cfc..d3e522df2d 100644 --- a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp +++ b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp @@ -3,6 +3,7 @@ #include "../overlay_manager.h" #include "../overlay_message_dialog.h" #include "../../GSRender.h" +#include "Emu/System.h" #include "Emu/Cell/ErrorCodes.h" namespace rsx diff --git a/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.cpp b/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.cpp index d1af06bab2..3177a76a98 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.cpp @@ -2,7 +2,8 @@ #include "overlay_animated_icon.h" #include "Utilities/File.h" -#include "../Common/time.hpp" + +#include "Emu/Cell/timers.hpp" namespace rsx { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.h b/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.h index 71add8e9ce..b4a864d047 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_animated_icon.h @@ -1,6 +1,6 @@ #pragma once -#include "overlays.h" +#include "Emu/RSX/Overlays/overlay_controls.h" namespace rsx { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_animation.cpp b/rpcs3/Emu/RSX/Overlays/overlay_animation.cpp index 3bab8b6bcc..0d5d8f3baa 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_animation.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_animation.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "overlay_animation.h" #include "overlay_controls.h" -#include "Emu/system_config.h" namespace rsx { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_compile_notification.cpp b/rpcs3/Emu/RSX/Overlays/overlay_compile_notification.cpp index da7f58e6d7..434226bfa5 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_compile_notification.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_compile_notification.cpp @@ -1,5 +1,4 @@ #include "stdafx.h" -#include "overlays.h" #include "overlay_message.h" #include "overlay_loading_icon.hpp" diff --git a/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp b/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp index 480967c0c0..cf6e18adca 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp @@ -5,6 +5,7 @@ #include "util/logs.hpp" #include "Utilities/geometry.h" #include "Utilities/File.h" +#include "Emu/Cell/timers.hpp" #ifndef _WIN32 #include diff --git a/rpcs3/Emu/RSX/Overlays/overlay_controls.h b/rpcs3/Emu/RSX/Overlays/overlay_controls.h index f6d36ceaad..45a3fde222 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_controls.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_controls.h @@ -3,7 +3,6 @@ #include "overlay_fonts.h" #include "Emu/localized_string.h" -#include "Emu/Cell/timers.hpp" #include diff --git a/rpcs3/Emu/RSX/Overlays/overlay_cursor.cpp b/rpcs3/Emu/RSX/Overlays/overlay_cursor.cpp index 589bcabbcd..e20074d1b8 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_cursor.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_cursor.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "overlay_cursor.h" #include "overlay_manager.h" +#include "Emu/Cell/timers.hpp" namespace rsx { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_cursor.h b/rpcs3/Emu/RSX/Overlays/overlay_cursor.h index 079346f649..d464c509fe 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_cursor.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_cursor.h @@ -1,6 +1,7 @@ #pragma once #include "overlays.h" +#include "Utilities/mutex.h" #include namespace rsx diff --git a/rpcs3/Emu/RSX/Overlays/overlay_fonts.cpp b/rpcs3/Emu/RSX/Overlays/overlay_fonts.cpp index 6de7440e5e..8f3c7173b4 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_fonts.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_fonts.cpp @@ -1,5 +1,5 @@ #include "stdafx.h" -#include "overlay_controls.h" +#include "overlay_fonts.h" #include "Emu/System.h" #include "Emu/vfs_config.h" diff --git a/rpcs3/Emu/RSX/Overlays/overlay_manager.h b/rpcs3/Emu/RSX/Overlays/overlay_manager.h index d7a10988c1..7146be3dda 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_manager.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_manager.h @@ -5,10 +5,7 @@ #include "Emu/IdManager.h" #include "Utilities/mutex.h" #include "Utilities/Thread.h" -#include "Utilities/Timer.h" - -#include -#include +#include "Utilities/lockless.h" namespace rsx { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message.cpp b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp index f7a7a8ed7c..b00f0cc5b3 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp @@ -1,6 +1,6 @@ #include "stdafx.h" #include "overlay_message.h" -#include "Emu/RSX/Common/time.hpp" +#include "Emu/Cell/timers.hpp" namespace rsx { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp index c47b5687c0..4bf189e354 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp @@ -2,10 +2,10 @@ #include "overlay_manager.h" #include "overlay_osk.h" #include "Emu/Cell/Modules/cellSysutil.h" -#include "Emu/Cell/Modules/cellMsgDialog.h" -#include "Emu/Cell/Modules/cellKb.h" +#include "Emu/Io/Keyboard.h" #include "Emu/System.h" #include "Emu/system_config.h" +#include "Emu/Cell/timers.hpp" LOG_CHANNEL(osk, "OSK"); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp b/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp index 0bc954b0dc..0c4b9becac 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp @@ -7,7 +7,6 @@ #include #include -#include #include "util/cpu_stats.hpp" diff --git a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp index b65f2bdb9b..a796d62de3 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp @@ -4,8 +4,6 @@ #include "Emu/vfs_config.h" #include "Emu/system_utils.hpp" #include "Emu/System.h" -#include "Utilities/StrUtil.h" -#include "Utilities/Thread.h" namespace rsx { diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index a1fe40d8ce..47e7ee3f87 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -1,12 +1,11 @@ #include "stdafx.h" #include "overlays.h" #include "overlay_manager.h" -#include "overlay_message_dialog.h" #include "Input/pad_thread.h" #include "Emu/Io/interception.h" #include "Emu/Io/KeyboardHandler.h" #include "Emu/RSX/RSXThread.h" -#include "Emu/RSX/Common/time.hpp" +#include "Emu/System.h" LOG_CHANNEL(overlays); diff --git a/rpcs3/Emu/RSX/Overlays/overlays.h b/rpcs3/Emu/RSX/Overlays/overlays.h index d706a6e3f6..254a47cb7b 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.h +++ b/rpcs3/Emu/RSX/Overlays/overlays.h @@ -2,10 +2,8 @@ #include "overlay_animation.h" #include "overlay_controls.h" -#include "Emu/IdManager.h" #include "Emu/Io/pad_types.h" -#include "Utilities/mutex.h" #include "Utilities/Timer.h" #include "../Common/bitfield.hpp" diff --git a/rpcs3/Emu/RSX/Program/CgBinaryProgram.h b/rpcs3/Emu/RSX/Program/CgBinaryProgram.h index 9e567952bc..f63e2adc1b 100644 --- a/rpcs3/Emu/RSX/Program/CgBinaryProgram.h +++ b/rpcs3/Emu/RSX/Program/CgBinaryProgram.h @@ -1,6 +1,6 @@ #pragma once -#include "Emu/Memory/vm.h" +#include "util/endian.hpp" #include "Emu/RSX/GL/GLVertexProgram.h" #include "Emu/RSX/GL/GLFragmentProgram.h" #include "Emu/RSX/Program/ProgramStateCache.h" diff --git a/rpcs3/Emu/RSX/Program/CgBinaryVertexProgram.cpp b/rpcs3/Emu/RSX/Program/CgBinaryVertexProgram.cpp index 00b1ca7204..c37fcd5a59 100644 --- a/rpcs3/Emu/RSX/Program/CgBinaryVertexProgram.cpp +++ b/rpcs3/Emu/RSX/Program/CgBinaryVertexProgram.cpp @@ -1,7 +1,5 @@ #include "stdafx.h" #include "CgBinaryProgram.h" - -#include "Emu/System.h" #include "RSXVertexProgram.h" void CgBinaryDisasm::AddScaCodeDisasm(const std::string& code) diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp index 4a2215d9f9..43e4d866a5 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp @@ -1,6 +1,4 @@ #include "stdafx.h" -#include "Emu/System.h" -#include "../rsx_methods.h" #include "FragmentProgramDecompiler.h" #include diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp index 3f72a3b4a7..0a1005eb46 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp @@ -3,7 +3,6 @@ #include "GLSLCommon.h" #include "RSXFragmentProgram.h" -#include "Emu/system_config.h" #include "Emu/RSX/gcm_enums.h" #include "Utilities/StrFmt.h" diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/GenericVSPassthrough.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/GenericVSPassthrough.glsl index 0428dc16c4..b57ad58cbc 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/GenericVSPassthrough.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/GenericVSPassthrough.glsl @@ -1,5 +1,7 @@ R"( #version 420 +#extension GL_ARB_separate_shader_objects: enable + layout(location=0) out vec2 tc0; #ifdef VULKAN diff --git a/rpcs3/Emu/RSX/Program/MSAA/ColorResolvePass.glsl b/rpcs3/Emu/RSX/Program/MSAA/ColorResolvePass.glsl new file mode 100644 index 0000000000..d2e1c25b60 --- /dev/null +++ b/rpcs3/Emu/RSX/Program/MSAA/ColorResolvePass.glsl @@ -0,0 +1,37 @@ +R"( +#version 430 + +layout(local_size_x=%WORKGROUP_SIZE_X, local_size_y=%WORKGROUP_SIZE_Y, local_size_z=1) in; + +#ifdef VULKAN +layout(set=0, binding=0, %IMAGE_FORMAT) uniform readonly restrict image2DMS multisampled; +layout(set=0, binding=1) uniform writeonly restrict image2D resolve; +#else +layout(binding=0, %IMAGE_FORMAT) uniform readonly restrict image2DMS multisampled; +layout(binding=1) uniform writeonly restrict image2D resolve; +#endif + +#if %BGRA_SWAP +#define shuffle(x) (x.bgra) +#else +#define shuffle(x) (x) +#endif + +void main() +{ + ivec2 resolve_size = imageSize(resolve); + ivec2 aa_size = imageSize(multisampled); + ivec2 sample_count = resolve_size / aa_size; + + if (any(greaterThanEqual(gl_GlobalInvocationID.xy, uvec2(resolve_size)))) return; + + ivec2 resolve_coords = ivec2(gl_GlobalInvocationID.xy); + ivec2 aa_coords = resolve_coords / sample_count; + ivec2 sample_loc = ivec2(resolve_coords % sample_count); + int sample_index = sample_loc.x + (sample_loc.y * sample_count.y); + + vec4 aa_sample = imageLoad(multisampled, aa_coords, sample_index); + imageStore(resolve, resolve_coords, shuffle(aa_sample)); +} + +)" diff --git a/rpcs3/Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl b/rpcs3/Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl new file mode 100644 index 0000000000..56ed52b402 --- /dev/null +++ b/rpcs3/Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl @@ -0,0 +1,37 @@ +R"( +#version 430 + +layout(local_size_x=%WORKGROUP_SIZE_X, local_size_y=%WORKGROUP_SIZE_Y, local_size_z=1) in; + +#ifdef VULKAN +layout(set=0, binding=0) uniform writeonly restrict image2DMS multisampled; +layout(set=0, binding=1, %IMAGE_FORMAT) uniform readonly restrict image2D resolve; +#else +layout(binding=0) uniform writeonly restrict image2DMS multisampled; +layout(binding=1, %IMAGE_FORMAT) uniform readonly restrict image2D resolve; +#endif + +#if %BGRA_SWAP +#define shuffle(x) (x.bgra) +#else +#define shuffle(x) (x) +#endif + +void main() +{ + ivec2 resolve_size = imageSize(resolve); + ivec2 aa_size = imageSize(multisampled); + ivec2 sample_count = resolve_size / aa_size; + + if (any(greaterThanEqual(gl_GlobalInvocationID.xy, uvec2(resolve_size)))) return; + + ivec2 resolve_coords = ivec2(gl_GlobalInvocationID.xy); + ivec2 aa_coords = resolve_coords / sample_count; + ivec2 sample_loc = ivec2(resolve_coords % sample_count); + int sample_index = sample_loc.x + (sample_loc.y * sample_count.y); + + vec4 resolved_sample = imageLoad(resolve, resolve_coords); + imageStore(multisampled, aa_coords, sample_index, shuffle(resolved_sample)); +} + +)" diff --git a/rpcs3/Emu/RSX/Program/MSAA/DepthResolvePass.glsl b/rpcs3/Emu/RSX/Program/MSAA/DepthResolvePass.glsl new file mode 100644 index 0000000000..cc957aa0ac --- /dev/null +++ b/rpcs3/Emu/RSX/Program/MSAA/DepthResolvePass.glsl @@ -0,0 +1,23 @@ +R"( +#version 420 +#extension GL_ARB_separate_shader_objects: enable + +#ifdef VULKAN +layout(set=0, binding=0) uniform sampler2DMS fs0; +layout(push_constant) uniform static_data { ivec2 sample_count; }; +#else +layout(binding=31) uniform sampler2DMS fs0; +uniform ivec2 sample_count; +#endif + +void main() +{ + ivec2 out_coord = ivec2(gl_FragCoord.xy); + ivec2 in_coord = (out_coord / sample_count.xy); + ivec2 sample_loc = out_coord % sample_count.xy; + int sample_index = sample_loc.x + (sample_loc.y * sample_count.y); + float frag_depth = texelFetch(fs0, in_coord, sample_index).x; + gl_FragDepth = frag_depth; +} + +)" diff --git a/rpcs3/Emu/RSX/Program/MSAA/DepthStencilResolvePass.glsl b/rpcs3/Emu/RSX/Program/MSAA/DepthStencilResolvePass.glsl new file mode 100644 index 0000000000..c547cc46b8 --- /dev/null +++ b/rpcs3/Emu/RSX/Program/MSAA/DepthStencilResolvePass.glsl @@ -0,0 +1,28 @@ +R"( +#version 420 +#extension GL_ARB_separate_shader_objects: enable +#extension GL_ARB_shader_stencil_export : enable + +#ifdef VULKAN +layout(set=0, binding=0) uniform sampler2DMS fs0; +layout(set=0, binding=1) uniform usampler2DMS fs1; +layout(push_constant) uniform static_data { ivec2 sample_count; }; +#else +layout(binding=31) uniform sampler2DMS fs0; +layout(binding=30) uniform usampler2DMS fs1; +uniform ivec2 sample_count; +#endif + +void main() +{ + ivec2 out_coord = ivec2(gl_FragCoord.xy); + ivec2 in_coord = (out_coord / sample_count.xy); + ivec2 sample_loc = out_coord % ivec2(sample_count.xy); + int sample_index = sample_loc.x + (sample_loc.y * sample_count.y); + float frag_depth = texelFetch(fs0, in_coord, sample_index).x; + uint frag_stencil = texelFetch(fs1, in_coord, sample_index).x; + gl_FragDepth = frag_depth; + gl_FragStencilRefARB = int(frag_stencil); +} + +)" diff --git a/rpcs3/Emu/RSX/Program/MSAA/DepthStencilUnresolvePass.glsl b/rpcs3/Emu/RSX/Program/MSAA/DepthStencilUnresolvePass.glsl new file mode 100644 index 0000000000..de10cc351b --- /dev/null +++ b/rpcs3/Emu/RSX/Program/MSAA/DepthStencilUnresolvePass.glsl @@ -0,0 +1,28 @@ +R"( +#version 420 +#extension GL_ARB_separate_shader_objects: enable +#extension GL_ARB_shader_stencil_export : enable + +#ifdef VULKAN +layout(set=0, binding=0) uniform sampler2D fs0; +layout(set=0, binding=1) uniform usampler2D fs1; +layout(push_constant) uniform static_data { ivec2 sample_count; }; +#else +layout(binding=31) uniform sampler2D fs0; +layout(binding=30) uniform usampler2D fs1; +uniform ivec2 sample_count; +#endif + +void main() +{ + ivec2 pixel_coord = ivec2(gl_FragCoord.xy); + pixel_coord *= sample_count.xy; + pixel_coord.x += (gl_SampleID % sample_count.x); + pixel_coord.y += (gl_SampleID / sample_count.x); + float frag_depth = texelFetch(fs0, pixel_coord, 0).x; + uint frag_stencil = texelFetch(fs1, pixel_coord, 0).x; + gl_FragDepth = frag_depth; + gl_FragStencilRefARB = int(frag_stencil); +} + +)" diff --git a/rpcs3/Emu/RSX/Program/MSAA/DepthUnresolvePass.glsl b/rpcs3/Emu/RSX/Program/MSAA/DepthUnresolvePass.glsl new file mode 100644 index 0000000000..c4e5ee2f63 --- /dev/null +++ b/rpcs3/Emu/RSX/Program/MSAA/DepthUnresolvePass.glsl @@ -0,0 +1,23 @@ +R"( +#version 420 +#extension GL_ARB_separate_shader_objects: enable + +#ifdef VULKAN +layout(set=0, binding=0) uniform sampler2D fs0; +layout(push_constant) uniform static_data { ivec2 sample_count; }; +#else +layout(binding=31) uniform sampler2D fs0; +uniform ivec2 sample_count; +#endif + +void main() +{ + ivec2 pixel_coord = ivec2(gl_FragCoord.xy); + pixel_coord *= sample_count.xy; + pixel_coord.x += (gl_SampleID % sample_count.x); + pixel_coord.y += (gl_SampleID / sample_count.x); + float frag_depth = texelFetch(fs0, pixel_coord, 0).x; + gl_FragDepth = frag_depth; +} + +)" diff --git a/rpcs3/Emu/RSX/Program/MSAA/StencilResolvePass.glsl b/rpcs3/Emu/RSX/Program/MSAA/StencilResolvePass.glsl new file mode 100644 index 0000000000..5d954d7a5e --- /dev/null +++ b/rpcs3/Emu/RSX/Program/MSAA/StencilResolvePass.glsl @@ -0,0 +1,28 @@ +R"( +#version 420 +#extension GL_ARB_separate_shader_objects: enable + +#ifdef VULKAN +layout(set=0, binding=0) uniform usampler2DMS fs0; +layout(push_constant) uniform static_data +{ + layout(offset=0) ivec2 sample_count; + layout(offset=8) int stencil_mask; +}; +#else +layout(binding=31) uniform usampler2DMS fs0; +uniform ivec2 sample_count; +uniform int stencil_mask; +#endif + +void main() +{ + ivec2 out_coord = ivec2(gl_FragCoord.xy); + ivec2 in_coord = (out_coord / sample_count.xy); + ivec2 sample_loc = out_coord % sample_count.xy; + int sample_index = sample_loc.x + (sample_loc.y * sample_count.y); + uint frag_stencil = texelFetch(fs0, in_coord, sample_index).x; + if ((frag_stencil & uint(stencil_mask)) == 0) discard; +} + +)" diff --git a/rpcs3/Emu/RSX/Program/MSAA/StencilUnresolvePass.glsl b/rpcs3/Emu/RSX/Program/MSAA/StencilUnresolvePass.glsl new file mode 100644 index 0000000000..c3a0f65be1 --- /dev/null +++ b/rpcs3/Emu/RSX/Program/MSAA/StencilUnresolvePass.glsl @@ -0,0 +1,28 @@ +R"( +#version 420 +#extension GL_ARB_separate_shader_objects: enable + +#ifdef VULKAN +layout(set=0, binding=0) uniform usampler2D fs0; +layout(push_constant) uniform static_data +{ + layout(offset=0) ivec2 sample_count; + layout(offset=8) int stencil_mask; +}; +#else +layout(binding=31) uniform usampler2D fs0; +uniform ivec2 sample_count; +uniform int stencil_mask; +#endif + +void main() +{ + ivec2 pixel_coord = ivec2(gl_FragCoord.xy); + pixel_coord *= sample_count.xy; + pixel_coord.x += (gl_SampleID % sample_count.x); + pixel_coord.y += (gl_SampleID / sample_count.x); + uint frag_stencil = texelFetch(fs0, pixel_coord, 0).x; + if ((frag_stencil & uint(stencil_mask)) == 0) discard; +} + +)" diff --git a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp index 6a1bcffc7a..b6c2fba452 100644 --- a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp +++ b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp @@ -4,8 +4,6 @@ #include #include "util/v128.hpp" -#include "util/asm.hpp" - #if defined(ARCH_X64) #include "emmintrin.h" diff --git a/rpcs3/Emu/RSX/Program/SPIRVCommon.cpp b/rpcs3/Emu/RSX/Program/SPIRVCommon.cpp index e1aac0bd33..f40ff06161 100644 --- a/rpcs3/Emu/RSX/Program/SPIRVCommon.cpp +++ b/rpcs3/Emu/RSX/Program/SPIRVCommon.cpp @@ -20,7 +20,7 @@ #endif #include "SPIRVCommon.h" -#include "GLSLCommon.h" +#include "Emu/RSX/Program/GLSLTypes.h" namespace spirv { diff --git a/rpcs3/Emu/RSX/Program/VertexProgramDecompiler.cpp b/rpcs3/Emu/RSX/Program/VertexProgramDecompiler.cpp index 1511902300..dc9a034995 100644 --- a/rpcs3/Emu/RSX/Program/VertexProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/Program/VertexProgramDecompiler.cpp @@ -1,6 +1,4 @@ #include "stdafx.h" -#include "Emu/System.h" - #include "VertexProgramDecompiler.h" #include diff --git a/rpcs3/Emu/RSX/RSXDisAsm.h b/rpcs3/Emu/RSX/RSXDisAsm.h index 08143cbfbf..445e5290bf 100644 --- a/rpcs3/Emu/RSX/RSXDisAsm.h +++ b/rpcs3/Emu/RSX/RSXDisAsm.h @@ -1,6 +1,6 @@ #pragma once -#include "Emu/Cell/PPCDisAsm.h" +#include "Emu/CPU/CPUDisAsm.h" class RSXDisAsm final : public CPUDisAsm { diff --git a/rpcs3/Emu/RSX/RSXFIFO.cpp b/rpcs3/Emu/RSX/RSXFIFO.cpp index 839144a754..3f6889bdaa 100644 --- a/rpcs3/Emu/RSX/RSXFIFO.cpp +++ b/rpcs3/Emu/RSX/RSXFIFO.cpp @@ -1,9 +1,9 @@ #include "stdafx.h" +#include "Emu/System.h" #include "RSXFIFO.h" #include "RSXThread.h" #include "Capture/rsx_capture.h" -#include "Common/time.hpp" #include "Core/RSXReservationLock.hpp" #include "Emu/Memory/vm_reservation.h" #include "Emu/Cell/lv2/sys_rsx.h" @@ -11,6 +11,7 @@ #include "util/asm.hpp" +#include #include using spu_rdata_t = std::byte[128]; diff --git a/rpcs3/Emu/RSX/RSXOffload.cpp b/rpcs3/Emu/RSX/RSXOffload.cpp index 97a27ae0ee..5f8737543e 100644 --- a/rpcs3/Emu/RSX/RSXOffload.cpp +++ b/rpcs3/Emu/RSX/RSXOffload.cpp @@ -6,6 +6,8 @@ #include "RSXOffload.h" #include "RSXThread.h" +#include "Utilities/lockless.h" + #include #include "util/asm.hpp" diff --git a/rpcs3/Emu/RSX/RSXTexture.cpp b/rpcs3/Emu/RSX/RSXTexture.cpp index 5951f368e9..9b8f4bfb2e 100644 --- a/rpcs3/Emu/RSX/RSXTexture.cpp +++ b/rpcs3/Emu/RSX/RSXTexture.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "RSXTexture.h" -#include "rsx_methods.h" #include "rsx_utils.h" #include "Emu/system_config.h" diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 57ffc0e11f..420aa6eb8e 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -2,11 +2,7 @@ #include "RSXThread.h" #include "Capture/rsx_capture.h" -#include "Common/BufferUtils.h" -#include "Common/buffer_stream.hpp" -#include "Common/texture_cache.h" #include "Common/surface_store.h" -#include "Common/time.hpp" #include "Core/RSXReservationLock.hpp" #include "Core/RSXEngLock.hpp" #include "Host/MM.h" @@ -18,8 +14,8 @@ #include "gcm_printing.h" #include "RSXDisAsm.h" -#include "Emu/Cell/PPUCallback.h" -#include "Emu/Cell/SPUThread.h" +#include "Emu/System.h" +#include "Emu/Cell/PPUThread.h" #include "Emu/Cell/timers.hpp" #include "Emu/Cell/lv2/sys_event.h" #include "Emu/Cell/lv2/sys_time.h" @@ -27,19 +23,15 @@ #include "util/serialization_ext.hpp" #include "Overlays/overlay_perf_metrics.h" #include "Overlays/overlay_debug_overlay.h" -#include "Overlays/overlay_message.h" +#include "Overlays/overlay_manager.h" #include "Utilities/date_time.h" -#include "Utilities/StrUtil.h" -#include "Crypto/unzip.h" #include "util/asm.hpp" #include -#include #include #include -#include class GSRender; @@ -961,7 +953,7 @@ namespace rsx } // Wait for startup (TODO) - while (!rsx_thread_running || Emu.IsPaused()) + while (!rsx_thread_running || Emu.IsPausedOrReady()) { // Execute backend-local tasks first do_local_task(performance_counters.state); @@ -1637,6 +1629,13 @@ namespace rsx layout.aa_factors[0] = aa_factor_u; layout.aa_factors[1] = aa_factor_v; + // Log this to frame stats + if (layout.target != rsx::surface_target::none) + { + m_frame_stats.framebuffer_stats.add(layout.width, layout.height, aa_mode); + } + + // Check if anything has changed bool really_changed = false; for (u8 i = 0; i < rsx::limits::color_buffers_count; ++i) diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 257c70e4a5..8b5f2af10d 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -1,14 +1,8 @@ #pragma once -#include #include #include -#include -#include -#include -#include "GCM.h" -#include "rsx_cache.h" #include "RSXFIFO.h" #include "RSXOffload.h" #include "RSXZCULL.h" @@ -32,7 +26,6 @@ #include "Core/RSXDriverState.h" #include "Core/RSXFrameBuffer.h" #include "Core/RSXContext.h" -#include "Core/RSXIOMap.hpp" #include "Core/RSXVertexTypes.h" #include "NV47/FW/GRAPH_backend.h" @@ -87,6 +80,7 @@ namespace rsx { bool supports_multidraw; // Draw call batching bool supports_hw_a2c; // Alpha to coverage + bool supports_hw_a2c_1spp; // Alpha to coverage at 1 sample per pixel bool supports_hw_renormalization; // Should be true on NV hardware which matches PS3 texture renormalization behaviour bool supports_hw_msaa; // MSAA support bool supports_hw_a2one; // Alpha to one @@ -466,4 +460,9 @@ namespace rsx { return g_fxo->try_get(); } + + inline const backend_configuration& get_renderer_backend_config() + { + return g_fxo->get().get_backend_config(); + } } diff --git a/rpcs3/Emu/RSX/RSXZCULL.h b/rpcs3/Emu/RSX/RSXZCULL.h index cb072561fa..4627fa2ca5 100644 --- a/rpcs3/Emu/RSX/RSXZCULL.h +++ b/rpcs3/Emu/RSX/RSXZCULL.h @@ -4,6 +4,8 @@ #include #include +#include "Utilities/mutex.h" + #include "rsx_utils.h" #include diff --git a/rpcs3/Emu/RSX/VK/VKAsyncScheduler.cpp b/rpcs3/Emu/RSX/VK/VKAsyncScheduler.cpp index 7211d50e1e..fd4e667ef9 100644 --- a/rpcs3/Emu/RSX/VK/VKAsyncScheduler.cpp +++ b/rpcs3/Emu/RSX/VK/VKAsyncScheduler.cpp @@ -1,10 +1,5 @@ #include "VKAsyncScheduler.h" #include "VKHelpers.h" -#include "VKResourceManager.h" - -#include "Emu/IdManager.h" -#include "Utilities/lockless.h" -#include "Utilities/mutex.h" #include diff --git a/rpcs3/Emu/RSX/VK/VKAsyncScheduler.h b/rpcs3/Emu/RSX/VK/VKAsyncScheduler.h index 4613a122d2..0b00830804 100644 --- a/rpcs3/Emu/RSX/VK/VKAsyncScheduler.h +++ b/rpcs3/Emu/RSX/VK/VKAsyncScheduler.h @@ -2,8 +2,7 @@ #include "vkutils/commands.h" #include "vkutils/sync.h" - -#include "Utilities/Thread.h" +#include "Utilities/mutex.h" #define VK_MAX_ASYNC_COMPUTE_QUEUES 256 diff --git a/rpcs3/Emu/RSX/VK/VKCompute.cpp b/rpcs3/Emu/RSX/VK/VKCompute.cpp index e7cfb617ba..7cda0a8ff9 100644 --- a/rpcs3/Emu/RSX/VK/VKCompute.cpp +++ b/rpcs3/Emu/RSX/VK/VKCompute.cpp @@ -2,6 +2,7 @@ #include "VKHelpers.h" #include "VKRenderPass.h" #include "vkutils/buffer_object.h" +#include "VKPipelineCompiler.h" #define VK_MAX_COMPUTE_TASKS 8192 // Max number of jobs per frame diff --git a/rpcs3/Emu/RSX/VK/VKCompute.h b/rpcs3/Emu/RSX/VK/VKCompute.h index faadecfc18..4f9a3f2a3a 100644 --- a/rpcs3/Emu/RSX/VK/VKCompute.h +++ b/rpcs3/Emu/RSX/VK/VKCompute.h @@ -1,5 +1,5 @@ #pragma once -#include "VKPipelineCompiler.h" +#include "Emu/RSX/VK/VKProgramPipeline.h" #include "vkutils/descriptors.h" #include "vkutils/buffer_object.h" diff --git a/rpcs3/Emu/RSX/VK/VKDMA.h b/rpcs3/Emu/RSX/VK/VKDMA.h index 44ad623825..e718733649 100644 --- a/rpcs3/Emu/RSX/VK/VKDMA.h +++ b/rpcs3/Emu/RSX/VK/VKDMA.h @@ -1,6 +1,5 @@ #pragma once #include "vkutils/buffer_object.h" -#include "vkutils/commands.h" namespace vk { diff --git a/rpcs3/Emu/RSX/VK/VKFormats.h b/rpcs3/Emu/RSX/VK/VKFormats.h index 65aaab44ce..85b52ca56b 100644 --- a/rpcs3/Emu/RSX/VK/VKFormats.h +++ b/rpcs3/Emu/RSX/VK/VKFormats.h @@ -1,7 +1,6 @@ #pragma once #include "VulkanAPI.h" #include "../gcm_enums.h" -#include namespace vk { diff --git a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp index eb57d11f7a..dd654a6736 100644 --- a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp @@ -5,7 +5,6 @@ #include "vkutils/device.h" #include "Emu/system_config.h" #include "../Program/GLSLCommon.h" -#include "../GCM.h" std::string VKFragmentDecompilerThread::getFloatTypeName(usz elementCount) { diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 4490ff54bf..d37f6d62d0 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -4,7 +4,6 @@ #include "VKAsyncScheduler.h" #include "VKCommandStream.h" -#include "VKCommonDecompiler.h" #include "VKCommonPipelineLayout.h" #include "VKCompute.h" #include "VKGSRender.h" @@ -630,6 +629,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) { backend_config.supports_hw_msaa = true; backend_config.supports_hw_a2c = true; + backend_config.supports_hw_a2c_1spp = true; backend_config.supports_hw_a2one = m_device->get_alpha_to_one_support(); } @@ -1982,14 +1982,32 @@ bool VKGSRender::load_program() m_program = nullptr; } - if (!m_program && (shadermode == shader_mode::async_with_interpreter || shadermode == shader_mode::interpreter_only)) + if (shadermode == shader_mode::async_with_interpreter || shadermode == shader_mode::interpreter_only) { - if (!m_shader_interpreter.is_interpreter(m_prev_program)) + const bool is_interpreter = !m_program; + const bool was_interpreter = m_shader_interpreter.is_interpreter(m_prev_program); + + // First load the next program if not available + if (!m_program) { + m_program = m_shader_interpreter.get(m_pipeline_properties, current_fp_metadata); + + // Program has changed, reupload m_interpreter_state = rsx::invalidate_pipeline_bits; } - m_program = m_shader_interpreter.get(m_pipeline_properties, current_fp_metadata); + // If swapping between interpreter and recompiler, we need to adjust some flags to reupload data as needed. + if (is_interpreter != was_interpreter) + { + // Always reupload transform constants when going between interpreter and recompiler + m_graphics_state |= rsx::transform_constants_dirty; + + // Always reload fragment constansts when moving from interpreter back to recompiler. + if (was_interpreter) + { + m_graphics_state |= rsx::fragment_constants_dirty; + } + } } return m_program != nullptr; @@ -2051,7 +2069,8 @@ void VKGSRender::load_program_env() return std::make_pair(m_instancing_buffer_ring_info.map(constants_data_table_offset, size), size); }); - m_draw_processor.fill_constants_instancing_buffer(indirection_table_buf, constants_array_buf, m_vertex_prog); + const auto bound_vertex_prog = m_shader_interpreter.is_interpreter(m_program) ? nullptr : m_vertex_prog; + m_draw_processor.fill_constants_instancing_buffer(indirection_table_buf, constants_array_buf, bound_vertex_prog); m_instancing_buffer_ring_info.unmap(); m_instancing_indirection_buffer_info = { m_instancing_buffer_ring_info.heap->value, indirection_table_offset, indirection_table_buf.size() }; @@ -2224,6 +2243,11 @@ void VKGSRender::load_program_env() handled_flags |= rsx::pipeline_state::fragment_constants_dirty; } + if (m_shader_interpreter.is_interpreter(m_program)) + { + ensure(m_vertex_constants_buffer_info.range >= 468 * 16); + } + m_graphics_state.clear(handled_flags); } @@ -2234,7 +2258,9 @@ bool VKGSRender::is_current_program_interpreted() const void VKGSRender::upload_transform_constants(const rsx::io_buffer& buffer) { - const usz transform_constants_size = (!m_vertex_prog || m_vertex_prog->has_indexed_constants) ? 8192 : m_vertex_prog->constant_ids.size() * 16; + const bool is_interpreter = m_shader_interpreter.is_interpreter(m_program); + const usz transform_constants_size = (is_interpreter || m_vertex_prog->has_indexed_constants) ? 8192 : m_vertex_prog->constant_ids.size() * 16; + if (transform_constants_size) { check_heap_status(VK_HEAP_CHECK_TRANSFORM_CONSTANTS_STORAGE); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index c245a2677f..028aa6b178 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -17,15 +17,10 @@ #include "VKFramebuffer.h" #include "VKShaderInterpreter.h" #include "VKQueryPool.h" -#include "util/asm.hpp" -#include "Emu/RSX/GCM.h" #include "Emu/RSX/GSRender.h" #include "Emu/RSX/Host/RSXDMAWriter.h" -#include -#include - using namespace vk::vmm_allocation_pool_; // clang workaround. using namespace vk::upscaling_flags_; // ditto diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index 2a31ffa51d..a0d0b643d1 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -7,12 +7,10 @@ #include "VKResolveHelper.h" #include "VKResourceManager.h" #include "VKDMA.h" -#include "VKCommandStream.h" #include "VKRenderPass.h" #include "vkutils/scratch.h" #include "vkutils/device.h" -#include "Emu/RSX/rsx_methods.h" #include namespace vk @@ -31,7 +29,7 @@ namespace vk bool g_drv_sanitize_fp_values = false; bool g_drv_disable_fence_reset = false; bool g_drv_emulate_cond_render = false; - bool g_drv_strict_query_scopes = false; + bool g_drv_strict_query_scopes = true; bool g_drv_force_reuse_query_pools = false; u64 g_num_processed_frames = 0; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index 281aab5f1b..302abfb805 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -1,17 +1,9 @@ #pragma once #include "util/types.hpp" -#include -#include #include -#include -#include -#include -#include -#include #include "VulkanAPI.h" -#include "vkutils/chip_class.h" #include "Utilities/geometry.h" #include "Emu/RSX/Common/TextureUtils.h" #include "Emu/RSX/rsx_utils.h" diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.cpp b/rpcs3/Emu/RSX/VK/VKOverlays.cpp index fd7dc1b0cc..8f862cb6e3 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.cpp +++ b/rpcs3/Emu/RSX/VK/VKOverlays.cpp @@ -15,6 +15,8 @@ #include "util/fnv_hash.hpp" +#include "Emu/Cell/timers.hpp" + namespace vk { overlay_pass::overlay_pass() @@ -47,9 +49,11 @@ namespace vk void overlay_pass::init_descriptors() { - rsx::simple_array descriptor_pool_sizes = + rsx::simple_array descriptor_pool_sizes = {}; + + if (m_num_uniform_buffers) { - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 } + descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, m_num_uniform_buffers }); }; if (m_num_usable_samplers) @@ -65,35 +69,38 @@ namespace vk // Reserve descriptor pools m_descriptor_pool.create(*m_device, descriptor_pool_sizes); - const auto num_bindings = 1 + m_num_usable_samplers + m_num_input_attachments; + const auto num_bindings = m_num_uniform_buffers + m_num_usable_samplers + m_num_input_attachments; rsx::simple_array bindings(num_bindings); + u32 binding_slot = 0; - bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - bindings[0].descriptorCount = 1; - bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; - bindings[0].binding = 0; - bindings[0].pImmutableSamplers = nullptr; - - u32 descriptor_index = 1; - for (u32 n = 0; n < m_num_usable_samplers; ++n, ++descriptor_index) + for (u32 n = 0; n < m_num_uniform_buffers; ++n, ++binding_slot) { - bindings[descriptor_index].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - bindings[descriptor_index].descriptorCount = 1; - bindings[descriptor_index].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - bindings[descriptor_index].binding = descriptor_index; - bindings[descriptor_index].pImmutableSamplers = nullptr; + bindings[binding_slot].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + bindings[binding_slot].descriptorCount = 1; + bindings[binding_slot].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + bindings[binding_slot].binding = binding_slot; + bindings[binding_slot].pImmutableSamplers = nullptr; } - for (u32 n = 0; n < m_num_input_attachments; ++n, ++descriptor_index) + for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding_slot) { - bindings[descriptor_index].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - bindings[descriptor_index].descriptorCount = 1; - bindings[descriptor_index].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - bindings[descriptor_index].binding = descriptor_index; - bindings[descriptor_index].pImmutableSamplers = nullptr; + bindings[binding_slot].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + bindings[binding_slot].descriptorCount = 1; + bindings[binding_slot].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + bindings[binding_slot].binding = binding_slot; + bindings[binding_slot].pImmutableSamplers = nullptr; } - ensure(descriptor_index == num_bindings); + for (u32 n = 0; n < m_num_input_attachments; ++n, ++binding_slot) + { + bindings[binding_slot].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + bindings[binding_slot].descriptorCount = 1; + bindings[binding_slot].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + bindings[binding_slot].binding = binding_slot; + bindings[binding_slot].pImmutableSamplers = nullptr; + } + + ensure(binding_slot == num_bindings); m_descriptor_layout = vk::descriptors::create_layout(bindings); VkPipelineLayoutCreateInfo layout_info = {}; @@ -120,9 +127,14 @@ namespace vk std::vector overlay_pass::get_fragment_inputs() { std::vector fs_inputs; - fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_uniform_buffer,{},{}, 0, "static_data" }); + u32 binding = 0; + + for (u32 n = 0; n < m_num_uniform_buffers; ++n, ++binding) + { + const std::string name = std::string("static_data") + (n > 0 ? std::to_string(n) : ""); + fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_uniform_buffer,{},{}, 0, name }); + } - u32 binding = 1; for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding) { fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_texture,{},{}, binding, "fs" + std::to_string(n) }); @@ -231,7 +243,10 @@ namespace vk update_uniforms(cmd, program); - program->bind_uniform({ m_ubo.heap->value, m_ubo_offset, std::max(m_ubo_length, 4u) }, 0, m_descriptor_set); + if (m_num_uniform_buffers > 0) + { + program->bind_uniform({ m_ubo.heap->value, m_ubo_offset, std::max(m_ubo_length, 4u) }, 0, m_descriptor_set); + } for (uint n = 0; n < src.size(); ++n) { diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.h b/rpcs3/Emu/RSX/VK/VKOverlays.h index d1288fb80a..2b11285653 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.h +++ b/rpcs3/Emu/RSX/VK/VKOverlays.h @@ -52,6 +52,7 @@ namespace vk VkFilter m_sampler_filter = VK_FILTER_LINEAR; u32 m_num_usable_samplers = 1; u32 m_num_input_attachments = 0; + u32 m_num_uniform_buffers = 1; std::unordered_map> m_program_cache; std::unique_ptr m_sampler; diff --git a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp index 930210f19d..52742e1241 100644 --- a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp +++ b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp @@ -4,8 +4,6 @@ #include "vkutils/device.h" #include "Utilities/Thread.h" -#include - #include "util/sysinfo.hpp" namespace vk diff --git a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h index 5ed3c7b07f..836bc5f14f 100644 --- a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h +++ b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.h @@ -1,5 +1,4 @@ #pragma once -#include "../rsx_utils.h" #include "Utilities/lockless.h" #include "VKProgramPipeline.h" #include "vkutils/graphics_pipeline_state.hpp" diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index 0c32d9034d..e8f182c897 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -2,7 +2,6 @@ #include "VKGSRender.h" #include "vkutils/buffer_object.h" #include "Emu/RSX/Overlays/overlay_manager.h" -#include "Emu/RSX/Overlays/overlays.h" #include "Emu/RSX/Overlays/overlay_debug_overlay.h" #include "Emu/Cell/Modules/cellVideoOut.h" @@ -831,6 +830,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) : 0; rsx::overlays::set_debug_overlay_text(fmt::format( + "Internal Resolution: %s\n" "RSX Load: %3d%%\n" "draw calls: %17d\n" "submits: %20d\n" @@ -845,6 +845,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) "Flush requests: %13d = %2d (%3d%%) hard faults, %2d unavoidable, %2d misprediction(s), %2d speculation(s)\n" "Texture uploads: %12u (%u from CPU - %02u%%, %u copies avoided)\n" "Vertex cache hits: %10u/%u (%u%%)", + info.stats.framebuffer_stats.to_string(!backend_config.supports_hw_msaa), get_load(), info.stats.draw_calls, info.stats.submit_count, info.stats.setup_time, info.stats.vertex_upload_time, info.stats.textures_upload_time, info.stats.draw_exec_time, info.stats.flip_time, num_dirty_textures, texture_memory_size, tmp_texture_memory_size, diff --git a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h index 975364c726..63db151ab3 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h +++ b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h @@ -1,12 +1,9 @@ #pragma once #include "VKVertexProgram.h" #include "VKFragmentProgram.h" -#include "VKRenderPass.h" #include "VKPipelineCompiler.h" #include "../Program/ProgramStateCache.h" -#include "util/fnv_hash.hpp" - namespace vk { struct VKTraits diff --git a/rpcs3/Emu/RSX/VK/VKProgramPipeline.h b/rpcs3/Emu/RSX/VK/VKProgramPipeline.h index 4b7cdd9092..06dbaf877f 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramPipeline.h +++ b/rpcs3/Emu/RSX/VK/VKProgramPipeline.h @@ -1,7 +1,7 @@ #pragma once #include "VulkanAPI.h" -#include "VKCommonDecompiler.h" +#include "Emu/RSX/Program/GLSLTypes.h" #include "vkutils/descriptors.h" diff --git a/rpcs3/Emu/RSX/VK/VKQueryPool.cpp b/rpcs3/Emu/RSX/VK/VKQueryPool.cpp index 3fd80a8d67..28cb2c2272 100644 --- a/rpcs3/Emu/RSX/VK/VKQueryPool.cpp +++ b/rpcs3/Emu/RSX/VK/VKQueryPool.cpp @@ -1,9 +1,11 @@ #include "stdafx.h" +#include "vkutils/query_pool.hpp" #include "VKHelpers.h" #include "VKQueryPool.h" #include "VKRenderPass.h" #include "VKResourceManager.h" #include "util/asm.hpp" +#include "VKGSRender.h" namespace vk { diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp b/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp index 899c6593e0..11e7747f2f 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp @@ -1,5 +1,4 @@ -#include "VKCompute.h" -#include "VKDMA.h" +#include "vkutils/data_heap.h" #include "VKRenderTargets.h" #include "VKResourceManager.h" #include "Emu/RSX/rsx_methods.h" @@ -998,7 +997,7 @@ namespace vk return; } - // Memory transfers + // Memory transfers vk::image* target_image = (samples() > 1) ? get_resolve_target_safe(cmd) : this; vk::blitter hw_blitter; const auto dst_bpp = get_bpp(); diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index 19c9f04f63..e7df9325c1 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -7,13 +7,10 @@ #include "VKHelpers.h" #include "vkutils/barriers.h" #include "vkutils/buffer_object.h" -#include "vkutils/data_heap.h" #include "vkutils/device.h" #include "vkutils/image.h" #include "vkutils/scratch.h" -#include - namespace vk { namespace surface_cache_utils diff --git a/rpcs3/Emu/RSX/VK/VKResolveHelper.cpp b/rpcs3/Emu/RSX/VK/VKResolveHelper.cpp index ca30abfc06..75a013ddfd 100644 --- a/rpcs3/Emu/RSX/VK/VKResolveHelper.cpp +++ b/rpcs3/Emu/RSX/VK/VKResolveHelper.cpp @@ -253,4 +253,90 @@ namespace vk if (g_depthstencil_resolver) g_depthstencil_resolver->free_resources(); if (g_depthstencil_unresolver) g_depthstencil_unresolver->free_resources(); } + + + void cs_resolve_base::build(const std::string& format_prefix, bool unresolve, bool bgra_swap) + { + create(); + + switch (optimal_group_size) + { + default: + case 64: + cs_wave_x = 8; + cs_wave_y = 8; + break; + case 32: + cs_wave_x = 8; + cs_wave_y = 4; + break; + } + + static const char* resolve_kernel = + #include "Emu/RSX/Program/MSAA/ColorResolvePass.glsl" + ; + + static const char* unresolve_kernel = + #include "Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl" + ; + + const std::pair syntax_replace[] = + { + { "%WORKGROUP_SIZE_X", std::to_string(cs_wave_x) }, + { "%WORKGROUP_SIZE_Y", std::to_string(cs_wave_y) }, + { "%IMAGE_FORMAT", format_prefix }, + { "%BGRA_SWAP", bgra_swap ? "1" : "0" } + }; + + m_src = unresolve ? unresolve_kernel : resolve_kernel; + m_src = fmt::replace_all(m_src, syntax_replace); + + rsx_log.notice("Compute shader:\n%s", m_src); + } + + void depth_resolve_base::build(bool resolve_depth, bool resolve_stencil, bool is_unresolve) + { + vs_src = + #include "Emu/RSX/Program/GLSLSnippets/GenericVSPassthrough.glsl" + ; + + static const char* depth_resolver = + #include "Emu/RSX/Program/MSAA/DepthResolvePass.glsl" + ; + + static const char* depth_unresolver = + #include "Emu/RSX/Program/MSAA/DepthUnresolvePass.glsl" + ; + + static const char* stencil_resolver = + #include "Emu/RSX/Program/MSAA/StencilResolvePass.glsl" + ; + + static const char* stencil_unresolver = + #include "Emu/RSX/Program/MSAA/StencilUnresolvePass.glsl" + ; + + static const char* depth_stencil_resolver = + #include "Emu/RSX/Program/MSAA/DepthStencilResolvePass.glsl" + ; + + static const char* depth_stencil_unresolver = + #include "Emu/RSX/Program/MSAA/DepthStencilUnresolvePass.glsl" + ; + + if (resolve_depth && resolve_stencil) + { + fs_src = is_unresolve ? depth_stencil_unresolver : depth_stencil_resolver; + } + else if (resolve_depth) + { + fs_src = is_unresolve ? depth_unresolver : depth_resolver; + } + else if (resolve_stencil) + { + fs_src = is_unresolve ? stencil_unresolver : stencil_resolver; + } + + rsx_log.notice("Resolve shader:\n%s", fs_src); + } } \ No newline at end of file diff --git a/rpcs3/Emu/RSX/VK/VKResolveHelper.h b/rpcs3/Emu/RSX/VK/VKResolveHelper.h index 6b83a5af9c..57cee06b86 100644 --- a/rpcs3/Emu/RSX/VK/VKResolveHelper.h +++ b/rpcs3/Emu/RSX/VK/VKResolveHelper.h @@ -21,70 +21,7 @@ namespace vk virtual ~cs_resolve_base() {} - // FIXME: move body to cpp - void build(const std::string& kernel, const std::string& format_prefix, int direction) - { - create(); - - // TODO: Tweak occupancy - switch (optimal_group_size) - { - default: - case 64: - cs_wave_x = 8; - cs_wave_y = 8; - break; - case 32: - cs_wave_x = 8; - cs_wave_y = 4; - break; - } - - const std::pair syntax_replace[] = - { - { "%wx", std::to_string(cs_wave_x) }, - { "%wy", std::to_string(cs_wave_y) }, - }; - - m_src = - "#version 430\n" - "layout(local_size_x=%wx, local_size_y=%wy, local_size_z=1) in;\n" - "\n"; - - m_src = fmt::replace_all(m_src, syntax_replace); - - if (direction == 0) - { - m_src += - "layout(set=0, binding=0, " + format_prefix + ") uniform readonly restrict image2DMS multisampled;\n" - "layout(set=0, binding=1) uniform writeonly restrict image2D resolve;\n"; - } - else - { - m_src += - "layout(set=0, binding=0) uniform writeonly restrict image2DMS multisampled;\n" - "layout(set=0, binding=1, " + format_prefix + ") uniform readonly restrict image2D resolve;\n"; - } - - m_src += - "\n" - "void main()\n" - "{\n" - " ivec2 resolve_size = imageSize(resolve);\n" - " ivec2 aa_size = imageSize(multisampled);\n" - " ivec2 sample_count = resolve_size / aa_size;\n" - "\n" - " if (any(greaterThanEqual(gl_GlobalInvocationID.xy, uvec2(resolve_size)))) return;" - "\n" - " ivec2 resolve_coords = ivec2(gl_GlobalInvocationID.xy);\n" - " ivec2 aa_coords = resolve_coords / sample_count;\n" - " ivec2 sample_loc = ivec2(resolve_coords % sample_count);\n" - " int sample_index = sample_loc.x + (sample_loc.y * sample_count.y);\n" - + kernel + - "}\n"; - - rsx_log.notice("Compute shader:\n%s", m_src); - } + void build(const std::string& format_prefix, bool unresolve, bool bgra_swap); std::vector> get_descriptor_layout() override { @@ -144,14 +81,8 @@ namespace vk { cs_resolve_task(const std::string& format_prefix, bool bgra_swap = false) { - // Allow rgba->bgra transformation for old GeForce cards - const std::string swizzle = bgra_swap? ".bgra" : ""; - - std::string kernel = - " vec4 aa_sample = imageLoad(multisampled, aa_coords, sample_index);\n" - " imageStore(resolve, resolve_coords, aa_sample" + swizzle + ");\n"; - - build(kernel, format_prefix, 0); + // BGRA-swap flag is a workaround to swap channels for old GeForce cards with broken compute image handling + build(format_prefix, false, bgra_swap); } }; @@ -159,14 +90,8 @@ namespace vk { cs_unresolve_task(const std::string& format_prefix, bool bgra_swap = false) { - // Allow rgba->bgra transformation for old GeForce cards - const std::string swizzle = bgra_swap? ".bgra" : ""; - - std::string kernel = - " vec4 resolved_sample = imageLoad(resolve, resolve_coords);\n" - " imageStore(multisampled, aa_coords, sample_index, resolved_sample" + swizzle + ");\n"; - - build(kernel, format_prefix, 1); + // BGRA-swap flag is a workaround to swap channels for old GeForce cards with broken compute image handling + build(format_prefix, true, bgra_swap); } }; @@ -184,43 +109,12 @@ namespace vk // Depth-stencil buffers are almost never filterable, and we do not need it here (1:1 mapping) m_sampler_filter = VK_FILTER_NEAREST; + + // Do not use UBOs + m_num_uniform_buffers = 0; } - void build(const std::string& kernel, const std::string& extensions, const std::vector& inputs) - { - vs_src = - "#version 450\n" - "#extension GL_ARB_separate_shader_objects : enable\n\n" - "\n" - "void main()\n" - "{\n" - " vec2 positions[] = {vec2(-1., -1.), vec2(1., -1.), vec2(-1., 1.), vec2(1., 1.)};\n" - " gl_Position = vec4(positions[gl_VertexIndex % 4], 0., 1.);\n" - "}\n"; - - fs_src = - "#version 420\n" - "#extension GL_ARB_separate_shader_objects : enable\n"; - fs_src += extensions + - "\n" - "layout(push_constant) uniform static_data{ ivec" + std::to_string(static_parameters_width) + " regs[1]; };\n"; - - int binding = 1; - for (const auto& input : inputs) - { - fs_src += "layout(set=0, binding=" + std::to_string(binding++) + ") uniform " + input + ";\n"; - } - - fs_src += - "//layout(pixel_center_integer) in vec4 gl_FragCoord;\n" - "\n" - "void main()\n" - "{\n"; - fs_src += kernel + - "}\n"; - - rsx_log.notice("Resolve shader:\n%s", fs_src); - } + void build(bool resolve_depth, bool resolve_stencil, bool unresolve); std::vector get_push_constants() override { @@ -263,15 +157,7 @@ namespace vk { depthonly_resolve() { - build( - " ivec2 out_coord = ivec2(gl_FragCoord.xy);\n" - " ivec2 in_coord = (out_coord / regs[0].xy);\n" - " ivec2 sample_loc = out_coord % regs[0].xy;\n" - " int sample_index = sample_loc.x + (sample_loc.y * regs[0].y);\n" - " float frag_depth = texelFetch(fs0, in_coord, sample_index).x;\n" - " gl_FragDepth = frag_depth;\n", - "", - { "sampler2DMS fs0" }); + build(true, false, false); } void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass) @@ -291,15 +177,7 @@ namespace vk { depthonly_unresolve() { - build( - " ivec2 pixel_coord = ivec2(gl_FragCoord.xy);\n" - " pixel_coord *= regs[0].xy;\n" - " pixel_coord.x += (gl_SampleID % regs[0].x);\n" - " pixel_coord.y += (gl_SampleID / regs[0].x);\n" - " float frag_depth = texelFetch(fs0, pixel_coord, 0).x;\n" - " gl_FragDepth = frag_depth;\n", - "", - { "sampler2D fs0" }); + build(true, false, true); } void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass) @@ -340,15 +218,7 @@ namespace vk static_parameters_width = 3; - build( - " ivec2 out_coord = ivec2(gl_FragCoord.xy);\n" - " ivec2 in_coord = (out_coord / regs[0].xy);\n" - " ivec2 sample_loc = out_coord % regs[0].xy;\n" - " int sample_index = sample_loc.x + (sample_loc.y * regs[0].y);\n" - " uint frag_stencil = texelFetch(fs0, in_coord, sample_index).x;\n" - " if ((frag_stencil & uint(regs[0].z)) == 0) discard;\n", - "", - {"usampler2DMS fs0"}); + build(false, true, false); } void get_dynamic_state_entries(std::vector& state_descriptors) override @@ -407,15 +277,7 @@ namespace vk static_parameters_width = 3; - build( - " ivec2 pixel_coord = ivec2(gl_FragCoord.xy);\n" - " pixel_coord *= regs[0].xy;\n" - " pixel_coord.x += (gl_SampleID % regs[0].x);\n" - " pixel_coord.y += (gl_SampleID / regs[0].x);\n" - " uint frag_stencil = texelFetch(fs0, pixel_coord, 0).x;\n" - " if ((frag_stencil & uint(regs[0].z)) == 0) discard;\n", - "", - { "usampler2D fs0" }); + build(false, true, false); } void get_dynamic_state_entries(std::vector& state_descriptors) override @@ -468,19 +330,7 @@ namespace vk renderpass_config.set_stencil_mask(0xFF); m_num_usable_samplers = 2; - build( - " ivec2 out_coord = ivec2(gl_FragCoord.xy);\n" - " ivec2 in_coord = (out_coord / regs[0].xy);\n" - " ivec2 sample_loc = out_coord % ivec2(regs[0].xy);\n" - " int sample_index = sample_loc.x + (sample_loc.y * regs[0].y);\n" - " float frag_depth = texelFetch(fs0, in_coord, sample_index).x;\n" - " uint frag_stencil = texelFetch(fs1, in_coord, sample_index).x;\n" - " gl_FragDepth = frag_depth;\n" - " gl_FragStencilRefARB = int(frag_stencil);\n", - - "#extension GL_ARB_shader_stencil_export : enable\n", - - { "sampler2DMS fs0", "usampler2DMS fs1" }); + build(true, true, false); } void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass) @@ -510,19 +360,7 @@ namespace vk renderpass_config.set_stencil_mask(0xFF); m_num_usable_samplers = 2; - build( - " ivec2 pixel_coord = ivec2(gl_FragCoord.xy);\n" - " pixel_coord *= regs[0].xy;\n" - " pixel_coord.x += (gl_SampleID % regs[0].x);\n" - " pixel_coord.y += (gl_SampleID / regs[0].x);\n" - " float frag_depth = texelFetch(fs0, pixel_coord, 0).x;\n" - " uint frag_stencil = texelFetch(fs1, pixel_coord, 0).x;\n" - " gl_FragDepth = frag_depth;\n" - " gl_FragStencilRefARB = int(frag_stencil);\n", - - "#extension GL_ARB_shader_stencil_export : enable\n", - - { "sampler2D fs0", "usampler2D fs1" }); + build(true, true, true); } void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass) diff --git a/rpcs3/Emu/RSX/VK/VKResourceManager.h b/rpcs3/Emu/RSX/VK/VKResourceManager.h index 5a7e53896c..11e6558018 100644 --- a/rpcs3/Emu/RSX/VK/VKResourceManager.h +++ b/rpcs3/Emu/RSX/VK/VKResourceManager.h @@ -1,12 +1,11 @@ #pragma once -#include "vkutils/image.h" +#include "Emu/RSX/VK/vkutils/sync.h" #include "vkutils/garbage_collector.h" #include "vkutils/query_pool.hpp" #include "vkutils/sampler.h" #include "Utilities/mutex.h" -#include #include #include diff --git a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp index f32cbfdf3d..b31e8121e4 100644 --- a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp @@ -1,13 +1,14 @@ #include "stdafx.h" + #include "VKShaderInterpreter.h" #include "VKCommonPipelineLayout.h" #include "VKVertexProgram.h" #include "VKFragmentProgram.h" -#include "VKGSRender.h" - #include "../Program/GLSLCommon.h" #include "../Program/ShaderInterpreter.h" #include "../rsx_methods.h" +#include "VKHelpers.h" +#include "VKRenderPass.h" namespace vk { diff --git a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h index 9dcd109402..0820f7f020 100644 --- a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h +++ b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.h @@ -1,5 +1,7 @@ #pragma once -#include "VKProgramBuffer.h" +#include "Emu/RSX/VK/VKProgramPipeline.h" +#include "Emu/RSX/Program/ProgramStateCache.h" +#include "Emu/RSX/VK/VKPipelineCompiler.h" #include "vkutils/descriptors.h" #include diff --git a/rpcs3/Emu/RSX/VK/VKTexture.cpp b/rpcs3/Emu/RSX/VK/VKTexture.cpp index c44df5764d..1f6be63c78 100644 --- a/rpcs3/Emu/RSX/VK/VKTexture.cpp +++ b/rpcs3/Emu/RSX/VK/VKTexture.cpp @@ -5,7 +5,6 @@ #include "VKHelpers.h" #include "VKFormats.h" #include "VKRenderPass.h" -#include "VKRenderTargets.h" #include "vkutils/data_heap.h" #include "vkutils/image_helpers.h" diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp index 503474e6ac..7107e1f32b 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp @@ -1,7 +1,8 @@ #include "stdafx.h" -#include "VKGSRender.h" +#include "Emu/RSX/VK/VKGSRenderTypes.hpp" #include "VKTextureCache.h" #include "VKCompute.h" +#include "VKAsyncScheduler.h" #include "util/asm.hpp" diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.h b/rpcs3/Emu/RSX/VK/VKTextureCache.h index 4696062c70..d7d3f81e8d 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.h +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.h @@ -1,6 +1,5 @@ #pragma once -#include "VKAsyncScheduler.h" #include "VKDMA.h" #include "VKRenderTargets.h" #include "VKResourceManager.h" @@ -10,8 +9,6 @@ #include "../Common/texture_cache.h" #include "../Common/tiled_dma_copy.hpp" -#include "Emu/Cell/timers.hpp" - #include #include diff --git a/rpcs3/Emu/RSX/VK/VKVertexProgram.h b/rpcs3/Emu/RSX/VK/VKVertexProgram.h index 940272d069..1bb6dfd91c 100644 --- a/rpcs3/Emu/RSX/VK/VKVertexProgram.h +++ b/rpcs3/Emu/RSX/VK/VKVertexProgram.h @@ -1,7 +1,5 @@ #pragma once #include "../Program/VertexProgramDecompiler.h" -#include "Utilities/Thread.h" -#include "VulkanAPI.h" #include "VKProgramPipeline.h" #include "vkutils/pipeline_binding_table.h" diff --git a/rpcs3/Emu/RSX/VK/upscalers/upscaling.h b/rpcs3/Emu/RSX/VK/upscalers/upscaling.h index 3f51558687..e9ebe31931 100644 --- a/rpcs3/Emu/RSX/VK/upscalers/upscaling.h +++ b/rpcs3/Emu/RSX/VK/upscalers/upscaling.h @@ -1,7 +1,5 @@ #pragma once -#include "util/types.hpp" - #include "../vkutils/commands.h" #include "../vkutils/image.h" diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index aa326982f6..9a4471b784 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -752,7 +752,25 @@ namespace vk device_fault_info.pNext = const_cast(device.pNext); device_fault_info.deviceFault = VK_TRUE; device_fault_info.deviceFaultVendorBinary = VK_FALSE; - device_fault_info.pNext = &device_fault_info; + device.pNext = &device_fault_info; + } + + VkPhysicalDeviceConditionalRenderingFeaturesEXT conditional_rendering_info{}; + if (pgpu->optional_features_support.conditional_rendering) + { + conditional_rendering_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT; + conditional_rendering_info.pNext = const_cast(device.pNext); + conditional_rendering_info.conditionalRendering = VK_TRUE; + device.pNext = &conditional_rendering_info; + } + + VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR shader_barycentric_info{}; + if (pgpu->optional_features_support.barycentric_coords) + { + shader_barycentric_info.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR; + shader_barycentric_info.pNext = const_cast(device.pNext); + shader_barycentric_info.fragmentShaderBarycentric = VK_TRUE; + device.pNext = &shader_barycentric_info; } if (auto error = vkCreateDevice(*pgpu, &device, nullptr, &dev)) diff --git a/rpcs3/Emu/RSX/VK/vkutils/image_helpers.cpp b/rpcs3/Emu/RSX/VK/vkutils/image_helpers.cpp index fdebf35f37..e8d8bc9c0d 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/image_helpers.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/image_helpers.cpp @@ -1,10 +1,8 @@ #include "stdafx.h" #include "image_helpers.h" #include "image.h" -#include "util/logs.hpp" #include "../VKRenderPass.h" #include "../../color_utils.h" -#include "../../gcm_enums.h" namespace vk { diff --git a/rpcs3/Emu/RSX/VK/vkutils/query_pool.hpp b/rpcs3/Emu/RSX/VK/vkutils/query_pool.hpp index 3cf0146c0b..0e480c2d0a 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/query_pool.hpp +++ b/rpcs3/Emu/RSX/VK/vkutils/query_pool.hpp @@ -2,6 +2,7 @@ #include "../VulkanAPI.h" #include "../../rsx_utils.h" +#include "shared.h" namespace vk { diff --git a/rpcs3/Emu/RSX/VK/vkutils/scratch.cpp b/rpcs3/Emu/RSX/VK/vkutils/scratch.cpp index a34c90091b..041067bea6 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/scratch.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/scratch.cpp @@ -1,10 +1,8 @@ #include "barriers.h" #include "buffer_object.h" #include "image.h" -#include "sampler.h" #include "../VKResourceManager.h" -#include "Utilities/address_range.h" #include diff --git a/rpcs3/Emu/RSX/VK/vkutils/shared.cpp b/rpcs3/Emu/RSX/VK/vkutils/shared.cpp index 16bc0066ae..b63bf89ec5 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/shared.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/shared.cpp @@ -29,37 +29,24 @@ namespace vk std::vector vendor_binary_data; std::string fault_description; -#ifdef _MSC_VER - __try + // Retrieve sizes + g_render_device->_vkGetDeviceFaultInfoEXT(*g_render_device, &fault_counts, nullptr); + + // Resize arrays and fill + address_info.resize(fault_counts.addressInfoCount); + vendor_info.resize(fault_counts.vendorInfoCount); + vendor_binary_data.resize(fault_counts.vendorBinarySize); + + VkDeviceFaultInfoEXT fault_info { -#endif - // Retrieve sizes - g_render_device->_vkGetDeviceFaultInfoEXT(*g_render_device, &fault_counts, nullptr); - - // Resize arrays and fill - address_info.resize(fault_counts.addressInfoCount); - vendor_info.resize(fault_counts.vendorInfoCount); - vendor_binary_data.resize(fault_counts.vendorBinarySize); - - VkDeviceFaultInfoEXT fault_info - { - .sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT, - .pAddressInfos = address_info.data(), - .pVendorInfos = vendor_info.data(), - .pVendorBinaryData = vendor_binary_data.data() - }; - g_render_device->_vkGetDeviceFaultInfoEXT(*g_render_device, &fault_counts, &fault_info); - - fault_description = fault_info.description; -#ifdef _MSC_VER - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - rsx_log.error("Driver crashed retrieving extended crash information. Are you running on an NVIDIA card?"); - return "Extended fault information is not available. The driver crashed when retrieving the details."; - } -#endif + .sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT, + .pAddressInfos = address_info.data(), + .pVendorInfos = vendor_info.data(), + .pVendorBinaryData = vendor_binary_data.data() + }; + g_render_device->_vkGetDeviceFaultInfoEXT(*g_render_device, &fault_counts, &fault_info); + fault_description = fault_info.description; std::string fault_message = fmt::format( "Device Fault Information:\n" "Fault Summary:\n" diff --git a/rpcs3/Emu/RSX/color_utils.h b/rpcs3/Emu/RSX/color_utils.h index 1f78a18202..7d315c66ad 100644 --- a/rpcs3/Emu/RSX/color_utils.h +++ b/rpcs3/Emu/RSX/color_utils.h @@ -3,6 +3,7 @@ #include #include #include "gcm_enums.h" +#include "Utilities/StrFmt.h" namespace rsx { diff --git a/rpcs3/Emu/RSX/gcm_enums.cpp b/rpcs3/Emu/RSX/gcm_enums.cpp index b6a0292fd9..5c7d88c875 100644 --- a/rpcs3/Emu/RSX/gcm_enums.cpp +++ b/rpcs3/Emu/RSX/gcm_enums.cpp @@ -1,8 +1,6 @@ #include "gcm_enums.h" #include "Utilities/StrFmt.h" -#include "Utilities/Thread.h" - using namespace rsx; template <> diff --git a/rpcs3/Emu/RSX/gcm_enums.h b/rpcs3/Emu/RSX/gcm_enums.h index dcf8b51b28..eaa182171e 100644 --- a/rpcs3/Emu/RSX/gcm_enums.h +++ b/rpcs3/Emu/RSX/gcm_enums.h @@ -2,7 +2,6 @@ #include "util/types.hpp" #include "Common/expected.hpp" -#include "Utilities/StrFmt.h" namespace gcm { diff --git a/rpcs3/Emu/RSX/gcm_printing.h b/rpcs3/Emu/RSX/gcm_printing.h index 46c1b7ea1a..0542349644 100644 --- a/rpcs3/Emu/RSX/gcm_printing.h +++ b/rpcs3/Emu/RSX/gcm_printing.h @@ -2,7 +2,6 @@ #include "util/types.hpp" #include -#include namespace rsx { diff --git a/rpcs3/Emu/RSX/rsx_cache.h b/rpcs3/Emu/RSX/rsx_cache.h index f5eca2c85b..4bbec32d99 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -1,4 +1,5 @@ #pragma once +#include "../system_config.h" #include "Utilities/File.h" #include "Utilities/lockless.h" #include "Utilities/Thread.h" @@ -6,8 +7,8 @@ #include "Common/unordered_map.hpp" #include "Emu/System.h" #include "Emu/cache_utils.hpp" -#include "Program/ProgramStateCache.h" -#include "Common/texture_cache_checker.h" +#include "Emu/RSX/Program/RSXVertexProgram.h" +#include "Emu/RSX/Program/RSXFragmentProgram.h" #include "Overlays/Shaders/shader_loading_dialog.h" #include diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp index a54a743996..ac5d92eef2 100644 --- a/rpcs3/Emu/RSX/rsx_methods.cpp +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -1,13 +1,11 @@ #include "stdafx.h" #include "rsx_methods.h" #include "RSXThread.h" -#include "rsx_utils.h" -#include "rsx_decode.h" -#include "Common/time.hpp" -#include "Emu/Cell/PPUCallback.h" +#include "Emu/Cell/PPUThread.h" #include "Emu/Cell/lv2/sys_rsx.h" -#include "Emu/RSX/Common/BufferUtils.h" + +#include "Emu/System.h" #include "Emu/RSX/NV47/HW/nv47.h" #include "Emu/RSX/NV47/HW/nv47_sync.hpp" #include "Emu/RSX/NV47/HW/context_accessors.define.h" // TODO: Context objects belong in FW not HW diff --git a/rpcs3/Emu/RSX/rsx_methods.h b/rpcs3/Emu/RSX/rsx_methods.h index d245a443e3..dbcb8bb15d 100644 --- a/rpcs3/Emu/RSX/rsx_methods.h +++ b/rpcs3/Emu/RSX/rsx_methods.h @@ -6,8 +6,6 @@ #include "rsx_decode.h" #include "RSXTexture.h" #include "rsx_vertex_data.h" -#include "Common/simple_array.hpp" -#include "Emu/Cell/timers.hpp" #include "Program/program_util.h" #include "NV47/FW/draw_call.hpp" diff --git a/rpcs3/Emu/RSX/rsx_utils.cpp b/rpcs3/Emu/RSX/rsx_utils.cpp index 03197d5ac1..7ca410a657 100644 --- a/rpcs3/Emu/RSX/rsx_utils.cpp +++ b/rpcs3/Emu/RSX/rsx_utils.cpp @@ -1,9 +1,7 @@ #include "stdafx.h" #include "rsx_utils.h" #include "rsx_methods.h" -#include "Emu/RSX/GCM.h" #include "Emu/Cell/Modules/cellVideoOut.h" -#include "Overlays/overlays.h" #ifdef _MSC_VER #pragma warning(push, 0) diff --git a/rpcs3/Emu/RSX/rsx_utils.h b/rpcs3/Emu/RSX/rsx_utils.h index 513d3c1bb8..c5ce6de16d 100644 --- a/rpcs3/Emu/RSX/rsx_utils.h +++ b/rpcs3/Emu/RSX/rsx_utils.h @@ -5,10 +5,6 @@ #include "Utilities/geometry.h" #include "gcm_enums.h" -#include -#include -#include - extern "C" { #include diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index cb31823026..7cb2c444aa 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -10,6 +10,7 @@ #include "Emu/vfs_config.h" #include "Emu/IPC_config.h" #include "Emu/savestate_utils.hpp" +#include "Emu/cache_utils.hpp" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" @@ -41,14 +42,12 @@ #include "Utilities/StrUtil.h" #include "../Crypto/unself.h" -#include "../Crypto/unzip.h" #include "util/logs.hpp" #include "util/init_mutex.hpp" +#include "util/sysinfo.hpp" -#include #include #include -#include #include "Utilities/JIT.h" @@ -62,6 +61,10 @@ #include "Emu/RSX/GSRender.h" +#ifdef LLVM_AVAILABLE +#include "llvm/Config/llvm-config.h" +#endif + LOG_CHANNEL(sys_log, "SYS"); // Preallocate 32 MiB @@ -345,6 +348,19 @@ extern void dump_executable(std::span data, const ppu_module* void Emulator::Init() { + // Log LLVM version + if (static bool logged_llvm = false; !logged_llvm) + { +#ifndef LLVM_AVAILABLE + sys_log.always()("LLVM version: Compiled without LLVM"); +#elif defined (LLVM_VERSION_STRING) + sys_log.always()("LLVM version: %s", LLVM_VERSION_STRING); +#else + sys_log.always()("LLVM version: Unknown"); +#endif + logged_llvm = true; + } + jit_runtime::initialize(); const std::string emu_dir = rpcs3::utils::get_emu_dir(); @@ -970,21 +986,26 @@ game_boot_result Emulator::BootGame(const std::string& path, const std::string& if (m_state == system_state::stopped) { std::tie(m_path, m_path_original, argv, envp, data, disc, klic, hdd1, m_config_mode, m_config_path) = std::move(save_args); + + if (result != game_boot_result::no_errors) + { + GetCallbacks().close_gs_frame(); + } } else { ensure(m_state == system_state::stopping); // Execute after Kill() is done - Emu.after_kill_callback = [save_args = std::move(save_args), this]() mutable + Emu.after_kill_callback = [this, result, save_args = std::move(save_args)]() mutable { std::tie(m_path, m_path_original, argv, envp, data, disc, klic, hdd1, m_config_mode, m_config_path) = std::move(save_args); - }; - } - if (result != game_boot_result::no_errors) - { - GetCallbacks().close_gs_frame(); + if (result != game_boot_result::no_errors) + { + GetCallbacks().close_gs_frame(); + } + }; } } @@ -1100,47 +1121,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, } else { - fs::file save{m_path, fs::isfile + fs::read}; - - if (m_path.ends_with(".SAVESTAT") && save && save.size() >= 8 && save.read() == "RPCS3SAV"_u64) - { - m_ar = std::make_shared(); - m_ar->set_reading_state(); - - m_ar->m_file_handler = make_uncompressed_serialization_file_handler(std::move(save)); - } - else if (save && m_path.ends_with(".zst")) - { - m_ar = std::make_shared(); - m_ar->set_reading_state(); - - m_ar->m_file_handler = make_compressed_zstd_serialization_file_handler(std::move(save)); - - if (m_ar->try_read().second != "RPCS3SAV"_u64) - { - m_ar.reset(); - } - else - { - m_ar->pos = 0; - } - } - else if (save && m_path.ends_with(".gz")) - { - m_ar = std::make_shared(); - m_ar->set_reading_state(); - - m_ar->m_file_handler = make_compressed_serialization_file_handler(std::move(save)); - - if (m_ar->try_read().second != "RPCS3SAV"_u64) - { - m_ar.reset(); - } - else - { - m_ar->pos = 0; - } - } + m_ar = make_savestate_reader(m_path); m_boot_source_type = CELL_GAME_GAMETYPE_SYS; } @@ -1172,18 +1153,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, if (m_ar) { - struct file_header - { - ENABLE_BITWISE_SERIALIZATION; - - nse_t magic; - bool LE_format; - bool state_inspection_support; - nse_t offset; - b8 flag_versions_is_following_data; - }; - - const auto header = m_ar->try_read().second; + const auto header = m_ar->try_read().second; if (header.magic != "RPCS3SAV"_u64) { @@ -1312,7 +1282,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, if (m_ar->m_max_data != m_ar->pos) { - fmt::throw_exception("TAR desrialization failed: read bytes: 0x%x, expected: 0x%x, path='%s', ar: %s", m_ar->pos - (m_ar->m_max_data - size), size, path, *m_ar); + fmt::throw_exception("TAR deserialization failed: read bytes: 0x%x, expected: 0x%x, path='%s', ar: %s", m_ar->pos - (m_ar->m_max_data - size), size, path, *m_ar); } m_ar->m_max_data = umax; @@ -3067,6 +3037,11 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s if (!IsStopped() && savestate) { + if (IsStarting()) + { + return; + } + if (!save_stage || !save_stage->prepared) { if (m_emu_state_close_pending.exchange(true)) @@ -3371,6 +3346,15 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s static_cast(init_mtx->init()); + // Call explcit semi-destructors (free memory before savestate) + for (const auto& [type, data] : *g_fxo) + { + if (type.thread_op) + { + type.thread_op(data, thread_state::destroying_context); + } + } + auto set_progress_message = [&](std::string_view text) { *verbose_message = stx::make_single(text); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index bd127aff4f..83a889af94 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -12,8 +12,6 @@ #include #include -#include "Emu/Cell/timers.hpp" - void init_fxo_for_exec(utils::serial*, bool); enum class localized_string_id; @@ -426,7 +424,8 @@ public: static void CleanUp(); bool IsRunning() const { return m_state == system_state::running; } - bool IsPaused() const { return m_state >= system_state::paused; } // ready/starting are also considered paused by this function + bool IsPaused() const { system_state state = m_state; return state >= system_state::paused && state <= system_state::frozen; } + bool IsPausedOrReady() const { return m_state >= system_state::paused; } bool IsStopped(bool test_fully = false) const { return test_fully ? m_state == system_state::stopped : m_state <= system_state::stopping; } bool IsReady() const { return m_state == system_state::ready; } bool IsStarting() const { return m_state == system_state::starting; } diff --git a/rpcs3/Emu/perf_meter.cpp b/rpcs3/Emu/perf_meter.cpp index 70cfa6b63e..adaed4cf47 100644 --- a/rpcs3/Emu/perf_meter.cpp +++ b/rpcs3/Emu/perf_meter.cpp @@ -5,6 +5,7 @@ #include "util/fence.hpp" #include "util/tsc.hpp" #include "Utilities/Thread.h" +#include "Utilities/mutex.h" #include #include diff --git a/rpcs3/Emu/perf_monitor.cpp b/rpcs3/Emu/perf_monitor.cpp index 5f0c6ad84b..15cdcf498f 100644 --- a/rpcs3/Emu/perf_monitor.cpp +++ b/rpcs3/Emu/perf_monitor.cpp @@ -2,6 +2,7 @@ #include "perf_monitor.hpp" #include "Emu/System.h" +#include "Emu/Cell/timers.hpp" #include "util/cpu_stats.hpp" #include "Utilities/Thread.h" diff --git a/rpcs3/Emu/perf_monitor.hpp b/rpcs3/Emu/perf_monitor.hpp index 634e891c83..a37b14fd9a 100644 --- a/rpcs3/Emu/perf_monitor.hpp +++ b/rpcs3/Emu/perf_monitor.hpp @@ -1,6 +1,7 @@ #pragma once -#include "util/types.hpp" +#include +using namespace std::literals; struct perf_monitor { diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index bb3e440564..31b24eba5f 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "util/types.hpp" #include "util/logs.hpp" -#include "util/asm.hpp" #include "util/v128.hpp" #include "util/simd.hpp" #include "Utilities/File.h" @@ -12,7 +11,6 @@ #include "System.h" #include -#include #include LOG_CHANNEL(sys_log, "SYS"); @@ -162,6 +160,60 @@ std::vector get_savestate_versioning_data(fs::file&& file, std::s return ver_data; } +std::shared_ptr make_savestate_reader(const std::string& path) +{ + std::shared_ptr ar; + + fs::file save{path, fs::isfile + fs::read}; + + if (!save) + { + return ar; + } + + if (path.ends_with(".SAVESTAT") && save.size() >= 8 && save.read() == "RPCS3SAV"_u64) + { + ar = std::make_shared(); + ar->set_reading_state(); + + ar->m_file_handler = make_uncompressed_serialization_file_handler(std::move(save)); + } + else if (path.ends_with(".zst")) + { + ar = std::make_shared(); + ar->set_reading_state(); + + ar->m_file_handler = make_compressed_zstd_serialization_file_handler(std::move(save)); + + if (ar->try_read().second != "RPCS3SAV"_u64) + { + ar.reset(); + } + else + { + ar->pos = 0; + } + } + else if (path.ends_with(".gz")) + { + ar = std::make_shared(); + ar->set_reading_state(); + + ar->m_file_handler = make_compressed_serialization_file_handler(std::move(save)); + + if (ar->try_read().second != "RPCS3SAV"_u64) + { + ar.reset(); + } + else + { + ar->pos = 0; + } + } + + return ar; +} + bool is_savestate_version_compatible(const std::vector& data, bool is_boot_check) { if (data.empty()) @@ -256,6 +308,15 @@ bool is_savestate_compatible(fs::file&& file, std::string_view filepath) return is_savestate_version_compatible(get_savestate_versioning_data(std::move(file), filepath), false); } +bool is_savestate_compatible(const std::string& filepath) +{ + if (fs::file file{filepath, fs::isfile + fs::read}) + { + return is_savestate_compatible(std::move(file), filepath); + } + return false; +} + std::vector read_used_savestate_versions() { std::vector used_serial; diff --git a/rpcs3/Emu/savestate_utils.hpp b/rpcs3/Emu/savestate_utils.hpp index 66d445369c..18028ae9ec 100644 --- a/rpcs3/Emu/savestate_utils.hpp +++ b/rpcs3/Emu/savestate_utils.hpp @@ -10,6 +10,16 @@ struct version_entry ENABLE_BITWISE_SERIALIZATION; }; +struct savestate_header +{ + ENABLE_BITWISE_SERIALIZATION; + + nse_t magic; + bool LE_format; + bool state_inspection_support; + nse_t offset; + b8 flag_versions_is_following_data; +}; struct hle_locks_t { @@ -27,9 +37,11 @@ struct hle_locks_t bool try_finalize(std::function test); }; +std::shared_ptr make_savestate_reader(const std::string& path); bool load_and_check_reserved(utils::serial& ar, usz size); bool is_savestate_version_compatible(const std::vector& data, bool is_boot_check); std::vector get_savestate_versioning_data(fs::file&& file, std::string_view filepath); bool is_savestate_compatible(fs::file&& file, std::string_view filepath); +bool is_savestate_compatible(const std::string& filepath); std::vector read_used_savestate_versions(); -std::string get_savestate_file(std::string_view title_id, std::string_view boot_path, s64 abs_id, s64 rel_id); \ No newline at end of file +std::string get_savestate_file(std::string_view title_id, std::string_view boot_path, s64 abs_id, s64 rel_id); diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index d3dfab4ce8..993294fc92 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -1,6 +1,5 @@ #pragma once -#include "config_mode.h" #include "system_config_types.h" #include "Utilities/Config.h" @@ -68,7 +67,7 @@ struct cfg_root : cfg::node cfg::_enum spu_xfloat_accuracy{ this, "XFloat Accuracy", xfloat_accuracy::approximate, false }; cfg::_int<-1, 14> ppu_128_reservations_loop_max_length{ this, "Accurate PPU 128-byte Reservation Op Max Length", 0, true }; // -1: Always accurate, 0: Never accurate, 1-14: max accurate loop length cfg::_int<-64, 64> stub_ppu_traps{ this, "Stub PPU Traps", 0, true }; // Hack, skip PPU traps for rare cases where the trap is continueable (specify relative instructions to skip) - cfg::_bool full_width_avx512{ this, "Full Width AVX-512", true }; + cfg::_bool precise_spu_verification{ this, "Precise SPU Verification", false }; // Disables use of xorsum based spu verification if enabled. cfg::_bool ppu_llvm_nj_fixup{ this, "PPU LLVM Java Mode Handling", true }; // Partially respect current Java Mode for alti-vec ops by PPU LLVM cfg::_bool use_accurate_dfma{ this, "Use Accurate DFMA", true }; // Enable accurate double-precision FMA for CPUs which do not support it natively cfg::_bool ppu_set_sat_bit{ this, "PPU Set Saturation Bit", false }; // Accuracy. If unset, completely disable saturation flag handling. diff --git a/rpcs3/Emu/system_config_types.h b/rpcs3/Emu/system_config_types.h index f3e3b31f42..9459ba698d 100644 --- a/rpcs3/Emu/system_config_types.h +++ b/rpcs3/Emu/system_config_types.h @@ -267,7 +267,7 @@ enum class np_internet_status enabled, }; -enum np_psn_status +enum class np_psn_status { disabled, psn_fake, diff --git a/rpcs3/Emu/system_utils.cpp b/rpcs3/Emu/system_utils.cpp index 49439dc3fc..2ccb15bd3f 100644 --- a/rpcs3/Emu/system_utils.cpp +++ b/rpcs3/Emu/system_utils.cpp @@ -6,7 +6,6 @@ #include "Emu/System.h" #include "util/sysinfo.hpp" #include "Utilities/File.h" -#include "Utilities/StrUtil.h" #include "Utilities/Thread.h" #include "Crypto/unpkg.h" #include "Crypto/unself.h" diff --git a/rpcs3/Emu/vfs_config.cpp b/rpcs3/Emu/vfs_config.cpp index 0d7508a284..bd292f9a25 100644 --- a/rpcs3/Emu/vfs_config.cpp +++ b/rpcs3/Emu/vfs_config.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "vfs_config.h" #include "Utilities/StrUtil.h" -#include "Utilities/StrFmt.h" LOG_CHANNEL(vfs_log, "VFS"); diff --git a/rpcs3/GLGSRender.vcxproj b/rpcs3/GLGSRender.vcxproj index 013888ead4..dbaffc5d00 100644 --- a/rpcs3/GLGSRender.vcxproj +++ b/rpcs3/GLGSRender.vcxproj @@ -60,6 +60,7 @@ + @@ -95,6 +96,7 @@ + diff --git a/rpcs3/GLGSRender.vcxproj.filters b/rpcs3/GLGSRender.vcxproj.filters index 28670f9bb5..4866b14643 100644 --- a/rpcs3/GLGSRender.vcxproj.filters +++ b/rpcs3/GLGSRender.vcxproj.filters @@ -48,6 +48,7 @@ upscalers\fsr1 + @@ -120,6 +121,7 @@ upscalers + diff --git a/rpcs3/Input/ds3_pad_handler.cpp b/rpcs3/Input/ds3_pad_handler.cpp index 0ea5aad19f..c2cf89afec 100644 --- a/rpcs3/Input/ds3_pad_handler.cpp +++ b/rpcs3/Input/ds3_pad_handler.cpp @@ -2,8 +2,6 @@ #include "ds3_pad_handler.h" #include "Emu/Io/pad_config.h" -#include "util/asm.hpp" - LOG_CHANNEL(ds3_log, "DS3"); using namespace reports; diff --git a/rpcs3/Input/ds4_pad_handler.cpp b/rpcs3/Input/ds4_pad_handler.cpp index a24351fad9..497db72e5d 100644 --- a/rpcs3/Input/ds4_pad_handler.cpp +++ b/rpcs3/Input/ds4_pad_handler.cpp @@ -678,7 +678,7 @@ int ds4_pad_handler::send_output_report(DS4Device* device) write_to_ptr(output.crc32, crcCalc); - return hid_write_control(device->hidDevice, &output.report_id, sizeof(ds4_output_report_bt)); + return hid_write(device->hidDevice, &output.report_id, sizeof(ds4_output_report_bt)); } ds4_output_report_usb output{}; diff --git a/rpcs3/Input/evdev_gun_handler.h b/rpcs3/Input/evdev_gun_handler.h index e5d83c391e..342fff40df 100644 --- a/rpcs3/Input/evdev_gun_handler.h +++ b/rpcs3/Input/evdev_gun_handler.h @@ -1,7 +1,6 @@ #pragma once #ifdef HAVE_LIBEVDEV -#include #include #include "Utilities/mutex.h" diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index d101d74173..989e95929d 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -8,7 +8,6 @@ #include "evdev_joystick_handler.h" #include "util/logs.hpp" -#include #include #include #include diff --git a/rpcs3/Input/evdev_joystick_handler.h b/rpcs3/Input/evdev_joystick_handler.h index f90fe12038..abf6f00988 100644 --- a/rpcs3/Input/evdev_joystick_handler.h +++ b/rpcs3/Input/evdev_joystick_handler.h @@ -9,7 +9,6 @@ #include #include #include -#include #include struct positive_axis : cfg::node diff --git a/rpcs3/Input/gui_pad_thread.cpp b/rpcs3/Input/gui_pad_thread.cpp index 87fa0999f3..08d6b4e9ee 100644 --- a/rpcs3/Input/gui_pad_thread.cpp +++ b/rpcs3/Input/gui_pad_thread.cpp @@ -14,7 +14,6 @@ #include "sdl_pad_handler.h" #endif #include "Emu/Io/PadHandler.h" -#include "Emu/System.h" #include "Emu/system_config.h" #include "Utilities/Thread.h" #include "rpcs3qt/gui_settings.h" @@ -40,6 +39,8 @@ LOG_CHANNEL(gui_log, "GUI"); +atomic_t gui_pad_thread::m_reset = false; + gui_pad_thread::gui_pad_thread() { m_thread = std::make_unique(&gui_pad_thread::run, this); @@ -145,6 +146,11 @@ bool gui_pad_thread::init() gui_log.notice("gui_pad_thread: Pad %d: device='%s', handler=%s, VID=0x%x, PID=0x%x, class_type=0x%x, class_profile=0x%x", i, cfg->device.to_string(), m_pad->m_pad_handler, m_pad->m_vendor_id, m_pad->m_product_id, m_pad->m_class_type, m_pad->m_class_profile); + if (handler_type != pad_handler::null) + { + input_log.notice("gui_pad_thread %d: config=\n%s", i, cfg->to_string()); + } + // We only use one pad break; } @@ -251,14 +257,19 @@ void gui_pad_thread::run() gui_log.notice("gui_pad_thread: Pad thread started"); - if (!init()) - { - gui_log.warning("gui_pad_thread: Pad thread stopped (init failed)"); - return; - } + m_reset = true; while (!m_terminate) { + if (m_reset && m_reset.exchange(false)) + { + if (!init()) + { + gui_log.warning("gui_pad_thread: Pad thread stopped (init failed during reset)"); + return; + } + } + // Only process input if there is an active window if (m_handler && m_pad && (m_allow_global_input || QApplication::activeWindow())) { diff --git a/rpcs3/Input/gui_pad_thread.h b/rpcs3/Input/gui_pad_thread.h index 4bc0414200..235d97e8b1 100644 --- a/rpcs3/Input/gui_pad_thread.h +++ b/rpcs3/Input/gui_pad_thread.h @@ -23,6 +23,11 @@ public: static std::shared_ptr GetHandler(pad_handler type); static void InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr& handler); + static void reset() + { + m_reset = true; + } + protected: bool init(); void run(); @@ -60,6 +65,7 @@ protected: std::unique_ptr m_thread; atomic_t m_terminate = false; atomic_t m_allow_global_input = false; + static atomic_t m_reset; std::array(pad_button::pad_button_max_enum)> m_last_button_state{}; diff --git a/rpcs3/Input/hid_pad_handler.h b/rpcs3/Input/hid_pad_handler.h index e0e14a0a27..50e7d59e20 100644 --- a/rpcs3/Input/hid_pad_handler.h +++ b/rpcs3/Input/hid_pad_handler.h @@ -6,6 +6,8 @@ #include "hidapi.h" +#include + struct CalibData { s16 bias = 0; diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 70f0e25618..a2b91dd109 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -26,6 +26,7 @@ #include "Emu/RSX/Overlays/overlay_message.h" #include "Emu/Cell/lv2/sys_usbd.h" #include "Emu/Cell/Modules/cellGem.h" +#include "Emu/Cell/timers.hpp" #include "Utilities/Thread.h" #include "util/atomic.hpp" @@ -56,6 +57,9 @@ struct pad_setting u32 port_status = 0; u32 device_capability = 0; u32 device_type = 0; + u32 class_type = 0; + u16 vendor_id = 0; + u16 product_id = 0; bool is_ldd_pad = false; }; @@ -86,6 +90,9 @@ void pad_thread::Init() m_pads[i]->m_port_status, m_pads[i]->m_device_capability, m_pads[i]->m_device_type, + m_pads[i]->m_class_type, + m_pads[i]->m_vendor_id, + m_pads[i]->m_product_id, m_pads[i]->ldd }; } @@ -96,6 +103,9 @@ void pad_thread::Init() CELL_PAD_STATUS_DISCONNECTED, CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR, CELL_PAD_DEV_TYPE_STANDARD, + CELL_PAD_PCLASS_TYPE_STANDARD, + 0, + 0, false }; } @@ -197,6 +207,12 @@ void pad_thread::Init() { input_log.notice("Pad %d: config=\n%s", i, cfg->to_string()); } + + // If the user changes the emulated controller, then simulate unplugging and plugging in a new controller + if (m_pads_connected[i] && (pad_settings[i].class_type != pad->m_class_type || pad_settings[i].vendor_id != pad->m_vendor_id || pad_settings[i].product_id != pad->m_product_id)) + { + pad->m_disconnection_timer = get_system_time() + 30'000ull; + } } pad->is_fake_pad = ((g_cfg.io.move == move_handler::real || g_cfg.io.move == move_handler::fake) && i >= (static_cast(CELL_PAD_MAX_PORT_NUM) - static_cast(CELL_GEM_MAX_NUM))) @@ -235,6 +251,25 @@ void pad_thread::update_pad_states() for (usz i = 0; i < m_pads.size(); i++) { const auto& pad = m_pads[i]; + + // Simulate unplugging and plugging in a new controller + if (pad && pad->m_disconnection_timer > 0) + { + const bool is_connected = pad->m_port_status & CELL_PAD_STATUS_CONNECTED; + const u64 now = get_system_time(); + + if (is_connected && now < pad->m_disconnection_timer) + { + pad->m_port_status &= ~CELL_PAD_STATUS_CONNECTED; + pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; + } + else if (!is_connected && now >= pad->m_disconnection_timer) + { + pad->m_port_status |= CELL_PAD_STATUS_CONNECTED + CELL_PAD_STATUS_ASSIGN_CHANGES; + pad->m_disconnection_timer = 0; + } + } + const bool connected = pad && !pad->is_fake_pad && !!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED); if (m_pads_connected[i] == connected) @@ -603,6 +638,8 @@ void pad_thread::UnregisterLddPad(u32 handle) ensure(handle < m_pads.size()); m_pads[handle]->ldd = false; + m_pads[handle]->m_port_status &= ~CELL_PAD_STATUS_CONNECTED; + m_pads[handle]->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; num_ldd_pad--; } diff --git a/rpcs3/Input/product_info.cpp b/rpcs3/Input/product_info.cpp index 9ce26e7878..825b50d0c1 100644 --- a/rpcs3/Input/product_info.cpp +++ b/rpcs3/Input/product_info.cpp @@ -1,5 +1,9 @@ #include "Input/product_info.h" +#include + +#include "Emu/Io/pad_types.h" + namespace input { static const std::map input_products = { diff --git a/rpcs3/Input/product_info.h b/rpcs3/Input/product_info.h index da95fb4c59..82f397d2b6 100644 --- a/rpcs3/Input/product_info.h +++ b/rpcs3/Input/product_info.h @@ -1,7 +1,7 @@ #pragma once #include -#include "Emu/Io/pad_types.h" +#include "util/types.hpp" namespace input { diff --git a/rpcs3/Input/ps_move_handler.cpp b/rpcs3/Input/ps_move_handler.cpp index 4acf46cf5f..3b5aaec2f7 100644 --- a/rpcs3/Input/ps_move_handler.cpp +++ b/rpcs3/Input/ps_move_handler.cpp @@ -2,10 +2,7 @@ #include "ps_move_handler.h" #include "ps_move_calibration.h" #include "Emu/Io/pad_config.h" -#include "Emu/System.h" -#include "Emu/system_config.h" #include "Emu/Cell/Modules/cellGem.h" -#include "Input/ps_move_config.h" LOG_CHANNEL(move_log, "Move"); diff --git a/rpcs3/Input/ps_move_handler.h b/rpcs3/Input/ps_move_handler.h index d8efc463c7..9b1da1d58b 100644 --- a/rpcs3/Input/ps_move_handler.h +++ b/rpcs3/Input/ps_move_handler.h @@ -2,15 +2,6 @@ #include "hid_pad_handler.h" -#ifndef _MSC_VER -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#endif -#include "3rdparty/fusion/fusion/Fusion/Fusion.h" -#ifndef _MSC_VER -#pragma GCC diagnostic pop -#endif - #include namespace reports diff --git a/rpcs3/Input/ps_move_tracker.h b/rpcs3/Input/ps_move_tracker.h index 5c9b9df9c7..a1fa0e8936 100644 --- a/rpcs3/Input/ps_move_tracker.h +++ b/rpcs3/Input/ps_move_tracker.h @@ -1,5 +1,7 @@ #pragma once +#include "Emu/Cell/Modules/cellGem.h" + #ifdef HAVE_OPENCV constexpr bool g_ps_move_tracking_supported = true; #else diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index bbb69c30b2..9a916678ae 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -40,7 +40,7 @@ Use - ..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\zlib\zlib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm\include;$(VULKAN_SDK)\Include;..\3rdparty\zstd\zstd\lib;$(SolutionDir)3rdparty\fusion\fusion\Fusion;$(SolutionDir)3rdparty\wolfssl\extra\win32 + ..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\zlib\zlib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(VULKAN_SDK)\Include;..\3rdparty\zstd\zstd\lib;$(SolutionDir)3rdparty\fusion\fusion\Fusion;$(SolutionDir)3rdparty\wolfssl\extra\win32 MaxSpeed AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;ZLIB_CONST;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions) AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;ZLIB_CONST;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions) @@ -51,6 +51,12 @@ cmd.exe /c "$(SolutionDir)\Utilities\git-version-gen.cmd" Updating git-version.h + + ntdll.lib + + + ntdll.lib + @@ -106,6 +112,7 @@ + @@ -168,6 +175,7 @@ + @@ -611,6 +619,7 @@ + @@ -1024,6 +1033,14 @@ + + + + + + + + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 23c7c34fb6..de42637d4c 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -130,6 +130,9 @@ {caf84300-5c45-4340-bd9a-8ac859409351} + + {ce6d6b90-8313-4273-b46c-d92bd450c002} + @@ -1026,6 +1029,9 @@ Emu\NP + + Emu\NP + Emu\NP @@ -1219,6 +1225,9 @@ Emu\GPU\RSX\Core + + Emu\GPU\RSX\Core + Crypto @@ -2677,6 +2686,9 @@ Emu\NP + + Emu\NP + Emu\GPU\RSX\Core @@ -2802,5 +2814,29 @@ Emu\CPU\Backends\AArch64 + + Emu\GPU\RSX\Program\MSAA + + + Emu\GPU\RSX\Program\MSAA + + + Emu\GPU\RSX\Program\MSAA + + + Emu\GPU\RSX\Program\MSAA + + + Emu\GPU\RSX\Program\MSAA + + + Emu\GPU\RSX\Program\MSAA + + + Emu\GPU\RSX\Program\MSAA + + + Emu\GPU\RSX\Program\MSAA + \ No newline at end of file diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index e82bcc12dc..098b84532f 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -1,5 +1,6 @@ #include "headless_application.h" +#include "Emu/System.h" #include "Emu/RSX/Null/NullGSRender.h" #include "Emu/Cell/Modules/cellMsgDialog.h" #include "Emu/Cell/Modules/cellOskDialog.h" diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 894115ecc8..1f6a96dfec 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -61,15 +61,6 @@ DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResoluti #if defined(__APPLE__) #include -#if defined (__x86_64__) -// sysinfo_darwin.mm -namespace Darwin_Version -{ - extern int getNSmajorVersion(); - extern int getNSminorVersion(); - extern int getNSpatchVersion(); -} -#endif #endif #include "Utilities/Config.h" @@ -618,12 +609,10 @@ int main(int argc, char** argv) #endif #if defined(__APPLE__) && defined(__x86_64__) - const int osx_ver_major = Darwin_Version::getNSmajorVersion(); - const int osx_ver_minor = Darwin_Version::getNSminorVersion(); - if ((osx_ver_major == 14 && osx_ver_minor < 3) && (utils::get_cpu_brand().rfind("VirtualApple", 0) == 0)) + if (const utils::OS_version os = utils::get_OS_version(); + os.version_major == 14 && os.version_minor < 3 && (utils::get_cpu_brand().rfind("VirtualApple", 0) == 0)) { - const int osx_ver_patch = Darwin_Version::getNSpatchVersion(); - report_fatal_error(fmt::format("RPCS3 requires macOS 14.3.0 or later.\nYou're currently using macOS %i.%i.%i.\nPlease update macOS from System Settings.\n\n", osx_ver_major, osx_ver_minor, osx_ver_patch)); + report_fatal_error(fmt::format("RPCS3 requires macOS 14.3.0 or later.\nYou're currently using macOS %i.%i.%i.\nPlease update macOS from System Settings.\n\n", os.version_major, os.version_minor, os.version_patch)); } #endif @@ -659,12 +648,13 @@ int main(int argc, char** argv) // Write OS version logs::stored_message os{sys_log.always()}; - os.text = utils::get_OS_version(); + os.text = utils::get_OS_version_string(); // Write Qt version logs::stored_message qt{(strcmp(QT_VERSION_STR, qVersion()) != 0) ? sys_log.error : sys_log.notice}; qt.text = fmt::format("Qt version: Compiled against Qt %s | Run-time uses Qt %s", QT_VERSION_STR, qVersion()); + // Write current time logs::stored_message time{sys_log.always()}; time.text = fmt::format("Current Time: %s", std::chrono::system_clock::now()); diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index adb7e376be..3e703c9af0 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -439,6 +439,9 @@ true + + true + true @@ -712,6 +715,9 @@ true + + true + true @@ -827,6 +833,7 @@ + @@ -1819,6 +1826,16 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing %(Identity)... diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 4cdbcedc2e..199495aaba 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -193,6 +193,9 @@ {f6b701aa-7f4a-4816-b05f-80d24cb70e13} + + {9b51636c-b371-425b-86d3-be003774a1b7} + @@ -471,6 +474,9 @@ Gui\trophy + + Gui\savestates + Gui\misc dialogs @@ -549,6 +555,12 @@ Generated Files\Release + + Generated Files\Debug + + + Generated Files\Release + Gui\misc dialogs @@ -1480,6 +1492,9 @@ Gui\trophy + + Gui\savestates + Gui\misc dialogs diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index e3e3c0a264..140b39644d 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -80,6 +80,7 @@ add_library(rpcs3_ui STATIC save_data_info_dialog.cpp save_data_list_dialog.cpp save_manager_dialog.cpp + savestate_manager_dialog.cpp screenshot_item.cpp screenshot_manager_dialog.cpp screenshot_preview.cpp diff --git a/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h b/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h index c432143185..3de5029978 100644 --- a/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h +++ b/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h @@ -1,7 +1,5 @@ #pragma once -#include "Emu/Io/MouseHandler.h" - #include #include #include diff --git a/rpcs3/rpcs3qt/cg_disasm_window.h b/rpcs3/rpcs3qt/cg_disasm_window.h index f1df84e507..dc0a963c84 100644 --- a/rpcs3/rpcs3qt/cg_disasm_window.h +++ b/rpcs3/rpcs3qt/cg_disasm_window.h @@ -1,7 +1,5 @@ #pragma once -#include "util/types.hpp" - #include #include diff --git a/rpcs3/rpcs3qt/cheat_manager.cpp b/rpcs3/rpcs3qt/cheat_manager.cpp index d5af8a1a2f..cd7fe67804 100644 --- a/rpcs3/rpcs3qt/cheat_manager.cpp +++ b/rpcs3/rpcs3qt/cheat_manager.cpp @@ -14,11 +14,12 @@ #include "Emu/IdManager.h" #include "Emu/Cell/PPUAnalyser.h" -#include "Emu/Cell/PPUFunction.h" +#include "Emu/Cell/PPUInterpreter.h" #include "Emu/Cell/lv2/sys_sync.h" #include "util/yaml.hpp" #include "util/asm.hpp" +#include "util/logs.hpp" #include "util/to_endian.hpp" #include "Utilities/File.h" #include "Utilities/StrUtil.h" diff --git a/rpcs3/rpcs3qt/curl_handle.cpp b/rpcs3/rpcs3qt/curl_handle.cpp index ff40004dec..cb91311d26 100644 --- a/rpcs3/rpcs3qt/curl_handle.cpp +++ b/rpcs3/rpcs3qt/curl_handle.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "curl_handle.h" -#include "Emu/system_utils.hpp" #include "util/logs.hpp" #ifdef _WIN32 diff --git a/rpcs3/rpcs3qt/debugger_list.cpp b/rpcs3/rpcs3qt/debugger_list.cpp index 9123df3907..d1e0f97677 100644 --- a/rpcs3/rpcs3qt/debugger_list.cpp +++ b/rpcs3/rpcs3qt/debugger_list.cpp @@ -4,13 +4,14 @@ #include "breakpoint_handler.h" #include "Emu/Cell/SPUThread.h" -#include "Emu/Cell/PPUThread.h" #include "Emu/CPU/CPUDisAsm.h" #include "Emu/CPU/CPUThread.h" #include "Emu/RSX/RSXDisAsm.h" #include "Emu/RSX/RSXThread.h" #include "Emu/System.h" +#include "util/asm.hpp" + #include #include #include diff --git a/rpcs3/rpcs3qt/dimensions_dialog.cpp b/rpcs3/rpcs3qt/dimensions_dialog.cpp index 596d94ae25..378f887f22 100644 --- a/rpcs3/rpcs3qt/dimensions_dialog.cpp +++ b/rpcs3/rpcs3qt/dimensions_dialog.cpp @@ -3,10 +3,6 @@ #include "dimensions_dialog.h" #include "Emu/Io/Dimensions.h" -#include "util/asm.hpp" - -#include - #include #include #include diff --git a/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp b/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp index b96699197a..c191b878de 100644 --- a/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp +++ b/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp @@ -1,6 +1,4 @@ #include "elf_memory_dumping_dialog.h" -#include "Utilities/Config.h" - #include "Emu/Cell/SPUThread.h" #include "qt_utils.h" diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index eb6439243a..a8a11d3985 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -8,7 +8,6 @@ #include "Emu/System.h" #include "Emu/system_config.h" -#include "Emu/vfs_config.h" #include "Emu/system_utils.hpp" #include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/Io/Keyboard.h" diff --git a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.h b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.h index ad1b863d7e..490dee3ae7 100644 --- a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.h @@ -1,7 +1,5 @@ #pragma once -#include "Emu/Io/pad_types.h" - #include #include #include diff --git a/rpcs3/rpcs3qt/game_list.h b/rpcs3/rpcs3qt/game_list.h index 1a5ce6b921..3007ee62d7 100644 --- a/rpcs3/rpcs3qt/game_list.h +++ b/rpcs3/rpcs3qt/game_list.h @@ -7,7 +7,6 @@ #include #include "game_list_base.h" -#include "util/atomic.hpp" #include diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 1699837ae3..2df8852718 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -2,28 +2,23 @@ #include "qt_utils.h" #include "settings_dialog.h" #include "pad_settings_dialog.h" -#include "custom_table_widget_item.h" #include "input_dialog.h" #include "localized.h" #include "progress_dialog.h" #include "persistent_settings.h" #include "emu_settings.h" #include "gui_settings.h" -#include "game_list_delegate.h" #include "game_list_table.h" #include "game_list_grid.h" #include "game_list_grid_item.h" #include "patch_manager_dialog.h" -#include "Emu/Memory/vm.h" #include "Emu/System.h" #include "Emu/vfs_config.h" #include "Emu/system_utils.hpp" #include "Loader/PSF.h" #include "util/types.hpp" #include "Utilities/File.h" -#include "Utilities/mutex.h" -#include "util/yaml.hpp" #include "util/sysinfo.hpp" #include "Input/pad_thread.h" @@ -1197,9 +1192,9 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) }); } - extern bool is_savestate_compatible(fs::file&& file, std::string_view filepath); + extern bool is_savestate_compatible(const std::string& filepath); - if (const std::string sstate = get_savestate_file(current_game.serial, current_game.path, 0, 0); is_savestate_compatible(fs::file(sstate), sstate)) + if (const std::string sstate = get_savestate_file(current_game.serial, current_game.path, 0, 0); is_savestate_compatible(sstate)) { QAction* boot_state = menu.addAction(is_current_running_game ? tr("&Reboot with savestate") diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 60c4e0daee..bbef37e377 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -2,7 +2,6 @@ #include "game_list.h" #include "custom_dock_widget.h" -#include "gui_save.h" #include "shortcut_utils.h" #include "Utilities/lockless.h" #include "Utilities/mutex.h" diff --git a/rpcs3/rpcs3qt/game_list_grid.cpp b/rpcs3/rpcs3qt/game_list_grid.cpp index fd8301a297..5b6e4c3410 100644 --- a/rpcs3/rpcs3qt/game_list_grid.cpp +++ b/rpcs3/rpcs3qt/game_list_grid.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "game_list_grid.h" #include "game_list_grid_item.h" -#include "movie_item.h" #include "gui_settings.h" #include "qt_utils.h" #include "Utilities/File.h" diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 5a9da7dad6..b24ce33fc3 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -12,7 +12,6 @@ #include "Emu/IdManager.h" #include "Emu/Audio/audio_utils.h" #include "Emu/Cell/Modules/cellScreenshot.h" -#include "Emu/Cell/Modules/cellVideoOut.h" #include "Emu/Cell/Modules/cellAudio.h" #include "Emu/Cell/lv2/sys_rsxaudio.h" #include "Emu/RSX/rsx_utils.h" @@ -243,6 +242,11 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey { gui_log.notice("Game window registered shortcut: %s (%s)", shortcut_key, key_sequence.toString()); + if (m_disable_kb_hotkeys) + { + return; + } + switch (shortcut_key) { case gui::shortcuts::shortcut::gw_toggle_fullscreen: @@ -252,7 +256,7 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey } case gui::shortcuts::shortcut::gw_exit_fullscreen: { - if (visibility() == FullScreen && !m_disable_kb_hotkeys) + if (visibility() == FullScreen) { toggle_fullscreen(); } @@ -281,70 +285,57 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey } case gui::shortcuts::shortcut::gw_pause_play: { - if (!m_disable_kb_hotkeys) + switch (Emu.GetStatus()) { - switch (Emu.GetStatus()) - { - case system_state::ready: - { - Emu.Run(true); - return; - } - case system_state::paused: - { - Emu.Resume(); - return; - } - default: - { - Emu.Pause(); - return; - } - } + case system_state::ready: + { + Emu.Run(true); + return; + } + case system_state::paused: + { + Emu.Resume(); + return; + } + default: + { + Emu.Pause(); + return; + } } break; } case gui::shortcuts::shortcut::gw_restart: { - if (!m_disable_kb_hotkeys) + if (Emu.IsStopped()) { - if (Emu.IsStopped()) - { - Emu.Restart(); - return; - } - - extern bool boot_last_savestate(bool testing); - boot_last_savestate(false); + Emu.Restart(); + return; } + + extern bool boot_last_savestate(bool testing); + boot_last_savestate(false); break; } case gui::shortcuts::shortcut::gw_savestate: { - if (!m_disable_kb_hotkeys) + if (!g_cfg.savestate.suspend_emu) { - if (!g_cfg.savestate.suspend_emu) + Emu.after_kill_callback = []() { - Emu.after_kill_callback = []() - { - Emu.Restart(); - }; + Emu.Restart(); + }; - // Make sure we keep the game window opened - Emu.SetContinuousMode(true); - } - - Emu.Kill(false, true); - return; + // Make sure we keep the game window opened + Emu.SetContinuousMode(true); } - break; + + Emu.Kill(false, true); + return; } case gui::shortcuts::shortcut::gw_rsx_capture: { - if (!m_disable_kb_hotkeys) - { - g_user_asked_for_frame_capture = true; - } + g_user_asked_for_frame_capture = true; break; } case gui::shortcuts::shortcut::gw_frame_limit: diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 9f5dc53cb7..b0a5d53812 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -54,7 +54,10 @@ #endif #ifdef _WIN32 -#include "Windows.h" +#include +#include + +#include "Emu/Cell/lv2/sys_usbd.h" #endif LOG_CHANNEL(gui_log, "GUI"); @@ -73,6 +76,9 @@ gui_application::~gui_application() #ifdef WITH_DISCORD_RPC discord::shutdown(); #endif +#ifdef _WIN32 + unregister_device_notification(); +#endif } bool gui_application::Init() @@ -196,6 +202,11 @@ bool gui_application::Init() // Install native event filter #ifdef _WIN32 // Currently only needed for raw mouse input on windows installNativeEventFilter(&m_native_event_filter); + + if (m_main_window) + { + register_device_notification(m_main_window->winId()); + } #endif return true; @@ -470,11 +481,26 @@ std::unique_ptr gui_application::get_gs_frame() } m_game_window = frame; + ensure(m_game_window); + +#ifdef _WIN32 + if (!m_show_gui) + { + register_device_notification(m_game_window->winId()); + } +#endif connect(m_game_window, &gs_frame::destroyed, this, [this]() { gui_log.notice("gui_application: Deleting old game window"); m_game_window = nullptr; + +#ifdef _WIN32 + if (!m_show_gui) + { + unregister_device_notification(); + } +#endif }); return std::unique_ptr(frame); @@ -1180,15 +1206,24 @@ void gui_application::OnAppStateChanged(Qt::ApplicationState state) bool gui_application::native_event_filter::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, [[maybe_unused]] void* message, [[maybe_unused]] qintptr* result) { #ifdef _WIN32 - if (!Emu.IsRunning() && !g_raw_mouse_handler) + if (!Emu.IsRunning() && !Emu.IsStarting() && !g_raw_mouse_handler) { return false; } if (eventType == "windows_generic_MSG") { - if (MSG* msg = static_cast(message); msg && (msg->message == WM_INPUT || msg->message == WM_KEYDOWN || msg->message == WM_KEYUP)) + if (MSG* msg = static_cast(message); msg && (msg->message == WM_INPUT || msg->message == WM_KEYDOWN || msg->message == WM_KEYUP || msg->message == WM_DEVICECHANGE)) { + if (msg->message == WM_DEVICECHANGE && (msg->wParam == DBT_DEVICEARRIVAL || msg->wParam == DBT_DEVICEREMOVECOMPLETE)) + { + if (Emu.IsRunning() || Emu.IsStarting()) + { + handle_hotplug_event(msg->wParam == DBT_DEVICEARRIVAL); + } + return false; + } + if (auto* handler = g_fxo->try_get(); handler && handler->type == mouse_handler::raw) { static_cast(handler)->handle_native_event(*msg); @@ -1204,3 +1239,40 @@ bool gui_application::native_event_filter::nativeEventFilter([[maybe_unused]] co return false; } + +#ifdef _WIN32 +void gui_application::register_device_notification(WId window_id) +{ + if (m_device_notification_handle) return; + + gui_log.notice("Registering device notifications..."); + + // Enable usb device hotplug events + // Currently only needed for hotplug on windows, as libusb handles other platforms + DEV_BROADCAST_DEVICEINTERFACE notification_filter {}; + notification_filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); + notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + notification_filter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; + + m_device_notification_handle = RegisterDeviceNotification(reinterpret_cast(window_id), ¬ification_filter, DEVICE_NOTIFY_WINDOW_HANDLE); + if (!m_device_notification_handle ) + { + gui_log.error("RegisterDeviceNotification() failed: %s", fmt::win_error{GetLastError(), nullptr}); + } +} + +void gui_application::unregister_device_notification() +{ + if (m_device_notification_handle) + { + gui_log.notice("Unregistering device notifications..."); + + if (!UnregisterDeviceNotification(m_device_notification_handle)) + { + gui_log.error("UnregisterDeviceNotification() failed: %s", fmt::win_error{GetLastError(), nullptr}); + } + + m_device_notification_handle = {}; + } +} +#endif diff --git a/rpcs3/rpcs3qt/gui_application.h b/rpcs3/rpcs3qt/gui_application.h index aa43425b31..a09e7bb895 100644 --- a/rpcs3/rpcs3qt/gui_application.h +++ b/rpcs3/rpcs3qt/gui_application.h @@ -19,6 +19,10 @@ #include #include +#ifdef _WIN32 +#include "Windows.h" +#endif + class gs_frame; class main_window; class gui_settings; @@ -118,6 +122,12 @@ private: typename Emulator::stop_counter_t m_emu_focus_out_emulation_id{}; bool m_is_pause_on_focus_loss_active = false; +#ifdef _WIN32 + void register_device_notification(WId window_id); + void unregister_device_notification(); + HDEVNOTIFY m_device_notification_handle {}; +#endif + private Q_SLOTS: void OnChangeStyleSheetRequest(); void OnShortcutChange(); diff --git a/rpcs3/rpcs3qt/gui_settings.cpp b/rpcs3/rpcs3qt/gui_settings.cpp index a79f9d9db7..8f42a14dd7 100644 --- a/rpcs3/rpcs3qt/gui_settings.cpp +++ b/rpcs3/rpcs3qt/gui_settings.cpp @@ -1,7 +1,7 @@ #include "gui_settings.h" #include "qt_utils.h" -#include "localized.h" +#include "category.h" #include "Emu/System.h" @@ -9,8 +9,6 @@ #include #include -#include - LOG_CHANNEL(cfg_log, "CFG"); extern void qt_events_aware_op(int repeat_duration_ms, std::function wrapped_op); @@ -20,42 +18,53 @@ namespace gui QString stylesheet; bool custom_stylesheet_active = false; + QString get_savestate_list_column_name(savestate_list_columns col) + { + switch (col) + { + case gui::savestate_list_columns::name: return "savestate_column_name"; + case gui::savestate_list_columns::compatible: return "savestate_column_compatible"; + case gui::savestate_list_columns::date: return "savestate_column_date"; + case gui::savestate_list_columns::path: return "savestate_column_path"; + case gui::savestate_list_columns::count: return ""; + } + + fmt::throw_exception("get_savestate_list_column_name: Invalid column"); + } + + QString get_savestate_game_list_column_name(savestate_game_list_columns col) + { + switch (col) + { + case gui::savestate_game_list_columns::icon: return "savestate_game_column_icon"; + case gui::savestate_game_list_columns::name: return "savestate_game_column_name"; + case gui::savestate_game_list_columns::savestates: return "savestate_game_column_savestates"; + case gui::savestate_game_list_columns::count: return ""; + } + + fmt::throw_exception("get_savestate_game_list_column_name: Invalid column"); + } + QString get_game_list_column_name(game_list_columns col) { switch (col) { - case game_list_columns::icon: - return "column_icon"; - case game_list_columns::name: - return "column_name"; - case game_list_columns::serial: - return "column_serial"; - case game_list_columns::firmware: - return "column_firmware"; - case game_list_columns::version: - return "column_version"; - case game_list_columns::category: - return "column_category"; - case game_list_columns::path: - return "column_path"; - case game_list_columns::move: - return "column_move"; - case game_list_columns::resolution: - return "column_resolution"; - case game_list_columns::sound: - return "column_sound"; - case game_list_columns::parental: - return "column_parental"; - case game_list_columns::last_play: - return "column_last_play"; - case game_list_columns::playtime: - return "column_playtime"; - case game_list_columns::compat: - return "column_compat"; - case game_list_columns::dir_size: - return "column_dir_size"; - case game_list_columns::count: - return ""; + case game_list_columns::icon: return "column_icon"; + case game_list_columns::name: return "column_name"; + case game_list_columns::serial: return "column_serial"; + case game_list_columns::firmware: return "column_firmware"; + case game_list_columns::version: return "column_version"; + case game_list_columns::category: return "column_category"; + case game_list_columns::path: return "column_path"; + case game_list_columns::move: return "column_move"; + case game_list_columns::resolution: return "column_resolution"; + case game_list_columns::sound: return "column_sound"; + case game_list_columns::parental: return "column_parental"; + case game_list_columns::last_play: return "column_last_play"; + case game_list_columns::playtime: return "column_playtime"; + case game_list_columns::compat: return "column_compat"; + case game_list_columns::dir_size: return "column_dir_size"; + case game_list_columns::count: return ""; } fmt::throw_exception("get_game_list_column_name: Invalid column"); @@ -65,24 +74,15 @@ namespace gui { switch (col) { - case trophy_list_columns::icon: - return "trophy_column_icon"; - case trophy_list_columns::name: - return "trophy_column_name"; - case trophy_list_columns::description: - return "trophy_column_description"; - case trophy_list_columns::type: - return "trophy_column_type"; - case trophy_list_columns::is_unlocked: - return "trophy_column_is_unlocked"; - case trophy_list_columns::id: - return "trophy_column_id"; - case trophy_list_columns::platinum_link: - return "trophy_column_platinum_link"; - case trophy_list_columns::time_unlocked: - return "trophy_column_time_unlocked"; - case trophy_list_columns::count: - return ""; + case trophy_list_columns::icon: return "trophy_column_icon"; + case trophy_list_columns::name: return "trophy_column_name"; + case trophy_list_columns::description: return "trophy_column_description"; + case trophy_list_columns::type: return "trophy_column_type"; + case trophy_list_columns::is_unlocked: return "trophy_column_is_unlocked"; + case trophy_list_columns::id: return "trophy_column_id"; + case trophy_list_columns::platinum_link: return "trophy_column_platinum_link"; + case trophy_list_columns::time_unlocked: return "trophy_column_time_unlocked"; + case trophy_list_columns::count: return ""; } fmt::throw_exception("get_trophy_list_column_name: Invalid column"); @@ -92,16 +92,11 @@ namespace gui { switch (col) { - case trophy_game_list_columns::icon: - return "trophy_game_column_icon"; - case trophy_game_list_columns::name: - return "trophy_game_column_name"; - case trophy_game_list_columns::progress: - return "trophy_game_column_progress"; - case trophy_game_list_columns::trophies: - return "trophy_game_column_trophies"; - case trophy_game_list_columns::count: - return ""; + case trophy_game_list_columns::icon: return "trophy_game_column_icon"; + case trophy_game_list_columns::name: return "trophy_game_column_name"; + case trophy_game_list_columns::progress: return "trophy_game_column_progress"; + case trophy_game_list_columns::trophies: return "trophy_game_column_trophies"; + case trophy_game_list_columns::count: return ""; } fmt::throw_exception("get_trophy_game_list_column_name: Invalid column"); @@ -115,21 +110,21 @@ gui_settings::gui_settings(QObject* parent) : settings(parent) QStringList gui_settings::GetGameListCategoryFilters(bool is_list_mode) const { - QStringList filterList; + QStringList filters; - if (GetCategoryVisibility(Category::HDD_Game, is_list_mode)) filterList.append(cat::cat_hdd_game); - if (GetCategoryVisibility(Category::Disc_Game, is_list_mode)) filterList.append(cat::cat_disc_game); - if (GetCategoryVisibility(Category::PS1_Game, is_list_mode)) filterList.append(cat::cat_ps1_game); - if (GetCategoryVisibility(Category::PS2_Game, is_list_mode)) filterList.append(cat::ps2_games); - if (GetCategoryVisibility(Category::PSP_Game, is_list_mode)) filterList.append(cat::psp_games); - if (GetCategoryVisibility(Category::Home, is_list_mode)) filterList.append(cat::cat_home); - if (GetCategoryVisibility(Category::Media, is_list_mode)) filterList.append(cat::media); - if (GetCategoryVisibility(Category::Data, is_list_mode)) filterList.append(cat::data); - if (GetCategoryVisibility(Category::OS, is_list_mode)) filterList.append(cat::os); - if (GetCategoryVisibility(Category::Unknown_Cat, is_list_mode)) filterList.append(cat::cat_unknown); - if (GetCategoryVisibility(Category::Others, is_list_mode)) filterList.append(cat::others); + if (GetCategoryVisibility(Category::HDD_Game, is_list_mode)) filters.append(cat::cat_hdd_game); + if (GetCategoryVisibility(Category::Disc_Game, is_list_mode)) filters.append(cat::cat_disc_game); + if (GetCategoryVisibility(Category::PS1_Game, is_list_mode)) filters.append(cat::cat_ps1_game); + if (GetCategoryVisibility(Category::PS2_Game, is_list_mode)) filters.append(cat::ps2_games); + if (GetCategoryVisibility(Category::PSP_Game, is_list_mode)) filters.append(cat::psp_games); + if (GetCategoryVisibility(Category::Home, is_list_mode)) filters.append(cat::cat_home); + if (GetCategoryVisibility(Category::Media, is_list_mode)) filters.append(cat::media); + if (GetCategoryVisibility(Category::Data, is_list_mode)) filters.append(cat::data); + if (GetCategoryVisibility(Category::OS, is_list_mode)) filters.append(cat::os); + if (GetCategoryVisibility(Category::Unknown_Cat, is_list_mode)) filters.append(cat::cat_unknown); + if (GetCategoryVisibility(Category::Others, is_list_mode)) filters.append(cat::others); - return filterList; + return filters; } bool gui_settings::GetCategoryVisibility(int cat, bool is_list_mode) const @@ -243,6 +238,16 @@ bool gui_settings::GetBootConfirmation(QWidget* parent, const gui_save& gui_save return true; } +void gui_settings::SetSavestateGamelistColVisibility(gui::savestate_game_list_columns col, bool val) const +{ + SetValue(GetGuiSaveForSavestateGameColumn(col), val); +} + +void gui_settings::SetSavestateListColVisibility(gui::savestate_list_columns col, bool val) const +{ + SetValue(GetGuiSaveForSavestateColumn(col), val); +} + void gui_settings::SetTrophyGamelistColVisibility(gui::trophy_game_list_columns col, bool val) const { SetValue(GetGuiSaveForTrophyGameColumn(col), val); @@ -268,6 +273,16 @@ logs::level gui_settings::GetLogLevel() const return logs::level(GetValue(gui::l_level).toUInt()); } +bool gui_settings::GetSavestateGamelistColVisibility(gui::savestate_game_list_columns col) const +{ + return GetValue(GetGuiSaveForSavestateGameColumn(col)).toBool(); +} + +bool gui_settings::GetSavestateListColVisibility(gui::savestate_list_columns col) const +{ + return GetValue(GetGuiSaveForSavestateColumn(col)).toBool(); +} + bool gui_settings::GetTrophyGamelistColVisibility(gui::trophy_game_list_columns col) const { return GetValue(GetGuiSaveForTrophyGameColumn(col)).toBool(); @@ -316,6 +331,16 @@ QSize gui_settings::SizeFromSlider(int pos) return gui::gl_icon_size_min + (gui::gl_icon_size_max - gui::gl_icon_size_min) * (1.f * pos / gui::gl_max_slider_pos); } +gui_save gui_settings::GetGuiSaveForSavestateGameColumn(gui::savestate_game_list_columns col) +{ + return gui_save{ gui::savestate, "visibility_" + gui::get_savestate_game_list_column_name(col), true }; +} + +gui_save gui_settings::GetGuiSaveForSavestateColumn(gui::savestate_list_columns col) +{ + return gui_save{ gui::savestate, "visibility_" + gui::get_savestate_list_column_name(col), true }; +} + gui_save gui_settings::GetGuiSaveForTrophyGameColumn(gui::trophy_game_list_columns col) { return gui_save{ gui::trophy, "visibility_" + gui::get_trophy_game_list_column_name(col), true }; diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index 82c6770d00..b3fab563cf 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -64,6 +64,27 @@ namespace gui count }; + enum class savestate_game_list_columns + { + icon = 0, + name = 1, + savestates = 2, + + count + }; + + enum class savestate_list_columns + { + name = 0, + compatible = 1, + date = 2, + path = 3, + + count + }; + + QString get_savestate_game_list_column_name(savestate_game_list_columns col); + QString get_savestate_list_column_name(savestate_list_columns col); QString get_trophy_game_list_column_name(trophy_game_list_columns col); QString get_trophy_list_column_name(trophy_list_columns col); QString get_game_list_column_name(game_list_columns col); @@ -109,6 +130,7 @@ namespace gui const QString log_viewer = "LogViewer"; const QString sc = "Shortcuts"; const QString navigation = "PadNavigation"; + const QString savestate = "Savestate"; const QString update_on = "true"; const QString update_off = "false"; @@ -120,6 +142,9 @@ namespace gui const gui_save rg_freeze = gui_save(main_window, "recentGamesFrozen", false); const gui_save rg_entries = gui_save(main_window, "recentGamesNames", QVariant::fromValue(q_pair_list())); + const gui_save rs_freeze = gui_save(main_window, "recentSavestatesFrozen", false); + const gui_save rs_entries = gui_save(main_window, "recentSavestatesNames", QVariant::fromValue(q_pair_list())); + const gui_save ib_skip_version = gui_save(main_window, "infoBoxSkipVersion", ""); const gui_save ib_pkg_success = gui_save(main_window, "infoBoxEnabledInstallPKG", true); const gui_save ib_pup_success = gui_save(main_window, "infoBoxEnabledInstallPUP", true); @@ -243,6 +268,13 @@ namespace gui const gui_save gs_geometry = gui_save(gs_frame, "geometry", QRect()); const gui_save gs_visibility = gui_save(gs_frame, "visibility", QWindow::Visibility::AutomaticVisibility); + const gui_save ss_icon_color = gui_save(trophy, "icon_color", gl_icon_color); + const gui_save ss_game_icon_size = gui_save(trophy, "game_icon_size", 25); + const gui_save ss_geometry = gui_save(trophy, "geometry", QByteArray()); + const gui_save ss_splitterState = gui_save(trophy, "splitterState", QByteArray()); + const gui_save ss_games_state = gui_save(trophy, "games_state", QByteArray()); + const gui_save ss_savestate_state = gui_save(trophy, "savestate_state", QByteArray()); + const gui_save tr_icon_color = gui_save(trophy, "icon_color", gl_icon_color); const gui_save tr_icon_height = gui_save(trophy, "icon_height", 75); const gui_save tr_game_iconSize = gui_save(trophy, "game_iconSize", 25); @@ -301,6 +333,8 @@ public: bool GetBootConfirmation(QWidget* parent, const gui_save& gui_save_entry = gui_save()); logs::level GetLogLevel() const; + bool GetSavestateGamelistColVisibility(gui::savestate_game_list_columns col) const; + bool GetSavestateListColVisibility(gui::savestate_list_columns col) const; bool GetTrophyGamelistColVisibility(gui::trophy_game_list_columns col) const; bool GetTrophylistColVisibility(gui::trophy_list_columns col) const; bool GetGamelistColVisibility(gui::game_list_columns col) const; @@ -313,6 +347,8 @@ public: /** Sets the visibility of the chosen category. */ void SetCategoryVisibility(int cat, bool val, bool is_list_mode) const; + void SetSavestateGamelistColVisibility(gui::savestate_game_list_columns col, bool val) const; + void SetSavestateListColVisibility(gui::savestate_list_columns col, bool val) const; void SetTrophyGamelistColVisibility(gui::trophy_game_list_columns col, bool val) const; void SetTrophylistColVisibility(gui::trophy_list_columns col, bool val) const; void SetGamelistColVisibility(gui::game_list_columns col, bool val) const; @@ -320,6 +356,8 @@ public: void SetCustomColor(int col, const QColor& val) const; private: + static gui_save GetGuiSaveForSavestateGameColumn(gui::savestate_game_list_columns col); + static gui_save GetGuiSaveForSavestateColumn(gui::savestate_list_columns col); static gui_save GetGuiSaveForTrophyGameColumn(gui::trophy_game_list_columns col); static gui_save GetGuiSaveForTrophyColumn(gui::trophy_list_columns col); static gui_save GetGuiSaveForGameColumn(gui::game_list_columns col); diff --git a/rpcs3/rpcs3qt/infinity_dialog.cpp b/rpcs3/rpcs3qt/infinity_dialog.cpp index 18d1cfe241..b172ddad22 100644 --- a/rpcs3/rpcs3qt/infinity_dialog.cpp +++ b/rpcs3/rpcs3qt/infinity_dialog.cpp @@ -1,15 +1,10 @@ #include "stdafx.h" #include "Utilities/File.h" -#include "Crypto/md5.h" #include "Crypto/aes.h" #include "Crypto/sha1.h" #include "infinity_dialog.h" #include "Emu/Io/Infinity.h" -#include "util/asm.hpp" - -#include - #include #include #include diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index e30fdff643..3b50f9ea97 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -7,6 +7,7 @@ #include #include "Emu/IdManager.h" +#include "Emu/System.h" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/SPUThread.h" #include "Emu/Cell/lv2/sys_lwmutex.h" diff --git a/rpcs3/rpcs3qt/log_frame.cpp b/rpcs3/rpcs3qt/log_frame.cpp index a318a9c03d..d01bd1bf4f 100644 --- a/rpcs3/rpcs3qt/log_frame.cpp +++ b/rpcs3/rpcs3qt/log_frame.cpp @@ -2,8 +2,6 @@ #include "qt_utils.h" #include "gui_settings.h" -#include "rpcs3_version.h" -#include "Utilities/mutex.h" #include "Utilities/lockless.h" #include "util/asm.hpp" @@ -14,7 +12,6 @@ #include #include -#include #include #include diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index e23d9b32bc..1f7eceba0a 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -3,6 +3,7 @@ #include "vfs_dialog.h" #include "save_manager_dialog.h" #include "trophy_manager_dialog.h" +#include "savestate_manager_dialog.h" #include "user_manager_dialog.h" #include "screenshot_manager_dialog.h" #include "kernel_explorer.h" @@ -38,12 +39,10 @@ #include "system_cmd_dialog.h" #include "emulated_pad_settings_dialog.h" #include "basic_mouse_settings_dialog.h" -#include "raw_mouse_settings_dialog.h" #include "vfs_tool_dialog.h" #include "welcome_dialog.h" #include -#include #include #include @@ -63,6 +62,8 @@ #include "Emu/System.h" #include "Emu/system_utils.hpp" #include "Emu/system_config.h" +#include "Emu/savestate_utils.hpp" +#include "Emu/Cell/timers.hpp" #include "Crypto/unpkg.h" #include "Crypto/unself.h" @@ -90,6 +91,10 @@ #include #endif +#ifdef _WIN32 +#include "raw_mouse_settings_dialog.h" +#endif + LOG_CHANNEL(gui_log, "GUI"); extern atomic_t g_user_asked_for_frame_capture; @@ -234,9 +239,9 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot) show(); // needs to be done before creating the thumbnail toolbar // enable play options if a recent game exists - const bool enable_play_last = !m_recent_game_acts.isEmpty() && m_recent_game_acts.first(); + const bool enable_play_last = !m_recent_game.actions.isEmpty() && m_recent_game.actions.first(); - const QString start_tooltip = enable_play_last ? tr("Play %0").arg(m_recent_game_acts.first()->text()) : tr("Play"); + const QString start_tooltip = enable_play_last ? tr("Play %0").arg(m_recent_game.actions.first()->text()) : tr("Play"); if (enable_play_last) { @@ -510,9 +515,9 @@ void main_window::OnPlayOrPause() show_boot_error(error); } } - else if (!m_recent_game_acts.isEmpty()) + else if (!m_recent_game.actions.isEmpty()) { - BootRecentAction(m_recent_game_acts.first()); + BootRecentAction(m_recent_game.actions.first(), false); } return; @@ -602,17 +607,23 @@ void main_window::Boot(const std::string& path, const std::string& title_id, boo { gui_log.error("Boot failed: reason: %s, path: %s", error, path); show_boot_error(error); + return; + } + + if (is_savestate_compatible(path)) + { + gui_log.success("Boot of savestate successful."); + AddRecentAction(gui::Recent_Game(QString::fromStdString(path), QString::fromStdString(Emu.GetTitleAndTitleID())), true); } else { gui_log.success("Boot successful."); + AddRecentAction(gui::Recent_Game(QString::fromStdString(Emu.GetBoot()), QString::fromStdString(Emu.GetTitleAndTitleID())), false); + } - AddRecentAction(gui::Recent_Game(qstr(Emu.GetBoot()), qstr(Emu.GetTitleAndTitleID()))); - - if (refresh_list) - { - m_game_list_frame->Refresh(true); - } + if (refresh_list) + { + m_game_list_frame->Refresh(true); } } @@ -2190,55 +2201,59 @@ void main_window::OnEnableDiscInsert(bool enabled) const ui->insertDiscAct->setEnabled(enabled); } -void main_window::BootRecentAction(const QAction* act) +void main_window::BootRecentAction(const QAction* act, bool is_savestate) { if (Emu.IsRunning()) { return; } + recent_game_wrapper& rgw = is_savestate ? m_recent_save : m_recent_game; + QMenu* menu = is_savestate ? ui->bootRecentSavestatesMenu : ui->bootRecentMenu; + const QString pth = act->data().toString(); - const std::string path = sstr(pth); + const std::string path = pth.toStdString(); QString name; bool contains_path = false; int idx = -1; - for (int i = 0; i < m_rg_entries.count(); i++) + for (int i = 0; i < rgw.entries.count(); i++) { - if (::at32(m_rg_entries, i).first == pth) + const auto& entry = rgw.entries[i]; + if (entry.first == pth) { idx = i; contains_path = true; - name = ::at32(m_rg_entries, idx).second; + name = entry.second; break; } } // path is invalid: remove action from list return - if ((contains_path && name.isEmpty()) || (!QFileInfo(pth).isDir() && !QFileInfo(pth).isFile())) + if ((contains_path && name.isEmpty()) || !fs::exists(path)) { if (contains_path) { // clear menu of actions - for (QAction* action : m_recent_game_acts) + for (QAction* action : rgw.actions) { - ui->bootRecentMenu->removeAction(action); + menu->removeAction(action); } // remove action from list - m_rg_entries.removeAt(idx); - m_recent_game_acts.removeAt(idx); + rgw.entries.removeAt(idx); + rgw.actions.removeAt(idx); - m_gui_settings->SetValue(gui::rg_entries, gui_settings::List2Var(m_rg_entries)); + m_gui_settings->SetValue(is_savestate ? gui::rs_entries : gui::rg_entries, gui_settings::List2Var(rgw.entries)); gui_log.error("Recent Game not valid, removed from Boot Recent list: %s", path); // refill menu with actions - for (int i = 0; i < m_recent_game_acts.count(); i++) + for (int i = 0; i < rgw.actions.count(); i++) { - m_recent_game_acts[i]->setShortcut(tr("Ctrl+%1").arg(i + 1)); - m_recent_game_acts[i]->setToolTip(::at32(m_rg_entries, i).second); - ui->bootRecentMenu->addAction(m_recent_game_acts[i]); + rgw.actions[i]->setShortcut(QString("%0+%1").arg(is_savestate ? "Alt" : "Ctrl").arg(i + 1)); + rgw.actions[i]->setToolTip(::at32(rgw.entries, i).second + "\n" + ::at32(rgw.entries, i).first); + menu->addAction(rgw.actions[i]); } gui_log.warning("Boot Recent list refreshed"); @@ -2253,19 +2268,21 @@ void main_window::BootRecentAction(const QAction* act) Boot(path, "", true); } -QAction* main_window::CreateRecentAction(const q_string_pair& entry, const uint& sc_idx) +QAction* main_window::CreateRecentAction(const q_string_pair& entry, u32 sc_idx, bool is_savestate) { + recent_game_wrapper& rgw = is_savestate ? m_recent_save : m_recent_game; + // if path is not valid remove from list if (entry.second.isEmpty() || (!QFileInfo(entry.first).isDir() && !QFileInfo(entry.first).isFile())) { - if (m_rg_entries.contains(entry)) + if (rgw.entries.contains(entry)) { gui_log.warning("Recent Game not valid, removing from Boot Recent list: %s", entry.first); - const int idx = m_rg_entries.indexOf(entry); - m_rg_entries.removeAt(idx); + const int idx = rgw.entries.indexOf(entry); + rgw.entries.removeAt(idx); - m_gui_settings->SetValue(gui::rg_entries, gui_settings::List2Var(m_rg_entries)); + m_gui_settings->SetValue(is_savestate ? gui::rs_entries : gui::rg_entries, gui_settings::List2Var(rgw.entries)); } return nullptr; } @@ -2280,8 +2297,8 @@ QAction* main_window::CreateRecentAction(const q_string_pair& entry, const uint& // create new action QAction* act = new QAction(shown_name, this); act->setData(entry.first); - act->setToolTip(entry.second); - act->setShortcut(tr("Ctrl+%1").arg(sc_idx)); + act->setToolTip(entry.second + "\n" + entry.first); + act->setShortcut(QString("%0+%1").arg(is_savestate ? "Alt" : "Ctrl").arg(sc_idx)); // truncate if too long if (shown_name.length() > 60) @@ -2290,69 +2307,74 @@ QAction* main_window::CreateRecentAction(const q_string_pair& entry, const uint& } // connect boot - connect(act, &QAction::triggered, this, [act, this]() {BootRecentAction(act); }); + connect(act, &QAction::triggered, this, [this, act, is_savestate](){ BootRecentAction(act, is_savestate); }); return act; } -void main_window::AddRecentAction(const q_string_pair& entry) +void main_window::AddRecentAction(const q_string_pair& entry, bool is_savestate) { + QAction* freezeAction = is_savestate ? ui->freezeRecentSavestatesAct : ui->freezeRecentAct; + // don't change list on freeze - if (ui->freezeRecentAct->isChecked()) + if (freezeAction->isChecked()) { return; } // create new action, return if not valid - QAction* act = CreateRecentAction(entry, 1); + QAction* act = CreateRecentAction(entry, 1, is_savestate); if (!act) { return; } + recent_game_wrapper& rgw = is_savestate ? m_recent_save : m_recent_game; + QMenu* menu = is_savestate ? ui->bootRecentSavestatesMenu : ui->bootRecentMenu; + // clear menu of actions - for (QAction* action : m_recent_game_acts) + for (QAction* action : rgw.actions) { - ui->bootRecentMenu->removeAction(action); + menu->removeAction(action); } // If path already exists, remove it in order to get it to beginning. Also remove duplicates. - for (int i = m_rg_entries.count() - 1; i >= 0; --i) + for (int i = rgw.entries.count() - 1; i >= 0; --i) { - if (m_rg_entries[i].first == entry.first) + if (rgw.entries[i].first == entry.first) { - m_rg_entries.removeAt(i); - m_recent_game_acts.removeAt(i); + rgw.entries.removeAt(i); + rgw.actions.removeAt(i); } } // remove oldest action at the end if needed - if (m_rg_entries.count() == 9) + if (rgw.entries.count() == 9) { - m_rg_entries.removeLast(); - m_recent_game_acts.removeLast(); + rgw.entries.removeLast(); + rgw.actions.removeLast(); } - else if (m_rg_entries.count() > 9) + else if (rgw.entries.count() > 9) { gui_log.error("Recent games entrylist too big"); } - if (m_rg_entries.count() < 9) + if (rgw.entries.count() < 9) { // add new action at the beginning - m_rg_entries.prepend(entry); - m_recent_game_acts.prepend(act); + rgw.entries.prepend(entry); + rgw.actions.prepend(act); } // refill menu with actions - for (int i = 0; i < m_recent_game_acts.count(); i++) + for (int i = 0; i < rgw.actions.count(); i++) { - m_recent_game_acts[i]->setShortcut(tr("Ctrl+%1").arg(i + 1)); - m_recent_game_acts[i]->setToolTip(::at32(m_rg_entries, i).second); - ui->bootRecentMenu->addAction(m_recent_game_acts[i]); + rgw.actions[i]->setShortcut(QString("%0+%1").arg(is_savestate ? "Alt" : "Ctrl").arg(i + 1)); + rgw.actions[i]->setToolTip(::at32(rgw.entries, i).second + "\n" + ::at32(rgw.entries, i).first); + menu->addAction(rgw.actions[i]); } - m_gui_settings->SetValue(gui::rg_entries, gui_settings::List2Var(m_rg_entries)); + m_gui_settings->SetValue(is_savestate ? gui::rs_entries : gui::rg_entries, gui_settings::List2Var(rgw.entries)); } void main_window::UpdateLanguageActions(const QStringList& language_codes, const QString& language_code) @@ -2638,26 +2660,59 @@ void main_window::CreateConnects() } }); + connect(ui->bootRecentSavestatesMenu, &QMenu::aboutToShow, this, [this]() + { + // Enable/Disable Recent Savestates List + const bool stopped = Emu.IsStopped(); + for (QAction* act : ui->bootRecentSavestatesMenu->actions()) + { + if (act != ui->freezeRecentSavestatesAct && act != ui->clearRecentSavestatesAct) + { + act->setEnabled(stopped); + } + } + }); + connect(ui->clearRecentAct, &QAction::triggered, this, [this]() { if (ui->freezeRecentAct->isChecked()) { return; } - m_rg_entries.clear(); - for (QAction* act : m_recent_game_acts) + m_recent_game.entries.clear(); + for (QAction* act : m_recent_game.actions) { ui->bootRecentMenu->removeAction(act); } - m_recent_game_acts.clear(); + m_recent_game.actions.clear(); m_gui_settings->SetValue(gui::rg_entries, gui_settings::List2Var(q_pair_list())); }); + connect(ui->clearRecentSavestatesAct, &QAction::triggered, this, [this]() + { + if (ui->freezeRecentSavestatesAct->isChecked()) + { + return; + } + m_recent_save.entries.clear(); + for (QAction* act : m_recent_save.actions) + { + ui->bootRecentSavestatesMenu->removeAction(act); + } + m_recent_save.actions.clear(); + m_gui_settings->SetValue(gui::rs_entries, gui_settings::List2Var(q_pair_list())); + }); + connect(ui->freezeRecentAct, &QAction::triggered, this, [this](bool checked) { m_gui_settings->SetValue(gui::rg_freeze, checked); }); + connect(ui->freezeRecentSavestatesAct, &QAction::triggered, this, [this](bool checked) + { + m_gui_settings->SetValue(gui::rs_freeze, checked); + }); + connect(ui->bootInstallPkgAct, &QAction::triggered, this, [this] {InstallPackages(); }); connect(ui->bootInstallPupAct, &QAction::triggered, this, [this] {InstallPup(); }); @@ -3009,6 +3064,14 @@ void main_window::CreateConnects() trop_manager->show(); }); + connect(ui->actionManage_Savestates, &QAction::triggered, this, [this] + { + savestate_manager_dialog* manager = new savestate_manager_dialog(m_gui_settings, m_game_list_frame->GetGameInfo()); + connect(this, &main_window::RequestDialogRepaint, manager, &savestate_manager_dialog::HandleRepaintUiRequest); + connect(manager, &savestate_manager_dialog::RequestBoot, this, [this](const std::string& path) { Boot(path, "", true); }); + manager->show(); + }); + connect(ui->actionManage_Skylanders_Portal, &QAction::triggered, this, [this] { skylander_dialog* sky_diag = skylander_dialog::get_dlg(this); @@ -3530,9 +3593,9 @@ void main_window::CreateDockWindows() ui->toolbar_start->setIcon(m_icon_restart); ui->toolbar_start->setText(tr("Restart")); } - else if (!m_recent_game_acts.isEmpty()) // Get last played game + else if (!m_recent_game.actions.isEmpty()) // Get last played game { - tooltip = tr("Play %0").arg(m_recent_game_acts.first()->text()); + tooltip = tr("Play %0").arg(m_recent_game.actions.first()->text()); } else { @@ -3584,35 +3647,45 @@ void main_window::ConfigureGuiFromSettings() m_mw->restoreState(m_gui_settings->GetValue(gui::mw_mwState).toByteArray()); ui->freezeRecentAct->setChecked(m_gui_settings->GetValue(gui::rg_freeze).toBool()); - m_rg_entries = gui_settings::Var2List(m_gui_settings->GetValue(gui::rg_entries)); + ui->freezeRecentSavestatesAct->setChecked(m_gui_settings->GetValue(gui::rs_freeze).toBool()); + m_recent_game.entries = gui_settings::Var2List(m_gui_settings->GetValue(gui::rg_entries)); + m_recent_save.entries = gui_settings::Var2List(m_gui_settings->GetValue(gui::rs_entries)); - // clear recent games menu of actions - for (QAction* act : m_recent_game_acts) + const auto update_recent_games_menu = [this](bool is_savestate) { - ui->bootRecentMenu->removeAction(act); - } - m_recent_game_acts.clear(); + recent_game_wrapper& rgw = is_savestate ? m_recent_save : m_recent_game; + QMenu* menu = is_savestate ? ui->bootRecentSavestatesMenu : ui->bootRecentMenu; - // Fill the recent games menu - for (int i = 0; i < m_rg_entries.count(); i++) - { - // adjust old unformatted entries (avoid duplication) - m_rg_entries[i] = gui::Recent_Game(m_rg_entries[i].first, m_rg_entries[i].second); - - // create new action - QAction* act = CreateRecentAction(m_rg_entries[i], i + 1); - - // add action to menu - if (act) + // clear recent games menu of actions + for (QAction* act : rgw.actions) { - m_recent_game_acts.append(act); - ui->bootRecentMenu->addAction(act); + menu->removeAction(act); } - else + rgw.actions.clear(); + + // Fill the recent games menu + for (int i = 0; i < rgw.entries.count(); i++) { - i--; // list count is now an entry shorter so we have to repeat the same index in order to load all other entries + // adjust old unformatted entries (avoid duplication) + rgw.entries[i] = gui::Recent_Game(rgw.entries[i].first, rgw.entries[i].second); + + // create new action + QAction* act = CreateRecentAction(rgw.entries[i], i + 1, is_savestate); + + // add action to menu + if (act) + { + rgw.actions.append(act); + menu->addAction(act); + } + else + { + i--; // list count is now an entry shorter so we have to repeat the same index in order to load all other entries + } } - } + }; + update_recent_games_menu(false); + update_recent_games_menu(true); ui->showLogAct->setChecked(m_gui_settings->GetValue(gui::mw_logger).toBool()); ui->showGameListAct->setChecked(m_gui_settings->GetValue(gui::mw_gamelist).toBool()); @@ -4147,16 +4220,27 @@ void main_window::dropEvent(QDropEvent* event) Emu.GracefulShutdown(false); - if (const auto error = Emu.BootGame(sstr(drop_paths.first()), "", true); error != game_boot_result::no_errors) + const std::string path = drop_paths.first().toStdString(); + + if (const auto error = Emu.BootGame(path, "", true); error != game_boot_result::no_errors) { - gui_log.error("Boot failed: reason: %s, path: %s", error, drop_paths.first()); + gui_log.error("Boot failed: reason: %s, path: %s", error, path); show_boot_error(error); + return; + } + + if (is_savestate_compatible(path)) + { + gui_log.success("Savestate Boot from drag and drop done: %s", path); + AddRecentAction(gui::Recent_Game(QString::fromStdString(path), QString::fromStdString(Emu.GetTitleAndTitleID())), true); } else { - gui_log.success("Elf Boot from drag and drop done: %s", drop_paths.first()); - m_game_list_frame->Refresh(true); + gui_log.success("Elf Boot from drag and drop done: %s", path); + AddRecentAction(gui::Recent_Game(QString::fromStdString(Emu.GetBoot()), QString::fromStdString(Emu.GetTitleAndTitleID())), false); } + + m_game_list_frame->Refresh(true); break; } case drop_type::drop_rrc: // replay a rsx capture file diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index a2a8f25678..daaf2848aa 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -175,17 +175,22 @@ private: drop_type IsValidFile(const QMimeData& md, QStringList* drop_paths = nullptr); void AddGamesFromDirs(QStringList&& paths); - QAction* CreateRecentAction(const q_string_pair& entry, const uint& sc_idx); - void BootRecentAction(const QAction* act); - void AddRecentAction(const q_string_pair& entry); + QAction* CreateRecentAction(const q_string_pair& entry, u32 sc_idx, bool is_savestate); + void BootRecentAction(const QAction* act, bool is_savestate); + void AddRecentAction(const q_string_pair& entry, bool is_savestate); void UpdateLanguageActions(const QStringList& language_codes, const QString& language); void UpdateFilterActions(); static QString GetCurrentTitle(); - q_pair_list m_rg_entries; - QList m_recent_game_acts; + struct recent_game_wrapper + { + q_pair_list entries; + QList actions; + }; + recent_game_wrapper m_recent_game {}; + recent_game_wrapper m_recent_save {}; std::shared_ptr m_selected_game; diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index adb66d8bcf..5bc38ff754 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -67,7 +67,7 @@ - Qt::ClickFocus + Qt::FocusPolicy::ClickFocus false @@ -119,16 +119,16 @@ - Qt::ClickFocus + Qt::FocusPolicy::ClickFocus false - Qt::Horizontal + Qt::Orientation::Horizontal - QSlider::NoTicks + QSlider::TickPosition::NoTicks @@ -145,7 +145,7 @@ - Qt::PreventContextMenu + Qt::ContextMenuPolicy::PreventContextMenu @@ -165,9 +165,6 @@ - - true - Boot Recent @@ -202,10 +199,22 @@ + + + Boot Recent Savestate + + + true + + + + + + @@ -288,6 +297,7 @@ + @@ -413,7 +423,7 @@ - Qt::PreventContextMenu + Qt::ContextMenuPolicy::PreventContextMenu Show tool bar @@ -422,7 +432,7 @@ false - Qt::TopToolBarArea + Qt::ToolBarArea::TopToolBarArea @@ -431,7 +441,7 @@ - Qt::ToolButtonTextUnderIcon + Qt::ToolButtonStyle::ToolButtonTextUnderIcon false @@ -1387,6 +1397,24 @@ Operating System + + + List Clear + + + + + true + + + List Freeze + + + + + Savestates + + diff --git a/rpcs3/rpcs3qt/memory_string_searcher.cpp b/rpcs3/rpcs3qt/memory_string_searcher.cpp index fc56509a12..c55cb8144d 100644 --- a/rpcs3/rpcs3qt/memory_string_searcher.cpp +++ b/rpcs3/rpcs3qt/memory_string_searcher.cpp @@ -1,9 +1,9 @@ #include "memory_viewer_panel.h" #include "Emu/Memory/vm.h" + #include "Emu/Memory/vm_reservation.h" #include "Emu/CPU/CPUDisAsm.h" #include "Emu/Cell/SPUDisAsm.h" -#include "Emu/IdManager.h" #include "Utilities/Thread.h" #include "Utilities/StrUtil.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include "util/logs.hpp" diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.cpp b/rpcs3/rpcs3qt/memory_viewer_panel.cpp index f9f246fbda..c1ae7a0a0e 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.cpp +++ b/rpcs3/rpcs3qt/memory_viewer_panel.cpp @@ -1,4 +1,3 @@ -#include "Utilities/mutex.h" #include "Emu/Memory/vm_locking.h" #include "Emu/Memory/vm.h" @@ -6,7 +5,6 @@ #include "Emu/Cell/SPUThread.h" #include "Emu/CPU/CPUDisAsm.h" -#include "Emu/Cell/SPUDisAsm.h" #include "Emu/RSX/RSXThread.h" #include "Emu/RSX/rsx_utils.h" #include "Emu/IdManager.h" @@ -26,7 +24,6 @@ #include "util/logs.hpp" #include "util/asm.hpp" -#include "util/vm.hpp" LOG_CHANNEL(gui_log, "GUI"); diff --git a/rpcs3/rpcs3qt/microphone_creator.cpp b/rpcs3/rpcs3qt/microphone_creator.cpp index 8f56bcae4e..daf9116eff 100644 --- a/rpcs3/rpcs3qt/microphone_creator.cpp +++ b/rpcs3/rpcs3qt/microphone_creator.cpp @@ -1,10 +1,10 @@ #include "stdafx.h" #include "microphone_creator.h" -#include "Utilities/StrFmt.h" #include "Utilities/StrUtil.h" -#include "3rdparty/OpenAL/openal-soft/include/AL/alext.h" +#include "3rdparty/OpenAL/openal-soft/include/AL/al.h" +#include "3rdparty/OpenAL/openal-soft/include/AL/alc.h" LOG_CHANNEL(cfg_log, "CFG"); diff --git a/rpcs3/rpcs3qt/osk_dialog_frame.cpp b/rpcs3/rpcs3qt/osk_dialog_frame.cpp index 8e9b385278..4e4c89318c 100644 --- a/rpcs3/rpcs3qt/osk_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/osk_dialog_frame.cpp @@ -1,8 +1,7 @@ #include "osk_dialog_frame.h" #include "custom_dialog.h" -#include "Emu/Cell/Modules/cellMsgDialog.h" -#include "util/asm.hpp" +#include "util/bless.hpp" #include #include diff --git a/rpcs3/rpcs3qt/pad_motion_settings_dialog.h b/rpcs3/rpcs3qt/pad_motion_settings_dialog.h index eeb413453b..f2c7713c6f 100644 --- a/rpcs3/rpcs3qt/pad_motion_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_motion_settings_dialog.h @@ -13,6 +13,7 @@ #include #include +#include namespace Ui { diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 2a2194d1b6..c20510c929 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -16,10 +16,9 @@ #include "Emu/System.h" #include "Emu/system_utils.hpp" -#include "Emu/Io/Null/NullPadHandler.h" -#include "Utilities/File.h" #include "Input/pad_thread.h" +#include "Input/gui_pad_thread.h" #include "Input/product_info.h" #include "Input/keyboard_pad_handler.h" @@ -245,6 +244,8 @@ pad_settings_dialog::~pad_settings_dialog() *m_input_thread = thread_state::finished; } + gui_pad_thread::reset(); + if (!Emu.IsStopped()) { pad::reset(Emu.GetTitleID()); diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.cpp b/rpcs3/rpcs3qt/patch_manager_dialog.cpp index a504e5790f..47cc299160 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/patch_manager_dialog.cpp @@ -14,7 +14,6 @@ #include "ui_patch_manager_dialog.h" #include "patch_manager_dialog.h" -#include "table_item_delegate.h" #include "gui_settings.h" #include "downloader.h" #include "qt_utils.h" diff --git a/rpcs3/rpcs3qt/ps_move_tracker_dialog.h b/rpcs3/rpcs3qt/ps_move_tracker_dialog.h index c7a8641e7e..fe3b043b26 100644 --- a/rpcs3/rpcs3qt/ps_move_tracker_dialog.h +++ b/rpcs3/rpcs3qt/ps_move_tracker_dialog.h @@ -1,9 +1,7 @@ #pragma once -#include "Utilities/mutex.h" #include "Utilities/Thread.h" #include "Input/pad_thread.h" -#include "Emu/Cell/Modules/cellGem.h" #include #include diff --git a/rpcs3/rpcs3qt/qt_camera_video_sink.h b/rpcs3/rpcs3qt/qt_camera_video_sink.h index 94946c177d..e3f405b55c 100644 --- a/rpcs3/rpcs3qt/qt_camera_video_sink.h +++ b/rpcs3/rpcs3qt/qt_camera_video_sink.h @@ -7,6 +7,7 @@ #include #include +#include class qt_camera_video_sink final : public QVideoSink { diff --git a/rpcs3/rpcs3qt/qt_music_handler.cpp b/rpcs3/rpcs3qt/qt_music_handler.cpp index 7eda064f6c..21ce264b84 100644 --- a/rpcs3/rpcs3qt/qt_music_handler.cpp +++ b/rpcs3/rpcs3qt/qt_music_handler.cpp @@ -67,7 +67,7 @@ qt_music_handler::qt_music_handler() { music_log.notice("Constructing Qt music handler..."); - m_media_player = std::make_shared(); + m_media_player = std::make_unique(); m_media_player->setAudioOutput(new QAudioOutput()); connect(m_media_player.get(), &QMediaPlayer::mediaStatusChanged, this, &qt_music_handler::handle_media_status); @@ -164,7 +164,7 @@ void qt_music_handler::fast_reverse(const std::string& path) } music_log.notice("Fast-reversing music..."); - m_media_player->setPlaybackRate(-2.0); + m_media_player->setPlaybackRate(-2.0); // NOTE: This doesn't work on the current Qt version m_media_player->play(); }); @@ -177,8 +177,8 @@ void qt_music_handler::set_volume(f32 volume) Emu.BlockingCallFromMainThread([&volume, this]() { - const int new_volume = std::max(0, std::min(volume * 100, 100)); - music_log.notice("Setting volume to %d%%", new_volume); + const f32 new_volume = std::clamp(volume, 0.0f, 1.0f); + music_log.notice("Setting volume to %f", new_volume); m_media_player->audioOutput()->setVolume(new_volume); }); } @@ -190,8 +190,8 @@ f32 qt_music_handler::get_volume() const Emu.BlockingCallFromMainThread([&volume, this]() { - volume = std::max(0.f, std::min(m_media_player->audioOutput()->volume(), 1.f)); - music_log.notice("Getting volume: %d%%", volume); + volume = std::clamp(m_media_player->audioOutput()->volume(), 0.0f, 1.0f); + music_log.notice("Getting volume: %f", volume); }); return volume; diff --git a/rpcs3/rpcs3qt/qt_music_handler.h b/rpcs3/rpcs3qt/qt_music_handler.h index 1e672fd8ec..9e62e06ce2 100644 --- a/rpcs3/rpcs3qt/qt_music_handler.h +++ b/rpcs3/rpcs3qt/qt_music_handler.h @@ -29,6 +29,6 @@ private Q_SLOTS: private: mutable std::mutex m_mutex; - std::shared_ptr m_media_player; + std::unique_ptr m_media_player; std::string m_path; }; diff --git a/rpcs3/rpcs3qt/recvmessage_dialog_frame.cpp b/rpcs3/rpcs3qt/recvmessage_dialog_frame.cpp index b617d74d95..7a805a5723 100644 --- a/rpcs3/rpcs3qt/recvmessage_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/recvmessage_dialog_frame.cpp @@ -7,6 +7,7 @@ #include "recvmessage_dialog_frame.h" #include "Emu/IdManager.h" #include "Emu/System.h" +#include "Emu/NP/rpcn_client.h" #include "util/logs.hpp" diff --git a/rpcs3/rpcs3qt/recvmessage_dialog_frame.h b/rpcs3/rpcs3qt/recvmessage_dialog_frame.h index 28255253dd..10b9bd87e9 100644 --- a/rpcs3/rpcs3qt/recvmessage_dialog_frame.h +++ b/rpcs3/rpcs3qt/recvmessage_dialog_frame.h @@ -4,8 +4,9 @@ #include #include "util/types.hpp" +#include "util/shared_ptr.hpp" #include "custom_dialog.h" -#include "Emu/NP/rpcn_client.h" +#include "Emu/Cell/Modules/sceNp.h" struct recvmessage_signal_struct { diff --git a/rpcs3/rpcs3qt/register_editor_dialog.h b/rpcs3/rpcs3qt/register_editor_dialog.h index 814d64cf39..c5ac750d8d 100644 --- a/rpcs3/rpcs3qt/register_editor_dialog.h +++ b/rpcs3/rpcs3qt/register_editor_dialog.h @@ -1,7 +1,5 @@ #pragma once -#include "util/types.hpp" - #include #include #include diff --git a/rpcs3/rpcs3qt/render_creator.cpp b/rpcs3/rpcs3qt/render_creator.cpp index c939c729fb..b85336bf8b 100644 --- a/rpcs3/rpcs3qt/render_creator.cpp +++ b/rpcs3/rpcs3qt/render_creator.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include LOG_CHANNEL(cfg_log, "CFG"); @@ -98,7 +97,7 @@ render_creator::render_creator(QObject *parent) : QObject(parent) #endif // Graphics Adapter - Vulkan = render_info(vulkan_adapters, supports_vulkan, emu_settings_type::VulkanAdapter, true); + Vulkan = render_info(vulkan_adapters, supports_vulkan, emu_settings_type::VulkanAdapter); OpenGL = render_info(); NullRender = render_info(); diff --git a/rpcs3/rpcs3qt/render_creator.h b/rpcs3/rpcs3qt/render_creator.h index 8203f09bb8..c433c77353 100644 --- a/rpcs3/rpcs3qt/render_creator.h +++ b/rpcs3/rpcs3qt/render_creator.h @@ -23,16 +23,14 @@ public: emu_settings_type type = emu_settings_type::VulkanAdapter; bool supported = true; bool has_adapters = true; - bool has_msaa = false; render_info() : has_adapters(false) {} - render_info(QStringList adapters, bool supported, emu_settings_type type, bool has_msaa) + render_info(QStringList adapters, bool supported, emu_settings_type type) : adapters(std::move(adapters)) , type(type) - , supported(supported) - , has_msaa(has_msaa) {} + , supported(supported) {} }; bool abort_requested = false; diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp index 499e1e2cc1..673dc53c52 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -7,14 +7,14 @@ #include #include #include - -#include +#include #include "qt_utils.h" #include "rpcn_settings_dialog.h" #include "Emu/System.h" #include "Emu/NP/rpcn_config.h" +#include "Emu/NP/ip_address.h" #include #include @@ -162,6 +162,9 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) QPushButton* btn_test = new QPushButton(tr("Test Account")); QLabel* label_npid = new QLabel(); + QCheckBox* checkbox_disable_ipv6 = new QCheckBox("Disable IPv6"); + checkbox_disable_ipv6->setCheckState(g_cfg_rpcn.get_ipv6_support() ? Qt::Unchecked : Qt::Checked); + const auto update_npid_label = [label_npid]() { const std::string npid = g_cfg_rpcn.get_npid(); @@ -180,6 +183,7 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) grp_buttons->setLayout(vbox_buttons); vbox_global->addWidget(grp_buttons); + vbox_global->addWidget(checkbox_disable_ipv6); setLayout(vbox_global); @@ -195,6 +199,10 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) g_cfg_rpcn.set_host(host.toString().toStdString()); g_cfg_rpcn.save(); + + // Resets the state in case the support was limited by the RPCN server + if (!np::is_ipv6_supported()) + np::is_ipv6_supported(np::IPV6_SUPPORT::IPV6_UNKNOWN); }); connect(btn_add_server, &QAbstractButton::clicked, this, [this]() @@ -335,6 +343,12 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) QMessageBox::information(this, tr("RPCN Account Valid!"), tr("Your account is valid!"), QMessageBox::Ok); }); + + connect(checkbox_disable_ipv6, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state) + { + g_cfg_rpcn.set_ipv6_support(state == Qt::Unchecked); + g_cfg_rpcn.save(); + }); } void rpcn_account_dialog::refresh_combobox() @@ -870,6 +884,8 @@ void rpcn_account_edit_dialog::change_password() return; } + g_cfg_rpcn.set_password(*password); + g_cfg_rpcn.save(); QMessageBox::information(this, tr("Password Successfully Changed!"), tr("Your password has been successfully changed!"), QMessageBox::Ok); } break; diff --git a/rpcs3/rpcs3qt/rsx_debugger.cpp b/rpcs3/rpcs3qt/rsx_debugger.cpp index e8d8bec3ea..809ce37525 100644 --- a/rpcs3/rpcs3qt/rsx_debugger.cpp +++ b/rpcs3/rpcs3qt/rsx_debugger.cpp @@ -4,7 +4,7 @@ #include "table_item_delegate.h" #include "Emu/RSX/RSXThread.h" #include "Emu/RSX/gcm_printing.h" -#include "util/asm.hpp" +#include "Utilities/File.h" #include #include diff --git a/rpcs3/rpcs3qt/save_data_dialog.cpp b/rpcs3/rpcs3qt/save_data_dialog.cpp index 20e9f78cfe..105fd7d27c 100644 --- a/rpcs3/rpcs3qt/save_data_dialog.cpp +++ b/rpcs3/rpcs3qt/save_data_dialog.cpp @@ -8,7 +8,6 @@ #include "Emu/RSX/Overlays/overlay_save_dialog.h" #include "Emu/Cell/Modules/cellSysutil.h" -#include "Utilities/Thread.h" #include "util/logs.hpp" LOG_CHANNEL(cellSaveData); diff --git a/rpcs3/rpcs3qt/save_data_dialog.h b/rpcs3/rpcs3qt/save_data_dialog.h index fa31b1533b..b33b16b77f 100644 --- a/rpcs3/rpcs3qt/save_data_dialog.h +++ b/rpcs3/rpcs3qt/save_data_dialog.h @@ -1,7 +1,6 @@ #pragma once #include "util/types.hpp" -#include "Emu/Memory/vm.h" #include "Emu/Cell/Modules/cellSaveData.h" class save_data_dialog : public SaveDialogBase diff --git a/rpcs3/rpcs3qt/save_data_list_dialog.h b/rpcs3/rpcs3qt/save_data_list_dialog.h index 334b45ca4b..13052ebbcb 100644 --- a/rpcs3/rpcs3qt/save_data_list_dialog.h +++ b/rpcs3/rpcs3qt/save_data_list_dialog.h @@ -1,7 +1,6 @@ #pragma once #include "util/types.hpp" -#include "Emu/Memory/vm.h" #include "Emu/Cell/Modules/cellSaveData.h" #include "Emu/RSX/Overlays/overlays.h" diff --git a/rpcs3/rpcs3qt/savestate_manager_dialog.cpp b/rpcs3/rpcs3qt/savestate_manager_dialog.cpp new file mode 100644 index 0000000000..463931b321 --- /dev/null +++ b/rpcs3/rpcs3qt/savestate_manager_dialog.cpp @@ -0,0 +1,794 @@ +#include "stdafx.h" +#include "savestate_manager_dialog.h" +#include "custom_table_widget_item.h" +#include "game_list_delegate.h" +#include "qt_utils.h" +#include "game_list.h" +#include "gui_settings.h" +#include "progress_dialog.h" + +#include +#include +#include +#include +#include +#include +#include + +LOG_CHANNEL(gui_log, "GUI"); + +enum GameUserRole +{ + GameIndex = Qt::UserRole, + GamePixmapLoaded, + GamePixmap +}; + +savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr gui_settings, const std::vector& games) + : QWidget() + , m_gui_settings(std::move(gui_settings)) + , m_game_info(games) +{ + // Nonspecific widget settings + setWindowTitle(tr("Savestate Manager")); + setObjectName("savestate_manager"); + setAttribute(Qt::WA_DeleteOnClose); + setAttribute(Qt::WA_StyledBackground); + + m_game_icon_size_index = m_gui_settings->GetValue(gui::ss_game_icon_size).toInt(); + + // Game chooser combo box + m_game_combo = new QComboBox(); + m_game_combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + + // Games Table + m_game_table = new game_list(); + m_game_table->setObjectName("savestate_manager_game_table"); + m_game_table->setShowGrid(false); + m_game_table->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + m_game_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + m_game_table->verticalScrollBar()->installEventFilter(this); + m_game_table->verticalScrollBar()->setSingleStep(20); + m_game_table->horizontalScrollBar()->setSingleStep(20); + m_game_table->setItemDelegate(new game_list_delegate(m_game_table)); + m_game_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_game_table->setSelectionMode(QAbstractItemView::SingleSelection); + m_game_table->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_game_table->setColumnCount(static_cast(gui::savestate_game_list_columns::count)); + m_game_table->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); + m_game_table->horizontalHeader()->setStretchLastSection(true); + m_game_table->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); + m_game_table->setContextMenuPolicy(Qt::CustomContextMenu); + m_game_table->verticalHeader()->setVisible(false); + m_game_table->setAlternatingRowColors(true); + m_game_table->installEventFilter(this); + + auto add_game_column = [this](gui::savestate_game_list_columns col, const QString& header_text, const QString& action_text) + { + m_game_table->setHorizontalHeaderItem(static_cast(col), new QTableWidgetItem(header_text)); + m_game_column_acts.append(new QAction(action_text, this)); + }; + + add_game_column(gui::savestate_game_list_columns::icon, tr("Icon"), tr("Show Icons")); + add_game_column(gui::savestate_game_list_columns::name, tr("Game"), tr("Show Games")); + add_game_column(gui::savestate_game_list_columns::savestates, tr("Savestates"), tr("Show Savestates")); + + // Savestate Table + m_savestate_table = new game_list(); + m_savestate_table->setObjectName("savestate_manager_savestate_table"); + m_savestate_table->setShowGrid(false); + m_savestate_table->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + m_savestate_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + m_savestate_table->verticalScrollBar()->installEventFilter(this); + m_savestate_table->verticalScrollBar()->setSingleStep(20); + m_savestate_table->horizontalScrollBar()->setSingleStep(20); + m_savestate_table->setItemDelegate(new game_list_delegate(m_savestate_table)); + m_savestate_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_savestate_table->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_savestate_table->setColumnCount(static_cast(gui::savestate_list_columns::count)); + m_savestate_table->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); + m_savestate_table->horizontalHeader()->setStretchLastSection(true); + m_savestate_table->verticalHeader()->setVisible(false); + m_savestate_table->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); + m_savestate_table->setContextMenuPolicy(Qt::CustomContextMenu); + m_savestate_table->setAlternatingRowColors(true); + m_savestate_table->installEventFilter(this); + + auto add_savestate_column = [this](gui::savestate_list_columns col, const QString& header_text, const QString& action_text) + { + m_savestate_table->setHorizontalHeaderItem(static_cast(col), new QTableWidgetItem(header_text)); + m_savestate_column_acts.append(new QAction(action_text, this)); + }; + + add_savestate_column(gui::savestate_list_columns::name, tr("Name"), tr("Show Names")); + add_savestate_column(gui::savestate_list_columns::compatible, tr("Compatible"), tr("Show Compatible")); + add_savestate_column(gui::savestate_list_columns::date, tr("Created"), tr("Show Created")); + add_savestate_column(gui::savestate_list_columns::path, tr("Path"), tr("Show Paths")); + + m_splitter = new QSplitter(); + m_splitter->addWidget(m_game_table); + m_splitter->addWidget(m_savestate_table); + + m_game_icon_size = gui_settings::SizeFromSlider(m_game_icon_size_index); + + QLabel* game_slider_label = new QLabel(); + game_slider_label->setText(tr("Game Icon Size: %0x%1").arg(m_game_icon_size.width()).arg(m_game_icon_size.height())); + + m_game_icon_slider = new QSlider(Qt::Horizontal); + m_game_icon_slider->setRange(0, gui::gl_max_slider_pos); + m_game_icon_slider->setValue(m_game_icon_size_index); + + // LAYOUTS + QGroupBox* choose_game = new QGroupBox(tr("Choose Game")); + QVBoxLayout* choose_layout = new QVBoxLayout(); + choose_layout->addWidget(m_game_combo); + choose_game->setLayout(choose_layout); + + QGroupBox* icon_settings = new QGroupBox(tr("Icon Options")); + QVBoxLayout* slider_layout = new QVBoxLayout(); + slider_layout->addWidget(game_slider_label); + slider_layout->addWidget(m_game_icon_slider); + icon_settings->setLayout(slider_layout); + + QVBoxLayout* options_layout = new QVBoxLayout(); + options_layout->addWidget(choose_game); + options_layout->addWidget(icon_settings); + options_layout->addStretch(); + + QHBoxLayout* all_layout = new QHBoxLayout(this); + all_layout->addLayout(options_layout); + all_layout->addWidget(m_splitter); + all_layout->setStretch(1, 1); + setLayout(all_layout); + + // Make connects + connect(m_game_icon_slider, &QSlider::valueChanged, this, [this, game_slider_label](int val) + { + m_game_icon_size_index = val; + m_game_icon_size = gui_settings::SizeFromSlider(val); + if (game_slider_label) + { + game_slider_label->setText(tr("Game Icon Size: %0x%1").arg(m_game_icon_size.width()).arg(m_game_icon_size.height())); + } + ResizeGameIcons(); + if (m_save_game_icon_size) + { + m_save_game_icon_size = false; + m_gui_settings->SetValue(gui::ss_game_icon_size, val); + } + }); + + connect(m_game_icon_slider, &QSlider::sliderReleased, this, [this]() + { + m_gui_settings->SetValue(gui::ss_game_icon_size, m_game_icon_slider->value()); + }); + + connect(m_game_icon_slider, &QSlider::actionTriggered, this, [this](int action) + { + if (action != QAbstractSlider::SliderNoAction && action != QAbstractSlider::SliderMove) + { // we only want to save on mouseclicks or slider release (the other connect handles this) + m_save_game_icon_size = true; // actionTriggered happens before the value was changed + } + }); + + connect(m_savestate_table, &QTableWidget::customContextMenuRequested, this, &savestate_manager_dialog::ShowSavestateTableContextMenu); + + connect(m_game_combo, &QComboBox::currentTextChanged, this, [this] + { + PopulateSavestateTable(); + }); + + connect(m_game_table, &QTableWidget::customContextMenuRequested, this, &savestate_manager_dialog::ShowGameTableContextMenu); + + connect(m_game_table, &QTableWidget::itemSelectionChanged, this, [this] + { + if (m_game_table->selectedItems().isEmpty()) + { + return; + } + QTableWidgetItem* item = m_game_table->item(m_game_table->selectedItems().first()->row(), static_cast(gui::savestate_game_list_columns::name)); + if (!item) + { + return; + } + m_game_combo->setCurrentText(item->text()); + }); + + connect(this, &savestate_manager_dialog::GameIconReady, this, [this](int index, const QPixmap& pixmap) + { + if (QTableWidgetItem* icon_item = m_game_table->item(index, static_cast(gui::savestate_game_list_columns::icon))) + { + icon_item->setData(Qt::DecorationRole, pixmap); + } + }); + + m_savestate_table->create_header_actions(m_savestate_column_acts, + [this](int col) { return m_gui_settings->GetSavestateListColVisibility(static_cast(col)); }, + [this](int col, bool visible) { m_gui_settings->SetSavestateListColVisibility(static_cast(col), visible); }); + + m_game_table->create_header_actions(m_game_column_acts, + [this](int col) { return m_gui_settings->GetSavestateGamelistColVisibility(static_cast(col)); }, + [this](int col, bool visible) { m_gui_settings->SetSavestateGamelistColVisibility(static_cast(col), visible); }); + + RepaintUI(true); + + StartSavestateLoadThreads(); +} + +savestate_manager_dialog::~savestate_manager_dialog() +{ + WaitAndAbortGameRepaintThreads(); +} + +bool savestate_manager_dialog::LoadSavestateFolderToDB(std::unique_ptr&& game_savestates) +{ + ensure(!!game_savestates); + + if (game_savestates->title_id.empty()) + { + gui_log.error("Failed to load savestates. Path empty!"); + return false; + } + + const std::string dir_path = fs::get_config_dir() + "savestates/" + game_savestates->title_id + "/"; + + const QDir savestate_dir(QString::fromStdString(dir_path)); + const QFileInfoList file_list = savestate_dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot); + + if (file_list.isEmpty()) + { + return false; + } + + // Populate game_savestates_data + game_savestates->savestates.resize(file_list.size()); + game_savestates->dir_path = dir_path; + + extern bool is_savestate_compatible(const std::string& filepath); + + for (usz id = 0; id < game_savestates->savestates.size(); ++id) + { + game_savestates->savestates[id].name = file_list[id].baseName(); + game_savestates->savestates[id].path = file_list[id].absoluteFilePath(); + game_savestates->savestates[id].date = file_list[id].birthTime(); + game_savestates->savestates[id].is_compatible = is_savestate_compatible(game_savestates->savestates[id].path.toStdString()); + } + + { + std::scoped_lock lock(m_savestate_db_mtx); + m_savestate_db.push_back(std::move(game_savestates)); + } + + return true; +} + +void savestate_manager_dialog::RepaintUI(bool restore_layout) +{ + if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool()) + { + m_game_icon_color = m_gui_settings->GetValue(gui::ss_icon_color).value(); + } + else + { + m_game_icon_color = gui::utils::get_label_color("savestate_manager_icon_background_color", Qt::transparent, Qt::transparent); + } + + PopulateGameTable(); + + if (restore_layout && !restoreGeometry(m_gui_settings->GetValue(gui::ss_geometry).toByteArray())) + { + resize(QGuiApplication::primaryScreen()->availableSize() * 0.7); + } + + if (restore_layout && !m_splitter->restoreState(m_gui_settings->GetValue(gui::ss_splitterState).toByteArray())) + { + const int width_left = m_splitter->width() * 0.4; + const int width_right = m_splitter->width() - width_left; + m_splitter->setSizes({ width_left, width_right }); + } + + PopulateSavestateTable(); + + const QByteArray game_table_state = m_gui_settings->GetValue(gui::ss_games_state).toByteArray(); + if (restore_layout && !m_game_table->horizontalHeader()->restoreState(game_table_state) && m_game_table->rowCount()) + { + // If no settings exist, resize to contents. (disabled) + //m_game_table->verticalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents); + //m_game_table->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents); + } + + const QByteArray savestate_table_state = m_gui_settings->GetValue(gui::ss_savestate_state).toByteArray(); + if (restore_layout && !m_savestate_table->horizontalHeader()->restoreState(savestate_table_state) && m_savestate_table->rowCount()) + { + // If no settings exist, resize to contents. (disabled) + //m_savestate_table->verticalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents); + //m_savestate_table->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents); + } + + if (restore_layout) + { + // Make sure the actions and the headers are synced + m_game_table->sync_header_actions(m_game_column_acts, [this](int col) { return m_gui_settings->GetSavestateGamelistColVisibility(static_cast(col)); }); + m_savestate_table->sync_header_actions(m_savestate_column_acts, [this](int col) { return m_gui_settings->GetSavestateListColVisibility(static_cast(col)); }); + } + + // Show dialog and then paint gui in order to adjust headers correctly + show(); + ReadjustGameTable(); + ReadjustSavestateTable(); +} + +void savestate_manager_dialog::HandleRepaintUiRequest() +{ + const QSize window_size = size(); + const QByteArray splitter_state = m_splitter->saveState(); + const QByteArray game_table_state = m_game_table->horizontalHeader()->saveState(); + const QByteArray savestate_table_state = m_savestate_table->horizontalHeader()->saveState(); + + RepaintUI(false); + + m_splitter->restoreState(splitter_state); + m_game_table->horizontalHeader()->restoreState(game_table_state); + m_savestate_table->horizontalHeader()->restoreState(savestate_table_state); + + // Make sure the actions and the headers are synced + m_game_table->sync_header_actions(m_game_column_acts, [this](int col) { return m_gui_settings->GetSavestateGamelistColVisibility(static_cast(col)); }); + m_savestate_table->sync_header_actions(m_savestate_column_acts, [this](int col) { return m_gui_settings->GetSavestateListColVisibility(static_cast(col)); }); + + resize(window_size); +} + +void savestate_manager_dialog::ResizeGameIcons() +{ + if (m_game_combo->count() <= 0) + return; + + WaitAndAbortGameRepaintThreads(); + + QPixmap placeholder(m_game_icon_size); + placeholder.fill(Qt::transparent); + + qRegisterMetaType>("QVector"); + for (int i = 0; i < m_game_table->rowCount(); ++i) + { + if (QTableWidgetItem* icon_item = m_game_table->item(i, static_cast(gui::savestate_game_list_columns::icon))) + { + icon_item->setData(Qt::DecorationRole, placeholder); + } + } + + ReadjustGameTable(); + + for (int i = 0; i < m_game_table->rowCount(); ++i) + { + if (movie_item* item = static_cast(m_game_table->item(i, static_cast(gui::savestate_game_list_columns::icon)))) + { + const qreal dpr = devicePixelRatioF(); + const int savestate_index = item->data(GameUserRole::GameIndex).toInt(); + const std::string icon_path = m_savestate_db[savestate_index]->game_icon_path; + + item->set_icon_load_func([this, icon_path, savestate_index, cancel = item->icon_loading_aborted(), dpr](int index) + { + if (cancel && cancel->load()) + { + return; + } + + QPixmap icon; + + if (movie_item* item = static_cast(m_game_table->item(index, static_cast(gui::savestate_game_list_columns::icon)))) + { + if (!item->data(GameUserRole::GamePixmapLoaded).toBool()) + { + // Load game icon + if (!icon.load(QString::fromStdString(icon_path))) + { + gui_log.warning("Could not load savestate game icon from path %s", icon_path); + } + item->setData(GameUserRole::GamePixmapLoaded, true); + item->setData(GameUserRole::GamePixmap, icon); + } + else + { + icon = item->data(GameUserRole::GamePixmap).value(); + } + } + + if (cancel && cancel->load()) + { + return; + } + + QPixmap new_icon(icon.size() * dpr); + new_icon.setDevicePixelRatio(dpr); + new_icon.fill(m_game_icon_color); + + if (!icon.isNull()) + { + QPainter painter(&new_icon); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.drawPixmap(QPoint(0, 0), icon); + painter.end(); + } + + new_icon = new_icon.scaled(m_game_icon_size * dpr, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation); + + if (!cancel || !cancel->load()) + { + Q_EMIT GameIconReady(index, new_icon); + } + }); + } + } +} + +void savestate_manager_dialog::ShowSavestateTableContextMenu(const QPoint& pos) +{ + const int row = m_savestate_table->currentRow(); + const QTableWidgetItem* path_item = m_savestate_table->item(row, static_cast(gui::savestate_list_columns::path)); + + if (!path_item) + { + return; + } + + QMenu* menu = new QMenu(); + QAction* show_savestate_dir = new QAction(tr("&Open Savestate Directory"), menu); + QAction* boot_savestate = new QAction(tr("&Boot Savestate"), menu); + QAction* delete_savestate = new QAction(tr("&Delete Savestate"), menu); + + if (const QTableWidgetItem* comp_item = m_savestate_table->item(row, static_cast(gui::savestate_list_columns::compatible))) + { + boot_savestate->setEnabled(comp_item->data(Qt::UserRole).toBool()); + } + + const int db_ind = m_game_combo->currentData().toInt(); + const std::string path = path_item->text().toStdString(); + + connect(show_savestate_dir, &QAction::triggered, this, [this, db_ind]() + { + gui::utils::open_dir(QString::fromStdString(m_savestate_db[db_ind]->dir_path)); + }); + + connect(boot_savestate, &QAction::triggered, this, [this, path]() + { + gui_log.notice("Booting savestate from savestate manager..."); + Q_EMIT RequestBoot(path); + }); + + connect(delete_savestate, &QAction::triggered, this, [this, path]() + { + gui_log.notice("Removing savestate '%s' from savestate manager...", path); + + if (QMessageBox::question(this, tr("Confirm Deletion"), tr("Delete savestate '%0'?").arg(QString::fromStdString(path))) != QMessageBox::Yes) + return; + + if (fs::remove_file(path)) + { + gui_log.success("Removed savestate '%s'", path); + StartSavestateLoadThreads(); // Reload the savestate list + } + else + { + gui_log.error("Failed to remove file '%s' (%s)", path, fs::g_tls_error); + QMessageBox::warning(this, tr("Deletion Failed!"), tr("Failed to delete savestate '%0'!").arg(QString::fromStdString(path))); + } + }); + + menu->addAction(boot_savestate); + menu->addAction(show_savestate_dir); + menu->addSeparator(); + menu->addAction(delete_savestate); + + menu->exec(m_savestate_table->viewport()->mapToGlobal(pos)); +} + +void savestate_manager_dialog::ShowGameTableContextMenu(const QPoint& pos) +{ + const int row = m_game_table->currentRow(); + + if (!m_game_table->item(row, static_cast(gui::savestate_game_list_columns::icon))) + { + return; + } + + QMenu* menu = new QMenu(); + QAction* remove_savestate_dir = new QAction(tr("&Remove All Savestates"), this); + QAction* show_savestate_dir = new QAction(tr("&Open Savestate Directory"), menu); + + const int db_ind = m_game_combo->currentData().toInt(); + + const QTableWidgetItem* name_item = m_game_table->item(row, static_cast(gui::savestate_game_list_columns::name)); + const QString name = name_item ? name_item->text() : ""; + + connect(remove_savestate_dir, &QAction::triggered, this, [this, name, db_ind]() + { + if (QMessageBox::question(this, tr("Delete Confirmation"), tr("Are you sure you want to delete the savestates for:\n%0?").arg(name), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + const std::string path = m_savestate_db[db_ind]->dir_path; + ensure(path != (fs::get_config_dir() + "savestates/")); // Make sure we aren't deleting the root path by accident + fs::remove_all(path); // Remove the game's savestate folder + StartSavestateLoadThreads(); // Reload the savestate list + } + }); + connect(show_savestate_dir, &QAction::triggered, this, [this, db_ind]() + { + gui::utils::open_dir(QString::fromStdString(m_savestate_db[db_ind]->dir_path)); + }); + + menu->addAction(show_savestate_dir); + + if (!name.isEmpty()) + { + QAction* copy_name = new QAction(tr("&Copy Name"), menu); + connect(copy_name, &QAction::triggered, this, [this, name]() + { + QApplication::clipboard()->setText(name); + }); + menu->addAction(copy_name); + } + + menu->addSeparator(); + menu->addAction(remove_savestate_dir); + + menu->exec(m_game_table->viewport()->mapToGlobal(pos)); +} + +void savestate_manager_dialog::StartSavestateLoadThreads() +{ + WaitAndAbortGameRepaintThreads(); + + m_savestate_db.clear(); + + const QString savestate_path = QString::fromStdString(fs::get_config_dir() + "savestates/"); + const QDir savestate_dir(savestate_path); + const QStringList folder_list = savestate_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + const int count = folder_list.count(); + + if (count <= 0) + { + RepaintUI(true); + return; + } + + std::vector> game_data(count); + + qRegisterMetaType>("QVector"); + QList indices; + for (int i = 0; i < count; ++i) + { + indices.append(i); + + game_data[i] = std::make_unique(); + game_data[i]->title_id = folder_list[i].toStdString(); + + for (const game_info& gameinfo : m_game_info) + { + if (gameinfo && gameinfo->info.serial == game_data[i]->title_id) + { + game_data[i]->game_name = gameinfo->info.name; + game_data[i]->game_icon_path = gameinfo->info.icon_path; + break; + } + } + + if (game_data[i]->game_name.empty()) + { + game_data[i]->game_name = game_data[i]->title_id; + } + } + + QFutureWatcher future_watcher; + + progress_dialog progress_dialog(tr("Loading savestates"), tr("Loading savestates, please wait..."), tr("Cancel"), 0, 1, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); + + connect(&future_watcher, &QFutureWatcher::progressRangeChanged, &progress_dialog, &QProgressDialog::setRange); + connect(&future_watcher, &QFutureWatcher::progressValueChanged, &progress_dialog, &QProgressDialog::setValue); + connect(&future_watcher, &QFutureWatcher::finished, this, [this]() { RepaintUI(true); }); + connect(&progress_dialog, &QProgressDialog::canceled, this, [this, &future_watcher]() + { + future_watcher.cancel(); + close(); // It's pointless to show an empty window + }); + + atomic_t error_count{}; + future_watcher.setFuture(QtConcurrent::map(indices, [this, &error_count, &game_data](const int& i) + { + gui_log.trace("Loading savestate dir: %s", game_data[i]->title_id); + + if (!LoadSavestateFolderToDB(std::move(game_data[i]))) + { + // TODO: add a way of showing the number of corrupted/invalid savestates in UI somewhere. + gui_log.error("Error occurred while parsing folder %s for savestates.", game_data[i]->title_id); + error_count++; + } + })); + + progress_dialog.exec(); + + future_watcher.waitForFinished(); + + if (error_count != 0) + { + gui_log.error("Failed to load %d of %d savestates!", error_count.load(), count); + } +} + +void savestate_manager_dialog::PopulateGameTable() +{ + WaitAndAbortGameRepaintThreads(); + + m_game_table->setSortingEnabled(false); // Disable sorting before using setItem calls + m_game_table->clearContents(); + m_game_table->setRowCount(static_cast(m_savestate_db.size())); + + m_game_combo->clear(); + m_game_combo->blockSignals(true); + + qRegisterMetaType>("QVector"); + QList indices; + for (usz i = 0; i < m_savestate_db.size(); ++i) + indices.append(static_cast(i)); + + QPixmap placeholder(m_game_icon_size); + placeholder.fill(Qt::transparent); + + for (int i = 0; i < indices.count(); ++i) + { + const QString name = QString::fromStdString(m_savestate_db[i]->game_name).simplified(); + const quint64 savestate_count = m_savestate_db[i]->savestates.size(); + + custom_table_widget_item* icon_item = new custom_table_widget_item; + icon_item->setData(Qt::DecorationRole, placeholder); + icon_item->setData(GameUserRole::GameIndex, i); + icon_item->setData(GameUserRole::GamePixmapLoaded, false); + icon_item->setData(GameUserRole::GamePixmap, QPixmap()); + + m_game_table->setItem(i, static_cast(gui::savestate_game_list_columns::icon), icon_item); + m_game_table->setItem(i, static_cast(gui::savestate_game_list_columns::name), new custom_table_widget_item(name)); + m_game_table->setItem(i, static_cast(gui::savestate_game_list_columns::savestates), new custom_table_widget_item(QString::number(savestate_count), Qt::UserRole, savestate_count)); + + m_game_combo->addItem(name, i); + } + + m_game_combo->model()->sort(0, Qt::AscendingOrder); + m_game_combo->blockSignals(false); + m_game_combo->setCurrentIndex(0); + + m_game_table->setSortingEnabled(true); // Enable sorting only after using setItem calls + + ResizeGameIcons(); + + gui::utils::resize_combo_box_view(m_game_combo); +} + +void savestate_manager_dialog::PopulateSavestateTable() +{ + if (m_game_combo->count() <= 0) + return; + + const auto& data = m_savestate_db[m_game_combo->currentData().toInt()]; + ensure(!!data); + + gui_log.trace("Populating Savestate Manager UI with %s %s", data->game_name, data->dir_path); + + const std::vector& savestates = data->savestates; + + m_savestate_table->clear_list(); + m_savestate_table->setRowCount(static_cast(savestates.size())); + m_savestate_table->setSortingEnabled(false); // Disable sorting before using setItem calls + + const QLocale locale{}; + + for (int i = 0; i < static_cast(savestates.size()); i++) + { + const savestate_data& savestate = savestates[i]; + m_savestate_table->setItem(i, static_cast(gui::savestate_list_columns::name), new custom_table_widget_item(savestate.name)); + m_savestate_table->setItem(i, static_cast(gui::savestate_list_columns::compatible), new custom_table_widget_item(savestate.is_compatible ? tr("Compatible") : tr("Not compatible"), Qt::UserRole, savestate.is_compatible)); + m_savestate_table->setItem(i, static_cast(gui::savestate_list_columns::date), new custom_table_widget_item(savestate.date.toString(), Qt::UserRole, savestate.date)); + m_savestate_table->setItem(i, static_cast(gui::savestate_list_columns::path), new custom_table_widget_item(savestate.path)); + } + + m_savestate_table->setSortingEnabled(true); // Re-enable sorting after using setItem calls +} + +void savestate_manager_dialog::ReadjustGameTable() const +{ + // Fixate vertical header and row height + m_game_table->verticalHeader()->setMinimumSectionSize(m_game_icon_size.height()); + m_game_table->verticalHeader()->setMaximumSectionSize(m_game_icon_size.height()); + m_game_table->resizeRowsToContents(); + + // Resize and fixate icon column + m_game_table->resizeColumnToContents(static_cast(gui::savestate_game_list_columns::icon)); + m_game_table->horizontalHeader()->setSectionResizeMode(static_cast(gui::savestate_game_list_columns::icon), QHeaderView::Fixed); + + // Shorten the last section to remove horizontal scrollbar if possible + m_game_table->resizeColumnToContents(static_cast(gui::savestate_game_list_columns::count) - 1); +} + +void savestate_manager_dialog::ReadjustSavestateTable() const +{ + // Fixate vertical header and row height + m_savestate_table->resizeRowsToContents(); + + // Shorten the last section to remove horizontal scrollbar if possible + m_savestate_table->resizeColumnToContents(static_cast(gui::savestate_list_columns::count) - 1); +} + +bool savestate_manager_dialog::eventFilter(QObject *object, QEvent *event) +{ + const bool is_savestate_scroll = object == m_savestate_table->verticalScrollBar(); + const bool is_savestate_table = object == m_savestate_table; + const bool is_game_scroll = object == m_game_table->verticalScrollBar(); + const bool is_game_table = object == m_game_table; + int zoom_val = 0; + + switch (event->type()) + { + case QEvent::Wheel: + { + QWheelEvent *wheelEvent = static_cast(event); + + if (wheelEvent->modifiers() & Qt::ControlModifier && (is_savestate_scroll || is_game_scroll)) + { + const QPoint numSteps = wheelEvent->angleDelta() / 8 / 15; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta + zoom_val = numSteps.y(); + } + break; + } + case QEvent::KeyPress: + { + QKeyEvent *keyEvent = static_cast(event); + + if (keyEvent && keyEvent->modifiers() == Qt::ControlModifier && (is_savestate_table || is_game_table)) + { + if (keyEvent->key() == Qt::Key_Plus) + { + zoom_val = 1; + } + else if (keyEvent->key() == Qt::Key_Minus) + { + zoom_val = -1; + } + } + break; + } + default: + break; + } + + if (zoom_val != 0) + { + if (m_game_icon_slider && (is_game_table || is_game_scroll)) + { + m_save_game_icon_size = true; + m_game_icon_slider->setSliderPosition(zoom_val + m_game_icon_slider->value()); + } + return true; + } + + return QWidget::eventFilter(object, event); +} + +void savestate_manager_dialog::closeEvent(QCloseEvent *event) +{ + // Save gui settings + m_gui_settings->SetValue(gui::ss_geometry, saveGeometry(), false); + m_gui_settings->SetValue(gui::ss_splitterState, m_splitter->saveState(), false); + m_gui_settings->SetValue(gui::ss_games_state, m_game_table->horizontalHeader()->saveState(), false); + m_gui_settings->SetValue(gui::ss_savestate_state, m_savestate_table->horizontalHeader()->saveState(), true); + + QWidget::closeEvent(event); +} + +void savestate_manager_dialog::WaitAndAbortGameRepaintThreads() +{ + for (int i = 0; i < m_game_table->rowCount(); i++) + { + if (movie_item* item = static_cast(m_game_table->item(i, static_cast(gui::savestate_game_list_columns::icon)))) + { + item->wait_for_icon_loading(true); + } + } +} diff --git a/rpcs3/rpcs3qt/savestate_manager_dialog.h b/rpcs3/rpcs3qt/savestate_manager_dialog.h new file mode 100644 index 0000000000..9c39f75765 --- /dev/null +++ b/rpcs3/rpcs3qt/savestate_manager_dialog.h @@ -0,0 +1,85 @@ +#pragma once + +#include "game_list_base.h" + +#include +#include +#include + +#include + +class game_list; +class gui_settings; + +class savestate_manager_dialog : public QWidget +{ + Q_OBJECT + +public: + explicit savestate_manager_dialog(std::shared_ptr gui_settings, const std::vector& games); + ~savestate_manager_dialog() override; + void RepaintUI(bool restore_layout = true); + +public Q_SLOTS: + void HandleRepaintUiRequest(); + +private Q_SLOTS: + void ResizeGameIcons(); + void ShowSavestateTableContextMenu(const QPoint& pos); + void ShowGameTableContextMenu(const QPoint& pos); + +Q_SIGNALS: + void GameIconReady(int index, const QPixmap& pixmap); + void RequestBoot(const std::string& path); + +private: + struct savestate_data + { + QString name; + QString path; + QDateTime date; + bool is_compatible = false; + }; + + struct game_savestates_data + { + std::vector savestates; + std::string title_id; + std::string game_name; + std::string game_icon_path; + std::string dir_path; + }; + + bool LoadSavestateFolderToDB(std::unique_ptr&& game_savestates); + void StartSavestateLoadThreads(); + + void PopulateGameTable(); + void PopulateSavestateTable(); + + void ReadjustGameTable() const; + void ReadjustSavestateTable() const; + + void WaitAndAbortGameRepaintThreads(); + + void closeEvent(QCloseEvent *event) override; + bool eventFilter(QObject *object, QEvent *event) override; + + std::shared_ptr m_gui_settings; + + std::vector m_game_info; + std::vector> m_savestate_db; //! Holds all the savestate information. + std::mutex m_savestate_db_mtx; + QComboBox* m_game_combo; //! Lets you choose a game + QSplitter* m_splitter; //! Contains the game and savestate tables + game_list* m_savestate_table; //! UI element to display savestate stuff. + game_list* m_game_table; //! UI element to display games. + + QList m_savestate_column_acts; + QList m_game_column_acts; + + int m_game_icon_size_index = 25; + QSize m_game_icon_size = QSize(m_game_icon_size_index, m_game_icon_size_index); + bool m_save_game_icon_size = false; + QSlider* m_game_icon_slider = nullptr; + QColor m_game_icon_color; +}; diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp index b93f9398ad..3825dfd747 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp @@ -5,7 +5,6 @@ #include "flow_widget.h" #include "qt_utils.h" #include "Utilities/File.h" -#include "Emu/VFS.h" #include "Emu/system_utils.hpp" #include diff --git a/rpcs3/rpcs3qt/sendmessage_dialog_frame.cpp b/rpcs3/rpcs3qt/sendmessage_dialog_frame.cpp index ce8f50003b..7f794b67f2 100644 --- a/rpcs3/rpcs3qt/sendmessage_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/sendmessage_dialog_frame.cpp @@ -6,6 +6,7 @@ #include "sendmessage_dialog_frame.h" #include "Emu/IdManager.h" +#include "Emu/NP/rpcn_client.h" #include "Emu/System.h" #include "util/logs.hpp" @@ -155,7 +156,7 @@ void sendmessage_dialog_frame::slot_remove_friend(QString name) remove_friend(m_lst_friends, name); } -void sendmessage_dialog_frame::callback_handler(u16 ntype, const std::string& username, bool status) +void sendmessage_dialog_frame::callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status) { QString qtr_username = QString::fromStdString(username); switch (ntype) diff --git a/rpcs3/rpcs3qt/sendmessage_dialog_frame.h b/rpcs3/rpcs3qt/sendmessage_dialog_frame.h index 1a74eddba8..d4aad5cd5d 100644 --- a/rpcs3/rpcs3qt/sendmessage_dialog_frame.h +++ b/rpcs3/rpcs3qt/sendmessage_dialog_frame.h @@ -5,7 +5,8 @@ #include "util/types.hpp" #include "custom_dialog.h" -#include "Emu/NP/rpcn_client.h" + +#include "Emu/Cell/Modules/sceNp.h" class sendmessage_dialog_frame : public QObject, public SendMessageDialogBase { @@ -15,7 +16,7 @@ public: sendmessage_dialog_frame() = default; ~sendmessage_dialog_frame(); error_code Exec(message_data& msg_data, std::set& npids) override; - void callback_handler(u16 ntype, const std::string& username, bool status) override; + void callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status) override; private: void add_friend(QListWidget* list, const QString& name); diff --git a/rpcs3/rpcs3qt/settings.cpp b/rpcs3/rpcs3qt/settings.cpp index 74901561c2..c87918d468 100644 --- a/rpcs3/rpcs3qt/settings.cpp +++ b/rpcs3/rpcs3qt/settings.cpp @@ -1,7 +1,5 @@ #include "settings.h" -#include "qt_utils.h" - #include "Utilities/File.h" settings::settings(QObject* parent) : QObject(parent), diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 12c62c4dbf..9dabd04cc9 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -36,8 +36,6 @@ #include "Loader/PSF.h" #include -#include -#include #include "util/sysinfo.hpp" #include "util/asm.hpp" @@ -838,12 +836,6 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std } } - // Enable/disable MSAA depending on renderer - ui->antiAliasing->setEnabled(renderer.has_msaa); - ui->antiAliasing->blockSignals(true); - ui->antiAliasing->setCurrentText(renderer.has_msaa ? qstr(m_emu_settings->GetSetting(emu_settings_type::MSAA)) : tr("Disabled", "MSAA")); - ui->antiAliasing->blockSignals(false); - ui->graphicsAdapterBox->clear(); // Fill combobox with placeholder if no adapters needed @@ -1070,7 +1062,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std get_audio_output_devices(false); change_audio_output_device(0); // Set device to 'Default' }); - + m_emu_settings->EnhanceComboBox(ui->combo_audio_channel_layout, emu_settings_type::AudioChannelLayout); SubscribeTooltip(ui->gb_audio_channel_layout, tooltips.settings.audio_channel_layout); @@ -1512,7 +1504,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->SetSetting(emu_settings_type::PSNCountry, country_code.toString().toStdString()); }); - + SubscribeTooltip(ui->gb_psnCountryBox, tooltips.settings.psn_country); if (!game) diff --git a/rpcs3/rpcs3qt/shortcut_handler.cpp b/rpcs3/rpcs3qt/shortcut_handler.cpp index 9d70aa0262..9eb8fa3ea4 100644 --- a/rpcs3/rpcs3qt/shortcut_handler.cpp +++ b/rpcs3/rpcs3qt/shortcut_handler.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "shortcut_handler.h" -#include "Emu/System.h" LOG_CHANNEL(shortcut_log, "Shortcuts"); diff --git a/rpcs3/rpcs3qt/shortcut_utils.cpp b/rpcs3/rpcs3qt/shortcut_utils.cpp index 1c9a3d759f..6c16cdff04 100644 --- a/rpcs3/rpcs3qt/shortcut_utils.cpp +++ b/rpcs3/rpcs3qt/shortcut_utils.cpp @@ -1,9 +1,7 @@ #include "stdafx.h" #include "shortcut_utils.h" #include "qt_utils.h" -#include "Emu/system_utils.hpp" #include "Emu/VFS.h" -#include "Emu/vfs_config.h" #include "Utilities/File.h" #include "Utilities/StrUtil.h" @@ -16,6 +14,8 @@ #include #include #include + +#include "Emu/system_utils.hpp" #else #include #include diff --git a/rpcs3/rpcs3qt/skylander_dialog.cpp b/rpcs3/rpcs3qt/skylander_dialog.cpp index 7ed79d740e..beebb8a965 100644 --- a/rpcs3/rpcs3qt/skylander_dialog.cpp +++ b/rpcs3/rpcs3qt/skylander_dialog.cpp @@ -1,12 +1,8 @@ #include "stdafx.h" #include "Utilities/File.h" -#include "Crypto/md5.h" -#include "Crypto/aes.h" #include "skylander_dialog.h" #include "Emu/Io/Skylander.h" -#include "util/asm.hpp" - #include #include #include diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp index 72a1e2d42c..1c525809ba 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp @@ -601,7 +601,6 @@ void trophy_manager_dialog::ResizeGameIcons() if (!item->data(GameUserRole::GamePixmapLoaded).toBool()) { // Load game icon - const std::string icon_path = m_trophies_db[trophy_index]->path + "ICON0.PNG"; if (!icon.load(QString::fromStdString(icon_path))) { gui_log.warning("Could not load trophy game icon from path %s", icon_path); @@ -1110,6 +1109,8 @@ void trophy_manager_dialog::PopulateTrophyTable() return; auto& data = m_trophies_db[m_game_combo->currentData().toInt()]; + ensure(!!data); + gui_log.trace("Populating Trophy Manager UI with %s %s", data->game_name, data->path); const int all_trophies = data->trop_usr->GetTrophiesCount(); diff --git a/rpcs3/rpcs3qt/update_manager.cpp b/rpcs3/rpcs3qt/update_manager.cpp index cdd7b4a0e1..92c5416d81 100644 --- a/rpcs3/rpcs3qt/update_manager.cpp +++ b/rpcs3/rpcs3qt/update_manager.cpp @@ -4,13 +4,12 @@ #include "rpcs3_version.h" #include "downloader.h" #include "gui_settings.h" -#include "Utilities/StrUtil.h" #include "Utilities/File.h" #include "Emu/System.h" -#include "Emu/system_utils.hpp" #include "Crypto/utils.h" #include "util/logs.hpp" #include "util/types.hpp" +#include "util/sysinfo.hpp" #include #include @@ -40,21 +39,12 @@ #define PATH_MAX MAX_PATH #endif +#include "Utilities/StrUtil.h" #else #include #include #endif -#if defined(__APPLE__) -// sysinfo_darwin.mm -namespace Darwin_Version -{ - extern int getNSmajorVersion(); - extern int getNSminorVersion(); - extern int getNSpatchVersion(); -} -#endif - LOG_CHANNEL(update_log, "UPDATER"); update_manager::update_manager(QObject* parent, std::shared_ptr gui_settings) @@ -116,26 +106,11 @@ void update_manager::check_for_updates(bool automatic, bool check_only, bool aut Q_EMIT signal_update_available(result_json && !m_update_message.isEmpty()); }); -#if defined(__APPLE__) - const std::string url = fmt::format("https://update.rpcs3.net/" - "?api=v3" - "&c=%s" - "&os_type=macos" - "&os_arch=" -#if defined(ARCH_X64) - "x64" -#elif defined(ARCH_ARM64) - "arm64" -#endif - "&os_version=%i.%i.%i", - rpcs3::get_commit_and_hash().second, - Darwin_Version::getNSmajorVersion(), - Darwin_Version::getNSminorVersion(), - Darwin_Version::getNSpatchVersion()); -#else - const std::string url = "https://update.rpcs3.net/?api=v2&c=" + rpcs3::get_commit_and_hash().second; -#endif - + const utils::OS_version os = utils::get_OS_version(); + + const std::string url = fmt::format("https://update.rpcs3.net/?api=v3&c=%s&os_type=%s&os_arch=%s&os_version=%i.%i.%i", + rpcs3::get_commit_and_hash().second, os.type, os.arch, os.version_major, os.version_minor, os.version_patch); + m_downloader->start(url, true, !automatic, tr("Checking For Updates"), true); } @@ -199,11 +174,25 @@ bool update_manager::handle_json(bool automatic, bool check_only, bool auto_acce #endif // Check that every bit of info we need is there - if (!latest[os].isObject() || !latest[os]["download"].isString() || !latest[os]["size"].isDouble() || !latest[os]["checksum"].isString() || !latest["version"].isString() || - !latest["datetime"].isString() || - (hash_found && (!current.isObject() || !current["version"].isString() || !current["datetime"].isString()))) + const auto check_json = [](bool cond, std::string_view msg) -> bool + { + if (cond) return true; + update_log.error("%s", msg); + return false; + }; + if (!(check_json(latest[os].isObject(), fmt::format("Node 'latest_build: %s' not found", os)) && + check_json(latest[os]["download"].isString(), fmt::format("Node 'latest_build: %s: download' not found or not a string", os)) && + check_json(latest[os]["size"].isDouble(), fmt::format("Node 'latest_build: %s: size' not found or not a double", os)) && + check_json(latest[os]["checksum"].isString(), fmt::format("Node 'latest_build: %s: checksum' not found or not a string", os)) && + check_json(latest["version"].isString(), "Node 'latest_build: version' not found or not a string") && + check_json(latest["datetime"].isString(), "Node 'latest_build: datetime' not found or not a string") + ) || + (hash_found && !( + check_json(current.isObject(), "JSON doesn't contain current_build section") && + check_json(current["version"].isString(), "Node 'current_build: datetime' not found or not a string") && + check_json(current["datetime"].isString(), "Node 'current_build: version' not found or not a string") + ))) { - update_log.error("Some information seems unavailable"); return false; } diff --git a/rpcs3/rpcs3qt/user_manager_dialog.cpp b/rpcs3/rpcs3qt/user_manager_dialog.cpp index 24336bd089..b8ccebdf43 100644 --- a/rpcs3/rpcs3qt/user_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/user_manager_dialog.cpp @@ -22,7 +22,6 @@ #include "Emu/System.h" #include "Emu/system_utils.hpp" -#include "Utilities/StrUtil.h" #include "Utilities/File.h" #include "util/logs.hpp" diff --git a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp index 878fb0476d..090e97ee69 100644 --- a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp +++ b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp @@ -1,5 +1,4 @@ #include "vfs_dialog_path_widget.h" -#include "Utilities/Config.h" #include #include diff --git a/rpcs3/rpcs3qt/vfs_dialog_path_widget.h b/rpcs3/rpcs3qt/vfs_dialog_path_widget.h index ea09a6e67c..a90a43d378 100644 --- a/rpcs3/rpcs3qt/vfs_dialog_path_widget.h +++ b/rpcs3/rpcs3qt/vfs_dialog_path_widget.h @@ -1,6 +1,5 @@ #pragma once -#include "util/types.hpp" #include "gui_settings.h" #include diff --git a/rpcs3/rpcs3qt/vfs_dialog_usb_tab.h b/rpcs3/rpcs3qt/vfs_dialog_usb_tab.h index 65cea0a2c3..02d7a25c83 100644 --- a/rpcs3/rpcs3qt/vfs_dialog_usb_tab.h +++ b/rpcs3/rpcs3qt/vfs_dialog_usb_tab.h @@ -1,7 +1,5 @@ #pragma once -#include "util/types.hpp" - #include "gui_settings.h" #include diff --git a/rpcs3/util/atomic.hpp b/rpcs3/util/atomic.hpp index 2d4946a04a..4c3ab8959c 100644 --- a/rpcs3/util/atomic.hpp +++ b/rpcs3/util/atomic.hpp @@ -2,7 +2,6 @@ #include "util/types.hpp" #include -#include #ifndef _MSC_VER #pragma GCC diagnostic push diff --git a/rpcs3/util/cpu_stats.cpp b/rpcs3/util/cpu_stats.cpp index acf965ddbc..de1d3b133c 100644 --- a/rpcs3/util/cpu_stats.cpp +++ b/rpcs3/util/cpu_stats.cpp @@ -2,12 +2,12 @@ #include "util/cpu_stats.hpp" #include "util/sysinfo.hpp" #include "util/logs.hpp" -#include "util/asm.hpp" #include "Utilities/StrUtil.h" #include #ifdef _WIN32 +#include "util/asm.hpp" #include "windows.h" #include "tlhelp32.h" #ifdef _MSC_VER @@ -18,8 +18,6 @@ #include "sstream" #include "stdlib.h" #include "sys/times.h" -#include "sys/types.h" -#include "unistd.h" #endif #ifdef __APPLE__ @@ -129,20 +127,6 @@ namespace utils per_core_usage.resize(utils::get_thread_count()); std::fill(per_core_usage.begin(), per_core_usage.end(), 0.0); -#if defined(_WIN32) || defined(__linux__) - const auto string_to_number = [](const std::string& str) -> std::pair - { - std::add_pointer_t eval; - const size_t number = std::strtol(str.c_str(), &eval, 10); - - if (str.c_str() + str.size() == eval) - { - return { true, number }; - } - - return { false, 0 }; - }; - #ifdef _WIN32 if (!m_cpu_cores || !m_cpu_query) { @@ -304,7 +288,6 @@ namespace utils { perf_log.error("Failed to open /proc/stat (%s)", strerror(errno)); } -#endif #else total_usage = get_usage(); #endif diff --git a/rpcs3/util/media_utils.cpp b/rpcs3/util/media_utils.cpp index 4ba78cb750..73bc86ef46 100644 --- a/rpcs3/util/media_utils.cpp +++ b/rpcs3/util/media_utils.cpp @@ -3,6 +3,7 @@ #include "Emu/System.h" #include +#include #ifdef _MSC_VER #pragma warning(push, 0) diff --git a/rpcs3/util/media_utils.h b/rpcs3/util/media_utils.h index 9666e53d86..471bda63ae 100644 --- a/rpcs3/util/media_utils.h +++ b/rpcs3/util/media_utils.h @@ -2,13 +2,11 @@ #include "Utilities/StrUtil.h" #include "Utilities/Thread.h" -#include "util/video_provider.h" +#include "util/video_sink.h" #include "Emu/Cell/Modules/cellMusic.h" #include #include -#include -#include namespace utils { diff --git a/rpcs3/util/serialization_ext.cpp b/rpcs3/util/serialization_ext.cpp index 62874d9b76..508cd79168 100644 --- a/rpcs3/util/serialization_ext.cpp +++ b/rpcs3/util/serialization_ext.cpp @@ -1,9 +1,8 @@ #include "util/types.hpp" #include "util/logs.hpp" #include "util/asm.hpp" -#include "util/simd.hpp" +#include "util/sysinfo.hpp" #include "util/endian.hpp" - #include "Utilities/lockless.h" #include "Utilities/File.h" #include "Utilities/StrFmt.h" diff --git a/rpcs3/util/serialization_ext.hpp b/rpcs3/util/serialization_ext.hpp index 75b6212890..51071e7188 100644 --- a/rpcs3/util/serialization_ext.hpp +++ b/rpcs3/util/serialization_ext.hpp @@ -187,7 +187,6 @@ private: void initialize(utils::serial& ar); void stream_data_prepare_thread_op(); void file_writer_thread_op(); - void blocked_compressed_write(const std::vector& data); }; template requires (std::is_same_v, fs::file>) diff --git a/rpcs3/util/shared_ptr.hpp b/rpcs3/util/shared_ptr.hpp index e9bdf83b3b..29e4150c21 100644 --- a/rpcs3/util/shared_ptr.hpp +++ b/rpcs3/util/shared_ptr.hpp @@ -1,6 +1,5 @@ #pragma once // No BOM and only basic ASCII in this header, or a neko will die -#include #include #include #include "atomic.hpp" diff --git a/rpcs3/util/simd.hpp b/rpcs3/util/simd.hpp index 750bb97fd1..ff4e0eed78 100644 --- a/rpcs3/util/simd.hpp +++ b/rpcs3/util/simd.hpp @@ -24,7 +24,6 @@ #include #include #include -#include #if defined(__clang__) #pragma clang diagnostic push diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index d64a5e6553..dc5ade1dc2 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -23,6 +23,7 @@ #endif #include +#include #include "util/asm.hpp" #include "util/fence.hpp" @@ -73,45 +74,45 @@ namespace Darwin_ProcessInfo namespace utils { #ifdef _WIN32 + // Some helpers for sanity + const auto read_reg_dword = [](HKEY hKey, std::string_view value_name) -> std::pair + { + DWORD val = 0; + DWORD len = sizeof(val); + if (ERROR_SUCCESS != RegQueryValueExA(hKey, value_name.data(), nullptr, nullptr, reinterpret_cast(&val), &len)) + { + return { false, 0 }; + } + return { true, val }; + }; + + const auto read_reg_sz = [](HKEY hKey, std::string_view value_name) -> std::pair + { + constexpr usz MAX_SZ_LEN = 255; + char sz[MAX_SZ_LEN + 1] {}; + DWORD sz_len = MAX_SZ_LEN; + + // Safety; null terminate + sz[0] = 0; + sz[MAX_SZ_LEN] = 0; + + // Read string + if (ERROR_SUCCESS != RegQueryValueExA(hKey, value_name.data(), nullptr, nullptr, reinterpret_cast(sz), &sz_len)) + { + return { false, "" }; + } + + // Safety, force null terminator + if (sz_len < MAX_SZ_LEN) + { + sz[sz_len] = 0; + } + return { true, sz }; + }; + // Alternative way to read OS version using the registry. static std::string get_fallback_windows_version() { - // Some helpers for sanity - const auto read_reg_dword = [](HKEY hKey, std::string_view value_name) -> std::pair - { - DWORD val; - DWORD len = sizeof(val); - if (ERROR_SUCCESS != RegQueryValueExA(hKey, value_name.data(), nullptr, nullptr, reinterpret_cast(&val), &len)) - { - return { false, 0 }; - } - return { true, val }; - }; - - const auto read_reg_sz = [](HKEY hKey, std::string_view value_name) -> std::pair - { - constexpr usz MAX_SZ_LEN = 255; - char sz[MAX_SZ_LEN + 1]; - DWORD sz_len = MAX_SZ_LEN; - - // Safety; null terminate - sz[0] = 0; - sz[MAX_SZ_LEN] = 0; - - // Read string - if (ERROR_SUCCESS != RegQueryValueExA(hKey, value_name.data(), nullptr, nullptr, reinterpret_cast(sz), &sz_len)) - { - return { false, "" }; - } - - // Safety, force null terminator - if (sz_len < MAX_SZ_LEN) - { - sz[sz_len] = 0; - } - return { true, sz }; - }; - HKEY hKey; if (ERROR_SUCCESS != RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey)) { @@ -524,7 +525,7 @@ std::string utils::get_system_info() } else { - fmt::append(result, " | TSC: Bad"); + fmt::append(result, " | TSC: Disabled"); } if (has_avx()) @@ -660,14 +661,88 @@ std::string utils::get_firmware_version() return {}; } -std::string utils::get_OS_version() +utils::OS_version utils::get_OS_version() +{ + OS_version res {}; + +#if _WIN32 + res.type = "windows"; +#elif __linux__ + res.type = "linux"; +#elif __APPLE__ + res.type = "macos"; +#elif __FreeBSD__ + res.type = "freebsd"; +#else + res.type = "unknown"; +#endif + +#if defined(ARCH_X64) + res.arch = "x64"; +#elif defined(ARCH_ARM64) + res.arch = "arm64"; +#else + res.arch = "unknown"; +#endif + +#ifdef _WIN32 + // GetVersionEx is deprecated, RtlGetVersion is kernel-mode only and AnalyticsInfo is UWP only. + // So we're forced to read PEB instead to get Windows version info. It's ugly but works. +#if defined(ARCH_X64) + constexpr DWORD peb_offset = 0x60; + const INT_PTR peb = __readgsqword(peb_offset); + res.version_major = *reinterpret_cast(peb + 0x118); + res.version_minor = *reinterpret_cast(peb + 0x11c); + res.version_patch = *reinterpret_cast(peb + 0x120); +#else + HKEY hKey; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + const auto [check_major, version_major] = read_reg_dword(hKey, "CurrentMajorVersionNumber"); + const auto [check_minor, version_minor] = read_reg_dword(hKey, "CurrentMinorVersionNumber"); + const auto [check_build, version_patch] = read_reg_sz(hKey, "CurrentBuildNumber"); + + if (check_major) res.version_major = version_major; + if (check_minor) res.version_minor = version_minor; + if (check_build) res.version_patch = stoi(version_patch); + + RegCloseKey(hKey); + } +#endif +#elif defined (__APPLE__) + res.version_major = Darwin_Version::getNSmajorVersion(); + res.version_minor = Darwin_Version::getNSminorVersion(); + res.version_patch = Darwin_Version::getNSpatchVersion(); +#else + if (struct utsname details = {}; !uname(&details)) + { + const std::vector version_list = fmt::split(details.release, { "." }); + const auto get_version_part = [&version_list](usz i) -> usz + { + if (version_list.size() <= i) return 0; + if (const auto [success, version_part] = string_to_number(version_list[i]); success) + { + return version_part; + } + return 0; + }; + res.version_major = get_version_part(0); + res.version_minor = get_version_part(1); + res.version_patch = get_version_part(2); + } +#endif + + return res; +} + +std::string utils::get_OS_version_string() { std::string output; #ifdef _WIN32 // GetVersionEx is deprecated, RtlGetVersion is kernel-mode only and AnalyticsInfo is UWP only. // So we're forced to read PEB instead to get Windows version info. It's ugly but works. #if defined(ARCH_X64) - const DWORD peb_offset = 0x60; + constexpr DWORD peb_offset = 0x60; const INT_PTR peb = __readgsqword(peb_offset); const DWORD version_major = *reinterpret_cast(peb + 0x118); const DWORD version_minor = *reinterpret_cast(peb + 0x11c); @@ -772,18 +847,68 @@ static const bool s_tsc_freq_evaluated = []() -> bool #endif if (!utils::has_invariant_tsc()) + { return 0; + } #ifdef _WIN32 LARGE_INTEGER freq; if (!QueryPerformanceFrequency(&freq)) + { return 0; + } if (freq.QuadPart <= 9'999'999) + { return 0; + } const ullong timer_freq = freq.QuadPart; #else + +#ifdef __linux__ + // Check if system clocksource is TSC. If the kernel trusts the TSC, we should too. + // Some Ryzen laptops have broken firmware when running linux (requires a kernel patch). This is also a problem on some older intel CPUs. + const char* clocksource_file = "/sys/devices/system/clocksource/clocksource0/available_clocksource"; + if (!fs::is_file(clocksource_file)) + { + // OS doesn't support sysfs? + printf("[TSC calibration] Could not determine available clock sources. Disabling TSC.\n"); + return 0; + } + + std::string clock_sources; + std::ifstream file(clocksource_file); + std::getline(file, clock_sources); + + if (file.fail()) + { + printf("[TSC calibration] Could not read the available clock sources on this system. Disabling TSC.\n"); + return 0; + } + + printf("[TSC calibration] Available clock sources: '%s'\n", clock_sources.c_str()); + + // Check if the Kernel has blacklisted the TSC + const auto available_clocks = fmt::split(clock_sources, { " " }); + const bool tsc_reliable = std::find(available_clocks.begin(), available_clocks.end(), "tsc") != available_clocks.end(); + + if (!tsc_reliable) + { + printf("[TSC calibration] TSC is not a supported clock source on this system.\n"); + return 0; + } + + printf("[TSC calibration] Kernel reports the TSC is reliable.\n"); +#else + if (utils::get_cpu_brand().find("Ryzen") != umax) + { + // MacOS is arm-native these days and I don't know much about BSD to fix this if it's an issue. (kd-11) + // Having this check only for Ryzen is broken behavior - other CPUs can also have this problem. + return 0; + } +#endif + constexpr ullong timer_freq = 1'000'000'000; #endif @@ -880,7 +1005,7 @@ static const bool s_tsc_freq_evaluated = []() -> bool return round_tsc(res, utils::mul_saturate(utils::add_saturate(rdtsc_diff[0], rdtsc_diff[1]), utils::aligned_div(timer_freq, timer_data[1] - timer_data[0]))); }(); - atomic_storage::release(utils::s_tsc_freq, cal_tsc); + atomic_storage::store(utils::s_tsc_freq, cal_tsc); return true; }(); @@ -963,10 +1088,20 @@ u32 utils::get_cpu_model() #endif } -namespace utils +u64 utils::_get_main_tid() { - u64 _get_main_tid() - { - return thread_ctrl::get_tid(); - } + return thread_ctrl::get_tid(); +} + +std::pair utils::string_to_number(std::string_view str) +{ + std::add_pointer_t eval; + const usz number = std::strtol(str.data(), &eval, 10); + + if (str.data() + str.size() == eval) + { + return { true, number }; + } + + return { false, 0 }; } diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index 4081b3a9cf..ff78288b15 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "util/types.hpp" #include @@ -67,7 +67,17 @@ namespace utils std::string get_firmware_version(); - std::string get_OS_version(); + struct OS_version + { + std::string type; + std::string arch; + int version_major = 0; + int version_minor = 0; + int version_patch = 0; + }; + OS_version get_OS_version(); + + std::string get_OS_version_string(); int get_maxfiles(); @@ -94,4 +104,6 @@ namespace utils { return s_tsc_freq; } + + std::pair string_to_number(std::string_view str); } diff --git a/rpcs3/util/typeindices.hpp b/rpcs3/util/typeindices.hpp index cb9b3b2748..c6f6190ba7 100644 --- a/rpcs3/util/typeindices.hpp +++ b/rpcs3/util/typeindices.hpp @@ -1,9 +1,6 @@ #pragma once #include "util/types.hpp" -#include "util/shared_ptr.hpp" - -#include #ifndef _MSC_VER #define ATTR_PURE __attribute__((pure)) diff --git a/rpcs3/util/video_provider.cpp b/rpcs3/util/video_provider.cpp index 1e5b747d5c..70888447f4 100644 --- a/rpcs3/util/video_provider.cpp +++ b/rpcs3/util/video_provider.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "video_provider.h" #include "Emu/RSX/Overlays/overlay_message.h" +#include "Emu/Cell/timers.hpp" extern "C" { diff --git a/rpcs3/util/vm_native.cpp b/rpcs3/util/vm_native.cpp index b57974f0d7..1aac9c1026 100644 --- a/rpcs3/util/vm_native.cpp +++ b/rpcs3/util/vm_native.cpp @@ -1,5 +1,4 @@ #include "stdafx.h" -#include "util/logs.hpp" #include "util/vm.hpp" #include "util/asm.hpp" #ifdef _WIN32 @@ -310,6 +309,11 @@ namespace utils void memory_commit(void* pointer, usz size, protection prot) { + if (!size) + { + return; + } + #ifdef _WIN32 ensure(::VirtualAlloc(pointer, size, MEM_COMMIT, +prot)); #else @@ -329,6 +333,11 @@ namespace utils void memory_decommit(void* pointer, usz size) { + if (!size) + { + return; + } + #ifdef _WIN32 ensure(::VirtualFree(pointer, size, MEM_DECOMMIT)); #else @@ -357,6 +366,11 @@ namespace utils void memory_reset(void* pointer, usz size, protection prot) { + if (!size) + { + return; + } + #ifdef _WIN32 memory_decommit(pointer, size); memory_commit(pointer, size, prot); @@ -390,6 +404,11 @@ namespace utils void memory_release(void* pointer, usz size) { + if (!size) + { + return; + } + #ifdef _WIN32 unmap_mappping_memory(reinterpret_cast(pointer), size); ensure(::VirtualFree(pointer, 0, MEM_RELEASE)); @@ -400,6 +419,11 @@ namespace utils void memory_protect(void* pointer, usz size, protection prot) { + if (!size) + { + return; + } + #ifdef _WIN32 DWORD old; @@ -429,6 +453,11 @@ namespace utils bool memory_lock(void* pointer, usz size) { + if (!size) + { + return true; + } + #ifdef _WIN32 return ::VirtualLock(pointer, size); #else