mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-05-07 13:37:46 +00:00
Merge branch 'master' into windows-clang
This commit is contained in:
commit
931bafcf87
181 changed files with 7324 additions and 1567 deletions
|
|
@ -6,8 +6,9 @@ export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
|
|||
export HOMEBREW_NO_ENV_HINTS=1
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
|
||||
brew install -f --overwrite --quiet pipenv googletest opencv@4 ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers
|
||||
brew install -f --overwrite --quiet googletest opencv@4 ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers vulkan-loader
|
||||
brew unlink --quiet ffmpeg qtbase qtsvg qtdeclarative
|
||||
|
||||
brew link -f --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5
|
||||
|
||||
# moltenvk based on commit for 1.4.0 release
|
||||
|
|
@ -42,11 +43,10 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
|
|||
sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader
|
||||
sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
|
||||
cd "/tmp/Qt"
|
||||
"$BREW_PATH/bin/pipenv" run pip3 uninstall py7zr requests semantic_version lxml
|
||||
"$BREW_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml --no-cache
|
||||
pip3 install py7zr requests semantic_version lxml --no-cache --break-system-packages
|
||||
mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64"
|
||||
sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader"
|
||||
"$BREW_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats -o "$QT_VER/clang_64"
|
||||
python3 "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats -o "$QT_VER/clang_64"
|
||||
fi
|
||||
|
||||
cd "$WORKDIR"
|
||||
|
|
@ -57,15 +57,14 @@ export SDL3_DIR="$BREW_PATH/opt/sdl3/lib/cmake/SDL3"
|
|||
|
||||
export PATH="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH"
|
||||
export LDFLAGS="-L$BREW_PATH/lib $BREW_PATH/opt/ffmpeg@5/lib/libavcodec.dylib $BREW_PATH/opt/ffmpeg@5/lib/libavformat.dylib $BREW_PATH/opt/ffmpeg@5/lib/libavutil.dylib $BREW_PATH/opt/ffmpeg@5/lib/libswscale.dylib $BREW_PATH/opt/ffmpeg@5/lib/libswresample.dylib $BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++.1.dylib $BREW_PATH/lib/libSDL3.dylib $BREW_PATH/lib/libGLEW.dylib $BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib -Wl,-rpath,$BREW_PATH/lib"
|
||||
export CPPFLAGS="-I$BREW_PATH/include -I$BREW_PATH/include -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export CPPFLAGS="-I$BREW_PATH/include -D__MAC_OS_X_VERSION_MIN_REQUIRED=144000"
|
||||
export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=144000"
|
||||
export LIBRARY_PATH="$BREW_PATH/lib"
|
||||
export LD_LIBRARY_PATH="$BREW_PATH/lib"
|
||||
|
||||
export VULKAN_SDK
|
||||
VULKAN_SDK="$BREW_PATH/opt/molten-vk"
|
||||
ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib" || true
|
||||
export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json"
|
||||
ln -s "$BREW_PATH/opt/vulkan-loader/lib/libvulkan.dylib" "$VULKAN_SDK/lib/libvulkan.dylib" || true
|
||||
|
||||
export LLVM_DIR
|
||||
LLVM_DIR="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER"
|
||||
|
|
@ -73,12 +72,9 @@ LLVM_DIR="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER"
|
|||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/opencv/ && !/SDL/ && !/feralinteractive/ { print $3 }' .gitmodules)
|
||||
|
||||
# 3rdparty fixes
|
||||
sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c
|
||||
|
||||
mkdir build && cd build || exit 1
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=14.0
|
||||
export MACOSX_DEPLOYMENT_TARGET=14.4
|
||||
|
||||
"$BREW_PATH/bin/cmake" .. \
|
||||
-DBUILD_RPCS3_TESTS=OFF \
|
||||
|
|
@ -110,7 +106,7 @@ export MACOSX_DEPLOYMENT_TARGET=14.0
|
|||
-DCMAKE_OSX_ARCHITECTURES=arm64 \
|
||||
-DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \
|
||||
-DCMAKE_IGNORE_PREFIX_PATH=/opt/homebrew/opt \
|
||||
-DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \
|
||||
-DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=144000" \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \
|
||||
-G Ninja
|
||||
|
|
|
|||
|
|
@ -6,13 +6,11 @@ export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
|
|||
export HOMEBREW_NO_ENV_HINTS=1
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
|
||||
brew install -f --overwrite --quiet ccache pipenv "llvm@$LLVM_COMPILER_VER"
|
||||
brew install -f --overwrite --quiet ccache "llvm@$LLVM_COMPILER_VER"
|
||||
brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER"
|
||||
# shellcheck disable=SC3009
|
||||
rm /usr/local/bin/{idle3.14,pip3.14,pydoc3.14,python3.14,python3.14-config} && \
|
||||
rm /usr/local/bin/{idle3,pip3,pydoc3,python3,python3-config}
|
||||
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet opencv@4 ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers
|
||||
arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet python@3.14 opencv@4 ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers vulkan-loader
|
||||
arch -x86_64 /usr/local/bin/brew unlink --quiet ffmpeg qtbase qtsvg qtdeclarative
|
||||
arch -x86_64 /usr/local/bin/brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5
|
||||
|
||||
|
|
@ -42,10 +40,10 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
|
|||
sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader
|
||||
sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
|
||||
cd "/tmp/Qt"
|
||||
"/opt/homebrew/bin/pipenv" --python "/opt/homebrew/bin/python3" run pip3 install py7zr requests semantic_version lxml
|
||||
pip3 install py7zr requests semantic_version lxml --no-cache --break-system-packages
|
||||
mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64"
|
||||
sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader"
|
||||
"/opt/homebrew/bin/pipenv" --python "/opt/homebrew/bin/python3" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats -o "$QT_VER/clang_64"
|
||||
python3 "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats -o "$QT_VER/clang_64"
|
||||
fi
|
||||
|
||||
cd "$WORKDIR"
|
||||
|
|
@ -57,15 +55,14 @@ export SDL3_DIR="$BREW_X64_PATH/opt/sdl3/lib/cmake/SDL3"
|
|||
export PATH="/opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH"
|
||||
# shellcheck disable=SC2155
|
||||
export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib,-L$(brew --prefix llvm)/lib/c++"
|
||||
export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000"
|
||||
export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -D__MAC_OS_X_VERSION_MIN_REQUIRED=144000"
|
||||
export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=144000"
|
||||
export LIBRARY_PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib:$BREW_X64_PATH/lib"
|
||||
export LD_LIBRARY_PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib:$BREW_X64_PATH/lib"
|
||||
|
||||
export VULKAN_SDK
|
||||
VULKAN_SDK="$BREW_X64_PATH/opt/molten-vk"
|
||||
ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib"
|
||||
export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json"
|
||||
ln -s "$BREW_X64_PATH/opt/vulkan-loader/lib/libvulkan.dylib" "$VULKAN_SDK/lib/libvulkan.dylib"
|
||||
|
||||
export LLVM_DIR
|
||||
LLVM_DIR="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER"
|
||||
|
|
@ -73,12 +70,9 @@ LLVM_DIR="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER"
|
|||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/opencv/ && !/SDL/ && !/feralinteractive/ { print $3 }' .gitmodules)
|
||||
|
||||
# 3rdparty fixes
|
||||
sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c
|
||||
|
||||
mkdir build && cd build || exit 1
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=14.0
|
||||
export MACOSX_DEPLOYMENT_TARGET=14.4
|
||||
|
||||
"/opt/homebrew/bin/cmake" .. \
|
||||
-DBUILD_RPCS3_TESTS=OFF \
|
||||
|
|
@ -112,7 +106,7 @@ export MACOSX_DEPLOYMENT_TARGET=14.0
|
|||
-DCMAKE_TOOLCHAIN_FILE=buildfiles/cmake/TCDarwinX86_64.cmake \
|
||||
-DCMAKE_IGNORE_PATH="$BREW_X64_PATH/lib" \
|
||||
-DCMAKE_IGNORE_PREFIX_PATH=/usr/local/opt \
|
||||
-DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \
|
||||
-DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=144000" \
|
||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \
|
||||
-G Ninja
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ echo "AVVER=$AVVER" >> ../.ci/ci-vars.env
|
|||
|
||||
cd bin
|
||||
mkdir "rpcs3.app/Contents/lib/" || true
|
||||
mkdir -p "rpcs3.app/Contents/Resources/vulkan/icd.d" || true
|
||||
wget https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.1/MoltenVK-macos-privateapi.tar
|
||||
tar -xvf MoltenVK-macos-privateapi.tar
|
||||
cp "MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" "rpcs3.app/Contents/Frameworks/libMoltenVK.dylib"
|
||||
cp "MoltenVK/MoltenVK/dynamic/dylib/macOS/MoltenVK_icd.json" "rpcs3.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json"
|
||||
sed -i '' "s/.\//..\/..\/..\/Frameworks\//g" "rpcs3.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json"
|
||||
|
||||
cp "$(realpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib"
|
||||
cp "$(realpath /opt/homebrew/opt/gcc/lib/gcc/current/libgcc_s.1.1.dylib)" "rpcs3.app/Contents/Frameworks/libgcc_s.1.1.dylib"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,13 @@ AVVER="${COMM_TAG}-${COMM_COUNT}"
|
|||
echo "AVVER=$AVVER" >> ../.ci/ci-vars.env
|
||||
|
||||
cd bin
|
||||
mkdir "rpcs3.app/Contents/lib/"
|
||||
mkdir "rpcs3.app/Contents/lib/" || true
|
||||
mkdir -p "rpcs3.app/Contents/Resources/vulkan/icd.d" || true
|
||||
wget https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.1/MoltenVK-macos-privateapi.tar
|
||||
tar -xvf MoltenVK-macos-privateapi.tar
|
||||
cp "MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" "rpcs3.app/Contents/Frameworks/libMoltenVK.dylib"
|
||||
cp "MoltenVK/MoltenVK/dynamic/dylib/macOS/MoltenVK_icd.json" "rpcs3.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json"
|
||||
sed -i '' "s/.\//..\/..\/..\/Frameworks\//g" "rpcs3.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json"
|
||||
|
||||
cp "$(realpath /usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib"
|
||||
cp "$(realpath /usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib)" "rpcs3.app/Contents/Frameworks/libunwind.1.dylib"
|
||||
|
|
|
|||
4
3rdparty/7zip/CMakeLists.txt
vendored
4
3rdparty/7zip/CMakeLists.txt
vendored
|
|
@ -59,11 +59,11 @@ if(WIN32 OR APPLE)
|
|||
7zip/C/XzEnc.c
|
||||
7zip/C/XzIn.c
|
||||
7zip/C/ZstdDec.c)
|
||||
target_include_directories(3rdparty_7zip INTERFACE
|
||||
target_include_directories(3rdparty_7zip SYSTEM INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/7zip/C>
|
||||
$<INSTALL_INTERFACE:/7zip/C>)
|
||||
|
||||
target_include_directories(3rdparty_7zip INTERFACE 7zip)
|
||||
target_include_directories(3rdparty_7zip SYSTEM INTERFACE 7zip)
|
||||
|
||||
set_property(TARGET 3rdparty_7zip PROPERTY FOLDER "3rdparty/")
|
||||
|
||||
|
|
|
|||
12
3rdparty/CMakeLists.txt
vendored
12
3rdparty/CMakeLists.txt
vendored
|
|
@ -35,7 +35,7 @@ if (USE_SYSTEM_FLATBUFFERS)
|
|||
message(FATAL_ERROR "flatc failed to regenerate flatbuffers headers.")
|
||||
endif()
|
||||
else()
|
||||
target_include_directories(3rdparty_flatbuffers INTERFACE flatbuffers/include)
|
||||
target_include_directories(3rdparty_flatbuffers SYSTEM INTERFACE flatbuffers/include)
|
||||
endif()
|
||||
|
||||
# libPNG
|
||||
|
|
@ -56,7 +56,7 @@ if (USE_SYSTEM_VULKAN_MEMORY_ALLOCATOR)
|
|||
add_library(3rdparty::vulkanmemoryallocator ALIAS GPUOpen::VulkanMemoryAllocator)
|
||||
else()
|
||||
add_library(3rdparty_vulkanmemoryallocator INTERFACE)
|
||||
target_include_directories(3rdparty_vulkanmemoryallocator INTERFACE GPUOpen/VulkanMemoryAllocator/include)
|
||||
target_include_directories(3rdparty_vulkanmemoryallocator SYSTEM INTERFACE GPUOpen/VulkanMemoryAllocator/include)
|
||||
add_library(3rdparty::vulkanmemoryallocator ALIAS 3rdparty_vulkanmemoryallocator)
|
||||
endif()
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ if (NOT ANDROID)
|
|||
find_package(OpenGL REQUIRED OPTIONAL_COMPONENTS EGL)
|
||||
|
||||
add_library(3rdparty_opengl INTERFACE)
|
||||
target_include_directories(3rdparty_opengl INTERFACE GL)
|
||||
target_include_directories(3rdparty_opengl SYSTEM INTERFACE GL)
|
||||
|
||||
if (WIN32)
|
||||
if(NOT MSVC)
|
||||
|
|
@ -204,7 +204,7 @@ if(USE_VULKAN)
|
|||
find_package(Wayland)
|
||||
if (WAYLAND_FOUND)
|
||||
target_include_directories(3rdparty_vulkan
|
||||
INTERFACE ${WAYLAND_INCLUDE_DIR})
|
||||
SYSTEM INTERFACE ${WAYLAND_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
@ -298,7 +298,7 @@ if(NOT ANDROID)
|
|||
message(STATUS "RPCS3: using shared ffmpeg")
|
||||
find_package(FFMPEG REQUIRED)
|
||||
|
||||
target_include_directories(3rdparty_ffmpeg INTERFACE ${FFMPEG_INCLUDE_DIR})
|
||||
target_include_directories(3rdparty_ffmpeg SYSTEM INTERFACE ${FFMPEG_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_ffmpeg INTERFACE ${FFMPEG_LIBRARIES})
|
||||
else()
|
||||
message(STATUS "RPCS3: using builtin ffmpeg")
|
||||
|
|
@ -328,7 +328,7 @@ if(NOT ANDROID)
|
|||
${FFMPEG_LIB_SWSCALE}
|
||||
${FFMPEG_LIB_SWRESAMPLE}
|
||||
)
|
||||
target_include_directories(3rdparty_ffmpeg INTERFACE "ffmpeg/include")
|
||||
target_include_directories(3rdparty_ffmpeg SYSTEM INTERFACE "ffmpeg/include")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
|||
4
3rdparty/SoundTouch/CMakeLists.txt
vendored
4
3rdparty/SoundTouch/CMakeLists.txt
vendored
|
|
@ -11,11 +11,11 @@ add_library(soundtouch STATIC EXCLUDE_FROM_ALL
|
|||
soundtouch/source/SoundTouch/TDStretch.cpp
|
||||
)
|
||||
|
||||
target_include_directories(soundtouch PRIVATE
|
||||
target_include_directories(soundtouch SYSTEM PRIVATE
|
||||
soundtouch/source/SoundTouch
|
||||
soundtouch/include)
|
||||
|
||||
target_include_directories(soundtouch INTERFACE
|
||||
target_include_directories(soundtouch SYSTEM INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/soundtouch/include>
|
||||
$<INSTALL_INTERFACE:/soundtouch/include>)
|
||||
|
||||
|
|
|
|||
2
3rdparty/asmjit/CMakeLists.txt
vendored
2
3rdparty/asmjit/CMakeLists.txt
vendored
|
|
@ -9,7 +9,7 @@ set(ASMJIT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/asmjit" CACHE PATH "Location of 'asm
|
|||
include("${ASMJIT_DIR}/CMakeLists.txt")
|
||||
|
||||
add_library(asmjit ${ASMJIT_SRC})
|
||||
target_include_directories(asmjit PUBLIC ${ASMJIT_DIR}/src)
|
||||
target_include_directories(asmjit SYSTEM PUBLIC ${ASMJIT_DIR}/src)
|
||||
target_link_libraries(asmjit PRIVATE ${ASMJIT_DEPS})
|
||||
target_compile_options(asmjit PRIVATE -Wno-nontrivial-memcall -Wno-deprecated-anon-enum-enum-conversion)
|
||||
|
||||
|
|
|
|||
2
3rdparty/discord-rpc/CMakeLists.txt
vendored
2
3rdparty/discord-rpc/CMakeLists.txt
vendored
|
|
@ -9,7 +9,7 @@ if (USE_DISCORD_RPC AND (WIN32 OR CMAKE_SYSTEM MATCHES "Linux" OR APPLE))
|
|||
set(WARNINGS_AS_ERRORS FALSE CACHE BOOL "When enabled, compiles with `-Werror` (on *nix platforms).")
|
||||
|
||||
add_subdirectory(discord-rpc EXCLUDE_FROM_ALL)
|
||||
target_include_directories(3rdparty_discordRPC INTERFACE discord-rpc/include)
|
||||
target_include_directories(3rdparty_discordRPC SYSTEM INTERFACE discord-rpc/include)
|
||||
target_compile_definitions(3rdparty_discordRPC INTERFACE -DWITH_DISCORD_RPC)
|
||||
target_link_libraries(3rdparty_discordRPC INTERFACE discord-rpc)
|
||||
endif()
|
||||
|
|
|
|||
2
3rdparty/feralinteractive/CMakeLists.txt
vendored
2
3rdparty/feralinteractive/CMakeLists.txt
vendored
|
|
@ -3,7 +3,7 @@
|
|||
add_library(3rdparty_feralinteractive INTERFACE)
|
||||
|
||||
if (CMAKE_SYSTEM MATCHES "Linux")
|
||||
target_include_directories(3rdparty_feralinteractive INTERFACE feralinteractive/lib)
|
||||
target_include_directories(3rdparty_feralinteractive SYSTEM INTERFACE feralinteractive/lib)
|
||||
target_compile_definitions(3rdparty_feralinteractive INTERFACE -DGAMEMODE_AVAILABLE)
|
||||
target_link_libraries(3rdparty_feralinteractive INTERFACE feralinteractive)
|
||||
endif()
|
||||
|
|
|
|||
2
3rdparty/flatbuffers
vendored
2
3rdparty/flatbuffers
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 595bf0007ab1929570c7671f091313c8fc20644e
|
||||
Subproject commit 187240970746d00bbd26b0f5873ed54d2477f9f3
|
||||
2
3rdparty/fusion/fusion
vendored
2
3rdparty/fusion/fusion
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 759ac5d698baefca53f1975a0bb1d2dcbdb9f836
|
||||
Subproject commit 008e03eac0ac1d5f85e16f5fcaefdda3fee75cb8
|
||||
2
3rdparty/glslang/CMakeLists.txt
vendored
2
3rdparty/glslang/CMakeLists.txt
vendored
|
|
@ -7,7 +7,7 @@ if(USE_SYSTEM_GLSLANG)
|
|||
target_link_libraries(3rdparty_glslang INTERFACE glslang::SPIRV)
|
||||
get_target_property(SPIRV_INCLUDE_DIRS glslang::SPIRV INTERFACE_INCLUDE_DIRECTORIES)
|
||||
list(TRANSFORM SPIRV_INCLUDE_DIRS APPEND "/glslang")
|
||||
target_include_directories(3rdparty_glslang INTERFACE ${SPIRV_INCLUDE_DIRS})
|
||||
target_include_directories(3rdparty_glslang SYSTEM INTERFACE ${SPIRV_INCLUDE_DIRS})
|
||||
else()
|
||||
set(ENABLE_PCH OFF CACHE BOOL "Enables Precompiled header" FORCE)
|
||||
set(BUILD_EXTERNAL OFF CACHE BOOL "Build external dependencies in /External" FORCE)
|
||||
|
|
|
|||
2
3rdparty/hidapi/CMakeLists.txt
vendored
2
3rdparty/hidapi/CMakeLists.txt
vendored
|
|
@ -4,7 +4,7 @@ if(USE_SYSTEM_HIDAPI)
|
|||
pkg_check_modules(hidapi-hidraw REQUIRED IMPORTED_TARGET hidapi-hidraw)
|
||||
add_library(3rdparty_hidapi INTERFACE)
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE PkgConfig::hidapi-hidraw)
|
||||
target_include_directories(3rdparty_hidapi INTERFACE PkgConfig::hidapi-hidraw)
|
||||
target_include_directories(3rdparty_hidapi SYSTEM INTERFACE PkgConfig::hidapi-hidraw)
|
||||
else()
|
||||
set(BUILD_SHARED_LIBS FALSE CACHE BOOL "Don't build shared libs")
|
||||
set(HIDAPI_INSTALL_TARGETS FALSE CACHE BOOL "Don't install anything")
|
||||
|
|
|
|||
4
3rdparty/libpng/CMakeLists.txt
vendored
4
3rdparty/libpng/CMakeLists.txt
vendored
|
|
@ -6,14 +6,14 @@ if (NOT USE_SYSTEM_LIBPNG)
|
|||
set(PNG_TESTS OFF CACHE BOOL "Build libpng tests")
|
||||
set(SKIP_INSTALL_ALL ON)
|
||||
add_subdirectory(libpng EXCLUDE_FROM_ALL)
|
||||
target_include_directories(png_static INTERFACE "${libpng_BINARY_DIR}" "${libpng_SOURCE_DIR}")
|
||||
target_include_directories(png_static SYSTEM INTERFACE "${libpng_BINARY_DIR}" "${libpng_SOURCE_DIR}")
|
||||
|
||||
set(LIBPNG_TARGET png_static PARENT_SCOPE)
|
||||
else()
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
add_library(3rdparty_system_libpng INTERFACE)
|
||||
target_include_directories(3rdparty_system_libpng INTERFACE ${PNG_INCLUDE_DIR})
|
||||
target_include_directories(3rdparty_system_libpng SYSTEM INTERFACE ${PNG_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_system_libpng INTERFACE ${PNG_LIBRARY})
|
||||
target_compile_definitions(3rdparty_system_libpng INTERFACE ${PNG_DEFINITIONS})
|
||||
|
||||
|
|
|
|||
2
3rdparty/llvm/CMakeLists.txt
vendored
2
3rdparty/llvm/CMakeLists.txt
vendored
|
|
@ -107,7 +107,7 @@ if(WITH_LLVM)
|
|||
|
||||
add_library(3rdparty_llvm INTERFACE)
|
||||
target_link_libraries(3rdparty_llvm INTERFACE ${LLVM_LIBS})
|
||||
target_include_directories(3rdparty_llvm INTERFACE ${LLVM_INCLUDE_DIRS})
|
||||
target_include_directories(3rdparty_llvm SYSTEM INTERFACE ${LLVM_INCLUDE_DIRS})
|
||||
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
|
||||
target_compile_definitions(3rdparty_llvm INTERFACE ${LLVM_DEFINITIONS_LIST} LLVM_AVAILABLE)
|
||||
|
||||
|
|
|
|||
6
3rdparty/miniupnp/CMakeLists.txt
vendored
6
3rdparty/miniupnp/CMakeLists.txt
vendored
|
|
@ -3,9 +3,9 @@ if(USE_SYSTEM_MINIUPNPC)
|
|||
pkg_check_modules(MiniUPnPc REQUIRED IMPORTED_TARGET miniupnpc>=2.3.3)
|
||||
add_library(3rdparty_miniupnpc INTERFACE)
|
||||
target_link_libraries(3rdparty_miniupnpc INTERFACE PkgConfig::MiniUPnPc)
|
||||
target_include_directories(3rdparty_miniupnpc INTERFACE PkgConfig::MiniUPnPc)
|
||||
target_include_directories(3rdparty_miniupnpc SYSTEM INTERFACE PkgConfig::MiniUPnPc)
|
||||
list(TRANSFORM MiniUPnPc_INCLUDE_DIRS APPEND "/miniupnpc")
|
||||
target_include_directories(3rdparty_miniupnpc INTERFACE ${MiniUPnPc_INCLUDE_DIRS})
|
||||
target_include_directories(3rdparty_miniupnpc SYSTEM INTERFACE ${MiniUPnPc_INCLUDE_DIRS})
|
||||
else()
|
||||
option (UPNPC_BUILD_STATIC "Build static library" TRUE)
|
||||
option (UPNPC_BUILD_SHARED "Build shared library" FALSE)
|
||||
|
|
@ -17,5 +17,5 @@ else()
|
|||
add_subdirectory(miniupnp/miniupnpc EXCLUDE_FROM_ALL)
|
||||
add_library(3rdparty_miniupnpc INTERFACE)
|
||||
target_link_libraries(3rdparty_miniupnpc INTERFACE libminiupnpc-static)
|
||||
target_include_directories(3rdparty_miniupnpc INTERFACE libminiupnpc-static)
|
||||
target_include_directories(3rdparty_miniupnpc SYSTEM INTERFACE libminiupnpc-static)
|
||||
endif()
|
||||
|
|
|
|||
2
3rdparty/rtmidi/CMakeLists.txt
vendored
2
3rdparty/rtmidi/CMakeLists.txt
vendored
|
|
@ -3,7 +3,7 @@ if(USE_SYSTEM_RTMIDI)
|
|||
pkg_check_modules(RtMidi REQUIRED IMPORTED_TARGET rtmidi>=6.0.0)
|
||||
add_library(rtmidi INTERFACE)
|
||||
target_link_libraries(rtmidi INTERFACE PkgConfig::RtMidi)
|
||||
target_include_directories(rtmidi INTERFACE PkgConfig::RtMidi)
|
||||
target_include_directories(rtmidi SYSTEM INTERFACE PkgConfig::RtMidi)
|
||||
else()
|
||||
option(RTMIDI_API_JACK "Compile with JACK support." OFF)
|
||||
option(RTMIDI_BUILD_TESTING "Build test programs" OFF)
|
||||
|
|
|
|||
2
3rdparty/stblib/CMakeLists.txt
vendored
2
3rdparty/stblib/CMakeLists.txt
vendored
|
|
@ -1,2 +1,2 @@
|
|||
add_library(3rdparty_stblib INTERFACE)
|
||||
target_include_directories(3rdparty_stblib INTERFACE stb)
|
||||
target_include_directories(3rdparty_stblib SYSTEM INTERFACE stb)
|
||||
|
|
|
|||
2
3rdparty/zlib/CMakeLists.txt
vendored
2
3rdparty/zlib/CMakeLists.txt
vendored
|
|
@ -13,6 +13,6 @@ else()
|
|||
|
||||
add_library(3rdparty_zlib INTERFACE)
|
||||
target_link_libraries(3rdparty_zlib INTERFACE zlibstatic)
|
||||
target_include_directories(3rdparty_zlib INTERFACE zlib ${CMAKE_CURRENT_BINARY_DIR}/zlib)
|
||||
target_include_directories(3rdparty_zlib SYSTEM INTERFACE zlib ${CMAKE_CURRENT_BINARY_DIR}/zlib)
|
||||
target_compile_definitions(3rdparty_zlib INTERFACE -DZLIB_CONST=1)
|
||||
endif()
|
||||
|
|
|
|||
2
3rdparty/zstd/CMakeLists.txt
vendored
2
3rdparty/zstd/CMakeLists.txt
vendored
|
|
@ -3,7 +3,7 @@ if(USE_SYSTEM_ZSTD)
|
|||
pkg_check_modules(zstd REQUIRED IMPORTED_TARGET libzstd)
|
||||
add_library(3rdparty_zstd INTERFACE)
|
||||
target_link_libraries(3rdparty_zstd INTERFACE PkgConfig::zstd)
|
||||
target_include_directories(3rdparty_zstd INTERFACE PkgConfig::RtMidi)
|
||||
target_include_directories(3rdparty_zstd SYSTEM INTERFACE PkgConfig::RtMidi)
|
||||
else()
|
||||
option(ZSTD_BUILD_PROGRAMS "BUILD PROGRAMS" OFF)
|
||||
option(ZSTD_BUILD_SHARED "BUILD SHARED LIBRARIES" OFF)
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ std::string fmt::win_error_to_string(unsigned long error, void* module_handle)
|
|||
if (FormatMessageW((module_handle ? FORMAT_MESSAGE_FROM_HMODULE : FORMAT_MESSAGE_FROM_SYSTEM) | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
module_handle, error, 0, reinterpret_cast<LPWSTR>(&message_buffer), 0, nullptr))
|
||||
{
|
||||
message = fmt::format("%s (0x%x)", fmt::trim(wchar_to_utf8(message_buffer), " \t\n\r\f\v"), error);
|
||||
const std::string utf8 = wchar_to_utf8(message_buffer);
|
||||
message = fmt::format("%s (0x%x)", fmt::trim_sv(utf8, " \t\n\r\f\v"), error);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -823,6 +824,50 @@ std::vector<std::string> fmt::split(std::string_view source, std::initializer_li
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> fmt::split_sv(std::string_view source, std::initializer_list<std::string_view> separators, bool is_skip_empty)
|
||||
{
|
||||
std::vector<std::string_view> result;
|
||||
|
||||
for (usz index = 0; index < source.size();)
|
||||
{
|
||||
usz pos = -1;
|
||||
usz sep_size = 0;
|
||||
|
||||
for (auto& separator : separators)
|
||||
{
|
||||
if (usz pos0 = source.find(separator, index); pos0 < pos)
|
||||
{
|
||||
pos = pos0;
|
||||
sep_size = separator.size();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sep_size)
|
||||
{
|
||||
result.emplace_back(&source[index], source.size() - index);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string_view piece = {&source[index], pos - index};
|
||||
|
||||
index = pos + sep_size;
|
||||
|
||||
if (piece.empty() && is_skip_empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.emplace_back(std::move(piece));
|
||||
}
|
||||
|
||||
if (result.empty() && !is_skip_empty)
|
||||
{
|
||||
result.emplace_back();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fmt::trim(const std::string& source, std::string_view values)
|
||||
{
|
||||
const usz begin = source.find_first_not_of(values);
|
||||
|
|
@ -838,6 +883,21 @@ std::string fmt::trim(const std::string& source, std::string_view values)
|
|||
return source.substr(begin, end + 1 - begin);
|
||||
}
|
||||
|
||||
std::string_view fmt::trim_sv(std::string_view source, std::string_view values)
|
||||
{
|
||||
const usz begin = source.find_first_not_of(values);
|
||||
|
||||
if (begin == source.npos)
|
||||
return {};
|
||||
|
||||
const usz end = source.find_last_not_of(values);
|
||||
|
||||
if (end == source.npos)
|
||||
return source.substr(begin);
|
||||
|
||||
return source.substr(begin, end + 1 - begin);
|
||||
}
|
||||
|
||||
std::string fmt::trim_front(const std::string& source, std::string_view values)
|
||||
{
|
||||
const usz begin = source.find_first_not_of(values);
|
||||
|
|
@ -848,12 +908,32 @@ std::string fmt::trim_front(const std::string& source, std::string_view values)
|
|||
return source.substr(begin);
|
||||
}
|
||||
|
||||
std::string_view fmt::trim_front_sv(std::string_view source, std::string_view values)
|
||||
{
|
||||
const usz begin = source.find_first_not_of(values);
|
||||
|
||||
if (begin == source.npos)
|
||||
return {};
|
||||
|
||||
return source.substr(begin);
|
||||
}
|
||||
|
||||
void fmt::trim_back(std::string& source, std::string_view values)
|
||||
{
|
||||
const usz index = source.find_last_not_of(values);
|
||||
source.resize(index + 1);
|
||||
}
|
||||
|
||||
std::string_view fmt::trim_back_sv(std::string_view source, std::string_view values)
|
||||
{
|
||||
const usz index = source.find_last_not_of(values);
|
||||
if (index == std::string_view::npos)
|
||||
return {};
|
||||
|
||||
source.remove_suffix(source.size() - (index + 1));
|
||||
return source;
|
||||
}
|
||||
|
||||
std::string fmt::to_upper(std::string_view string)
|
||||
{
|
||||
std::string result;
|
||||
|
|
|
|||
|
|
@ -139,57 +139,90 @@ namespace fmt
|
|||
// Splits the string into a vector of strings using the separators. The vector may contain empty strings unless is_skip_empty is true.
|
||||
std::vector<std::string> split(std::string_view source, std::initializer_list<std::string_view> separators, bool is_skip_empty = true);
|
||||
|
||||
// Splits the string_view into a vector of string_views using the separators. The vector may contain empty string_views unless is_skip_empty is true.
|
||||
std::vector<std::string_view> split_sv(std::string_view source, std::initializer_list<std::string_view> separators, bool is_skip_empty = true);
|
||||
|
||||
// Removes all preceding and trailing characters specified by 'values' from 'source'.
|
||||
std::string trim(const std::string& source, std::string_view values = " \t");
|
||||
|
||||
// Removes all preceding and trailing characters specified by 'values' from 'source' and returns the result.
|
||||
std::string_view trim_sv(std::string_view source, std::string_view values = " \t");
|
||||
|
||||
// Removes all preceding characters specified by 'values' from 'source'.
|
||||
std::string trim_front(const std::string& source, std::string_view values = " \t");
|
||||
|
||||
// Removes all preceding characters specified by 'values' from 'source' and returns the result.
|
||||
std::string_view trim_front_sv(std::string_view source, std::string_view values = " \t");
|
||||
|
||||
// Removes all trailing characters specified by 'values' from 'source'.
|
||||
void trim_back(std::string& source, std::string_view values = " \t");
|
||||
|
||||
// Removes all trailing characters specified by 'values' from 'source' and returns the result.
|
||||
std::string_view trim_back_sv(std::string_view source, std::string_view values = " \t");
|
||||
|
||||
template <typename T>
|
||||
std::string merge(const T& source, const std::string& separator)
|
||||
std::string merge(const T& source, std::string_view separator)
|
||||
{
|
||||
if (source.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
usz total = (source.size() - 1) * separator.size();
|
||||
for (const auto& s : source)
|
||||
{
|
||||
total += s.size();
|
||||
}
|
||||
|
||||
std::string result;
|
||||
result.reserve(total);
|
||||
|
||||
auto it = source.begin();
|
||||
auto end = source.end();
|
||||
|
||||
for (--end; it != end; ++it)
|
||||
{
|
||||
result += std::string{*it} + separator;
|
||||
result.append(*it);
|
||||
|
||||
if (!separator.empty())
|
||||
result.append(separator);
|
||||
}
|
||||
|
||||
return result + std::string{source.back()};
|
||||
return result.append(source.back());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string merge(std::initializer_list<T> sources, const std::string& separator)
|
||||
std::string merge(std::initializer_list<T> sources, std::string_view separator)
|
||||
{
|
||||
if (!sources.size())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
usz total = (sources.size() - 1) * separator.size();
|
||||
for (const auto& s : sources)
|
||||
{
|
||||
if (s.empty()) continue;
|
||||
total += s.size() + (s.size() - 1) * separator.size();
|
||||
}
|
||||
|
||||
std::string result;
|
||||
result.reserve(total);
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (const auto& v : sources)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
result = fmt::merge(v, separator);
|
||||
first = false;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
else if (!separator.empty())
|
||||
{
|
||||
result += separator + fmt::merge(v, separator);
|
||||
result.append(separator);
|
||||
}
|
||||
|
||||
result.append(fmt::merge(v, separator));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -2165,10 +2165,17 @@ void thread_base::start()
|
|||
ensure(m_thread);
|
||||
ensure(::ResumeThread(reinterpret_cast<HANDLE>(+m_thread)) != static_cast<DWORD>(-1));
|
||||
#elif defined(__APPLE__)
|
||||
pthread_attr_t stack_size_attr;
|
||||
pthread_attr_init(&stack_size_attr);
|
||||
pthread_attr_setstacksize(&stack_size_attr, 0x800000);
|
||||
ensure(pthread_create(reinterpret_cast<pthread_t*>(&m_thread.raw()), &stack_size_attr, entry_point, this) == 0);
|
||||
pthread_attr_t attrs;
|
||||
struct sched_param sp;
|
||||
memset(&sp, 0, sizeof(struct sched_param));
|
||||
sp.sched_priority=99;
|
||||
pthread_attr_init(&attrs);
|
||||
pthread_attr_setstacksize(&attrs, 0x800000);
|
||||
|
||||
pthread_attr_set_qos_class_np(&attrs, QOS_CLASS_USER_INTERACTIVE, 0);
|
||||
pthread_attr_setschedpolicy(&attrs, SCHED_RR);
|
||||
pthread_attr_setschedparam(&attrs, &sp);
|
||||
ensure(pthread_create(reinterpret_cast<pthread_t*>(&m_thread.raw()), &attrs, entry_point, this) == 0);
|
||||
#else
|
||||
ensure(pthread_create(reinterpret_cast<pthread_t*>(&m_thread.raw()), nullptr, entry_point, this) == 0);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ Intersection (&) and symmetric difference (^) is also available.
|
|||
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Utilities/StrFmt.h"
|
||||
|
||||
template <typename T>
|
||||
concept BitSetEnum = std::is_enum_v<T> && requires(T x)
|
||||
|
|
@ -384,6 +383,9 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct fmt_unveil;
|
||||
|
||||
template <typename T>
|
||||
struct fmt_unveil<bs_t<T>>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ void fmt_class_string<cheat_type>::format(std::string& out, u64 arg)
|
|||
});
|
||||
}
|
||||
|
||||
bool cheat_info::from_str(const std::string& cheat_line)
|
||||
bool cheat_info::from_str(std::string_view cheat_line)
|
||||
{
|
||||
auto cheat_vec = fmt::split(cheat_line, {"@@@"}, false);
|
||||
const auto cheat_vec = fmt::split(cheat_line, {"@@@"}, false);
|
||||
|
||||
s64 val64 = 0;
|
||||
if (cheat_vec.size() != 5 || !try_to_int64(&val64, cheat_vec[2], 0, cheat_type_max - 1))
|
||||
|
|
|
|||
|
|
@ -28,6 +28,6 @@ struct cheat_info
|
|||
u32 offset{};
|
||||
std::string red_script{};
|
||||
|
||||
bool from_str(const std::string& cheat_line);
|
||||
bool from_str(std::string_view cheat_line);
|
||||
std::string to_str() const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#elif __APPLE__
|
||||
#include <os/os_sync_wait_on_address.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
@ -76,21 +78,71 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
|
|||
{
|
||||
#ifdef __linux__
|
||||
return syscall(SYS_futex, uaddr, futex_op, static_cast<int>(val), timeout, nullptr, static_cast<int>(mask));
|
||||
#elif __APPLE__
|
||||
switch (futex_op)
|
||||
{
|
||||
case FUTEX_WAIT_PRIVATE:
|
||||
case FUTEX_WAIT_BITSET_PRIVATE:
|
||||
{
|
||||
if (timeout)
|
||||
{
|
||||
const uint64_t nsec = timeout->tv_nsec + timeout->tv_sec * 1000000000ull;
|
||||
return os_sync_wait_on_address_with_timeout(const_cast<void*>(uaddr), static_cast<uint64_t>(val), sizeof(uint), OS_SYNC_WAIT_ON_ADDRESS_NONE, OS_CLOCK_MACH_ABSOLUTE_TIME, nsec);
|
||||
}
|
||||
else
|
||||
{
|
||||
return os_sync_wait_on_address(const_cast<void*>(uaddr), static_cast<uint64_t>(val), sizeof(uint), OS_SYNC_WAIT_ON_ADDRESS_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
case FUTEX_WAKE_PRIVATE:
|
||||
case FUTEX_WAKE_BITSET_PRIVATE:
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
int ret = 0;
|
||||
if (val == INT32_MAX)
|
||||
{
|
||||
ret = os_sync_wake_by_address_all(const_cast<void*>(uaddr), sizeof(uint), OS_SYNC_WAKE_BY_ADDRESS_NONE);
|
||||
}
|
||||
else if (val-- >= 0)
|
||||
{
|
||||
ret = os_sync_wake_by_address_any(const_cast<void*>(uaddr), sizeof(uint), OS_SYNC_WAKE_BY_ADDRESS_NONE);
|
||||
}
|
||||
if (val <= 0 || val == INT32_MAX || (ret < 0 && errno == ENOENT))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
#else
|
||||
static struct futex_manager
|
||||
{
|
||||
struct waiter
|
||||
{
|
||||
uint val;
|
||||
uint mask;
|
||||
std::condition_variable cv;
|
||||
uint val;
|
||||
uint mask;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
std::mutex mutex;
|
||||
std::unordered_multimap<volatile void*, waiter*> map;
|
||||
struct bucket_t
|
||||
{
|
||||
std::mutex mutex;
|
||||
std::unordered_multimap<volatile void*, waiter*> map;
|
||||
};
|
||||
|
||||
// Not a power of 2 on purpose (for alignment optimiations)
|
||||
bucket_t bucks[63];
|
||||
|
||||
int operator()(volatile void* uaddr, int futex_op, uint val, const timespec* timeout, uint mask)
|
||||
{
|
||||
auto& bucket = bucks[(reinterpret_cast<u64>(uaddr) / 8) % std::size(bucks)];
|
||||
auto& mutex = bucket.mutex;
|
||||
auto& map = bucket.map;
|
||||
|
||||
std::unique_lock lock(mutex);
|
||||
|
||||
switch (futex_op)
|
||||
|
|
@ -111,7 +163,9 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
|
|||
waiter rec;
|
||||
rec.val = val;
|
||||
rec.mask = mask;
|
||||
const auto& ref = *map.emplace(uaddr, &rec);
|
||||
|
||||
// Announce the waiter
|
||||
map.emplace(uaddr, &rec);
|
||||
|
||||
int res = 0;
|
||||
|
||||
|
|
@ -127,6 +181,16 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
|
|||
{
|
||||
res = -1;
|
||||
errno = ETIMEDOUT;
|
||||
|
||||
// Cleanup
|
||||
for (auto range = map.equal_range(uaddr); range.first != range.second; range.first++)
|
||||
{
|
||||
if (range.first->second == &rec)
|
||||
{
|
||||
map.erase(range.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -134,7 +198,6 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
|
|||
// TODO: absolute timeout
|
||||
}
|
||||
|
||||
map.erase(std::find(map.find(uaddr), map.end(), ref));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -153,13 +216,29 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
|
|||
|
||||
if (entry.mask & mask)
|
||||
{
|
||||
entry.cv.notify_one();
|
||||
entry.mask = 0;
|
||||
entry.cv.notify_one();
|
||||
res++;
|
||||
val--;
|
||||
}
|
||||
}
|
||||
|
||||
if (res)
|
||||
{
|
||||
// Cleanup
|
||||
for (auto range = map.equal_range(uaddr); range.first != range.second;)
|
||||
{
|
||||
if (range.first->second->mask == 0)
|
||||
{
|
||||
map.erase(range.first);
|
||||
range = map.equal_range(uaddr);
|
||||
continue;
|
||||
}
|
||||
|
||||
range.first++;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ if(BUILD_RPCS3_TESTS)
|
|||
tests/test_simple_array.cpp
|
||||
tests/test_address_range.cpp
|
||||
tests/test_rsx_cfg.cpp
|
||||
tests/test_rsx_fp_asm.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(rpcs3_test
|
||||
|
|
|
|||
|
|
@ -598,15 +598,15 @@ bool package_reader::read_param_sfo()
|
|||
|
||||
const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0u;
|
||||
|
||||
std::string name(entry.name_size + BUF_PADDING, '\0');
|
||||
std::string name_buf(entry.name_size + BUF_PADDING, '\0');
|
||||
|
||||
if (usz read_size = decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), name.data()); read_size < entry.name_size)
|
||||
if (usz read_size = decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), name_buf.data()); read_size < entry.name_size)
|
||||
{
|
||||
pkg_log.error("PKG name could not be read (size=0x%x, offset=0x%x)", entry.name_size, entry.name_offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
fmt::trim_back(name, "\0"sv);
|
||||
std::string_view name = fmt::trim_back_sv(name_buf, "\0"sv);
|
||||
|
||||
// We're looking for the PARAM.SFO file, if there is any
|
||||
if (usz ndelim = name.find_first_not_of('/'); ndelim == umax || name.substr(ndelim) != "PARAM.SFO")
|
||||
|
|
@ -854,18 +854,18 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
|
|||
break;
|
||||
}
|
||||
|
||||
std::string name(entry.name_size + BUF_PADDING, '\0');
|
||||
std::string name_buf(entry.name_size + BUF_PADDING, '\0');
|
||||
|
||||
const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0u;
|
||||
|
||||
if (const usz read_size = decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), name.data()); read_size < entry.name_size)
|
||||
if (const usz read_size = decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), name_buf.data()); read_size < entry.name_size)
|
||||
{
|
||||
num_failures++;
|
||||
pkg_log.error("PKG name could not be read (size=0x%x, offset=0x%x)", entry.name_size, entry.name_offset);
|
||||
break;
|
||||
}
|
||||
|
||||
fmt::trim_back(name, "\0"sv);
|
||||
std::string_view name = fmt::trim_back_sv(name_buf, "\0"sv);
|
||||
|
||||
std::string path = m_install_path + vfs::escape(name);
|
||||
|
||||
|
|
|
|||
|
|
@ -347,6 +347,7 @@ public:
|
|||
};
|
||||
|
||||
bool is_valid() const { return m_is_valid; }
|
||||
const PKGHeader& get_header() const { return m_header; }
|
||||
package_install_result check_target_app_version() const;
|
||||
static package_install_result extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths);
|
||||
const psf::registry& get_psf() const { return m_psf; }
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "Utilities/File.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -412,6 +412,7 @@ target_sources(rpcs3_emu PRIVATE
|
|||
Io/pad_config_types.cpp
|
||||
Io/pad_types.cpp
|
||||
Io/PadHandler.cpp
|
||||
Io/ps_move_data.cpp
|
||||
Io/rb3drums_config.cpp
|
||||
Io/RB3MidiDrums.cpp
|
||||
Io/RB3MidiGuitar.cpp
|
||||
|
|
@ -442,6 +443,8 @@ target_sources(rpcs3_emu PRIVATE
|
|||
NP/np_requests.cpp
|
||||
NP/signaling_handler.cpp
|
||||
NP/np_structs_extra.cpp
|
||||
NP/clans_client.cpp
|
||||
NP/clans_config.cpp
|
||||
NP/rpcn_client.cpp
|
||||
NP/rpcn_config.cpp
|
||||
NP/rpcn_countries.cpp
|
||||
|
|
@ -517,12 +520,15 @@ target_sources(rpcs3_emu PRIVATE
|
|||
RSX/Overlays/overlay_video.cpp
|
||||
RSX/Overlays/Shaders/shader_loading_dialog.cpp
|
||||
RSX/Overlays/Shaders/shader_loading_dialog_native.cpp
|
||||
RSX/Program/Assembler/FPASM.cpp
|
||||
RSX/Program/Assembler/FPOpcodes.cpp
|
||||
RSX/Program/Assembler/FPToCFG.cpp
|
||||
RSX/Program/Assembler/Passes/FP/RegisterAnnotationPass.cpp
|
||||
RSX/Program/Assembler/Passes/FP/RegisterDependencyPass.cpp
|
||||
RSX/Program/CgBinaryProgram.cpp
|
||||
RSX/Program/CgBinaryFragmentProgram.cpp
|
||||
RSX/Program/CgBinaryVertexProgram.cpp
|
||||
RSX/Program/FragmentProgramDecompiler.cpp
|
||||
RSX/Program/FragmentProgramRegister.cpp
|
||||
RSX/Program/GLSLCommon.cpp
|
||||
RSX/Program/ProgramStateCache.cpp
|
||||
RSX/Program/program_util.cpp
|
||||
|
|
@ -624,6 +630,11 @@ if(TARGET 3rdparty_vulkan)
|
|||
RSX/VK/VKTextureCache.cpp
|
||||
RSX/VK/VulkanAPI.cpp
|
||||
)
|
||||
if(APPLE)
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
RSX/VK/vkutils/metal_layer.mm
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
|
|
|||
|
|
@ -506,7 +506,7 @@ extern f64 get_cpu_program_usage_percent(u64 hash)
|
|||
thread_local DECLARE(cpu_thread::g_tls_this_thread) = nullptr;
|
||||
|
||||
// Total number of CPU threads
|
||||
static atomic_t<u64, 64> s_cpu_counter{0};
|
||||
static atomic_t<u64, 128> s_cpu_counter{0};
|
||||
|
||||
// List of posted tasks for suspend_all
|
||||
//static atomic_t<cpu_thread::suspend_work*> s_cpu_work[128]{};
|
||||
|
|
|
|||
|
|
@ -71,7 +71,11 @@ cpu_translator::cpu_translator(llvm::Module* _module, bool is_be)
|
|||
result = m_ir->CreateInsertElement(v, m_ir->CreateExtractElement(data0, m_ir->CreateExtractElement(mask, i)), i);
|
||||
v->addIncoming(result, loop);
|
||||
m_ir->CreateCondBr(m_ir->CreateICmpULT(i, m_ir->getInt32(16)), loop, next);
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
m_ir->SetInsertPoint(next->getFirstNonPHIIt());
|
||||
#else
|
||||
m_ir->SetInsertPoint(next->getFirstNonPHI());
|
||||
#endif
|
||||
result = m_ir->CreateSelect(m_ir->CreateICmpSLT(index, zeros), zeros, result);
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -1149,7 +1149,11 @@ struct llvm_fshl
|
|||
static llvm::Function* get_fshl(llvm::IRBuilder<>* ir)
|
||||
{
|
||||
const auto _module = ir->GetInsertBlock()->getParent()->getParent();
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
return llvm::Intrinsic::getOrInsertDeclaration(_module, llvm::Intrinsic::fshl, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#else
|
||||
return llvm::Intrinsic::getDeclaration(_module, llvm::Intrinsic::fshl, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#endif
|
||||
}
|
||||
|
||||
static llvm::Value* fold(llvm::IRBuilder<>* ir, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3)
|
||||
|
|
@ -1221,7 +1225,11 @@ struct llvm_fshr
|
|||
static llvm::Function* get_fshr(llvm::IRBuilder<>* ir)
|
||||
{
|
||||
const auto _module = ir->GetInsertBlock()->getParent()->getParent();
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
return llvm::Intrinsic::getOrInsertDeclaration(_module, llvm::Intrinsic::fshr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#else
|
||||
return llvm::Intrinsic::getDeclaration(_module, llvm::Intrinsic::fshr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#endif
|
||||
}
|
||||
|
||||
static llvm::Value* fold(llvm::IRBuilder<>* ir, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3)
|
||||
|
|
@ -2220,7 +2228,11 @@ struct llvm_add_sat
|
|||
static llvm::Function* get_add_sat(llvm::IRBuilder<>* ir)
|
||||
{
|
||||
const auto _module = ir->GetInsertBlock()->getParent()->getParent();
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
return llvm::Intrinsic::getOrInsertDeclaration(_module, intr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#else
|
||||
return llvm::Intrinsic::getDeclaration(_module, intr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#endif
|
||||
}
|
||||
|
||||
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||
|
|
@ -2303,7 +2315,11 @@ struct llvm_sub_sat
|
|||
static llvm::Function* get_sub_sat(llvm::IRBuilder<>* ir)
|
||||
{
|
||||
const auto _module = ir->GetInsertBlock()->getParent()->getParent();
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
return llvm::Intrinsic::getOrInsertDeclaration(_module, intr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#else
|
||||
return llvm::Intrinsic::getDeclaration(_module, intr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#endif
|
||||
}
|
||||
|
||||
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||
|
|
@ -3592,7 +3608,11 @@ public:
|
|||
llvm::Function* get_intrinsic(llvm::Intrinsic::ID id)
|
||||
{
|
||||
const auto _module = m_ir->GetInsertBlock()->getParent()->getParent();
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
return llvm::Intrinsic::getOrInsertDeclaration(_module, id, {get_type<Types>()...});
|
||||
#else
|
||||
return llvm::Intrinsic::getDeclaration(_module, id, {get_type<Types>()...});
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
|
|
|
|||
|
|
@ -276,6 +276,8 @@ public:
|
|||
|
||||
u64 start_timestamp_us = 0;
|
||||
|
||||
std::array<ps_move_data, CELL_GEM_MAX_NUM> fake_move_data {}; // No need to be in savestate
|
||||
|
||||
atomic_t<u32> m_wake_up = 0;
|
||||
atomic_t<u32> m_done = 0;
|
||||
|
||||
|
|
@ -624,9 +626,9 @@ public:
|
|||
cellGem.notice("Could not load mouse gem config. Using defaults.");
|
||||
}
|
||||
|
||||
cellGem.notice("Real gem config=\n", g_cfg_gem_real.to_string());
|
||||
cellGem.notice("Fake gem config=\n", g_cfg_gem_fake.to_string());
|
||||
cellGem.notice("Mouse gem config=\n", g_cfg_gem_mouse.to_string());
|
||||
cellGem.notice("Real gem config=%s", g_cfg_gem_real.to_string());
|
||||
cellGem.notice("Fake gem config=%s", g_cfg_gem_fake.to_string());
|
||||
cellGem.notice("Mouse gem config=%s", g_cfg_gem_mouse.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -696,22 +698,165 @@ namespace gem
|
|||
{
|
||||
}
|
||||
|
||||
static inline u8 Y(u8 r, u8 g, u8 b) { return static_cast<u8>(0.299f * r + 0.587f * g + 0.114f * b); }
|
||||
static inline u8 U(u8 r, u8 g, u8 b) { return static_cast<u8>(-0.14713f * r - 0.28886f * g + 0.436f * b); }
|
||||
static inline u8 V(u8 r, u8 g, u8 b) { return static_cast<u8>(0.615f * r - 0.51499f * g - 0.10001f * b); }
|
||||
YUV(const u8 rgb[3])
|
||||
{
|
||||
const u8 r = rgb[0];
|
||||
const u8 g = rgb[1];
|
||||
const u8 b = rgb[2];
|
||||
y = Y(r, g, b);
|
||||
u = U(r, g, b);
|
||||
v = V(r, g, b);
|
||||
}
|
||||
|
||||
static inline u8 Y(u8 r, u8 g, u8 b) { return static_cast<u8>(std::clamp(0.299f * r + 0.587f * g + 0.114f * b, 0.0f, 255.0f)); }
|
||||
static inline u8 U(u8 r, u8 g, u8 b) { return static_cast<u8>(std::clamp(-0.169f * r - 0.331f * g + 0.499f * b + 128, 0.0f, 255.0f)); }
|
||||
static inline u8 V(u8 r, u8 g, u8 b) { return static_cast<u8>(std::clamp(0.499f * r - 0.460f * g - 0.040f * b + 128, 0.0f, 255.0f)); }
|
||||
};
|
||||
|
||||
bool convert_image_format(CellCameraFormat input_format, CellGemVideoConvertFormatEnum output_format,
|
||||
const std::vector<u8>& video_data_in, u32 width, u32 height,
|
||||
u8* video_data_out, u32 video_data_out_size, std::string_view caller)
|
||||
template <bool use_gain>
|
||||
static inline void debayer_raw8_impl(const u8* src, u8* dst, u8 alpha, f32 gain_r, f32 gain_g, f32 gain_b)
|
||||
{
|
||||
if (output_format != CELL_GEM_NO_VIDEO_OUTPUT && !video_data_out)
|
||||
constexpr u32 in_pitch = 640;
|
||||
constexpr u32 out_pitch = 640 * 4;
|
||||
|
||||
// Hamilton–Adams demosaicing
|
||||
for (s32 y = 0; y < 480; y++)
|
||||
{
|
||||
const bool is_even_y = (y % 2) == 0;
|
||||
const u8* srcc = src + y * in_pitch;
|
||||
const u8* srcu = src + std::max(0, y - 1) * in_pitch;
|
||||
const u8* srcd = src + std::min(480 - 1, y + 1) * in_pitch;
|
||||
|
||||
u8* dst0 = dst + y * out_pitch;
|
||||
|
||||
// Split loops (roughly twice the performance by removing one condition)
|
||||
if (is_even_y)
|
||||
{
|
||||
for (s32 x = 0; x < 640; x++, dst0 += 4)
|
||||
{
|
||||
const bool is_even_x = (x % 2) == 0;
|
||||
const int xl = std::max(0, x - 1);
|
||||
const int xr = std::min(640 - 1, x + 1);
|
||||
|
||||
u8 r, b, g;
|
||||
|
||||
if (is_even_x)
|
||||
{
|
||||
// Blue pixel
|
||||
const u8 up = srcu[x];
|
||||
const u8 down = srcd[x];
|
||||
const u8 left = srcc[xl];
|
||||
const u8 right = srcc[xr];
|
||||
const int dh = std::abs(int(left) - int(right));
|
||||
const int dv = std::abs(int(up) - int(down));
|
||||
|
||||
r = (srcu[xl] + srcu[xr] + srcd[xl] + srcd[xr]) / 4;
|
||||
if (dh < dv)
|
||||
g = (left + right) / 2;
|
||||
else if (dv < dh)
|
||||
g = (up + down) / 2;
|
||||
else
|
||||
g = (up + down + left + right) / 4;
|
||||
b = srcc[x];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Green (on blue row)
|
||||
r = (srcu[x] + srcd[x]) / 2;
|
||||
g = srcc[x];
|
||||
b = (srcc[xl] + srcc[xr]) / 2;
|
||||
}
|
||||
|
||||
if constexpr (use_gain)
|
||||
{
|
||||
dst0[0] = static_cast<u8>(std::clamp(r * gain_r, 0.0f, 255.0f));
|
||||
dst0[1] = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
|
||||
dst0[2] = static_cast<u8>(std::clamp(g * gain_g, 0.0f, 255.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
dst0[0] = r;
|
||||
dst0[1] = g;
|
||||
dst0[2] = b;
|
||||
}
|
||||
dst0[3] = alpha;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (s32 x = 0; x < 640; x++, dst0 += 4)
|
||||
{
|
||||
const bool is_even_x = (x % 2) == 0;
|
||||
const int xl = std::max(0, x - 1);
|
||||
const int xr = std::min(640 - 1, x + 1);
|
||||
|
||||
u8 r, b, g;
|
||||
|
||||
if (is_even_x)
|
||||
{
|
||||
// Green (on red row)
|
||||
r = (srcc[xl] + srcc[xr]) / 2;
|
||||
g = srcc[x];
|
||||
b = (srcu[x] + srcd[x]) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Red pixel
|
||||
const u8 up = srcu[x];
|
||||
const u8 down = srcd[x];
|
||||
const u8 left = srcc[xl];
|
||||
const u8 right = srcc[xr];
|
||||
const int dh = std::abs(int(left) - int(right));
|
||||
const int dv = std::abs(int(up) - int(down));
|
||||
|
||||
r = srcc[x];
|
||||
if (dh < dv)
|
||||
g = (left + right) / 2;
|
||||
else if (dv < dh)
|
||||
g = (up + down) / 2;
|
||||
else
|
||||
g = (up + down + left + right) / 4;
|
||||
b = (srcu[xl] + srcu[xr] + srcd[xl] + srcd[xr]) / 4;
|
||||
}
|
||||
|
||||
if constexpr (use_gain)
|
||||
{
|
||||
dst0[0] = static_cast<u8>(std::clamp(r * gain_r, 0.0f, 255.0f));
|
||||
dst0[1] = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
|
||||
dst0[2] = static_cast<u8>(std::clamp(g * gain_g, 0.0f, 255.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
dst0[0] = r;
|
||||
dst0[1] = g;
|
||||
dst0[2] = b;
|
||||
}
|
||||
dst0[3] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void debayer_raw8(const u8* src, u8* dst, u8 alpha, f32 gain_r, f32 gain_g, f32 gain_b)
|
||||
{
|
||||
if (gain_r != 1.0f || gain_g != 1.0f || gain_b != 1.0f)
|
||||
debayer_raw8_impl<true>(src, dst, alpha, gain_r, gain_g, gain_b);
|
||||
else
|
||||
debayer_raw8_impl<false>(src, dst, alpha, gain_r, gain_g, gain_b);
|
||||
}
|
||||
|
||||
bool convert_image_format(CellCameraFormat input_format, const CellGemVideoConvertAttribute& vc,
|
||||
const std::vector<u8>& video_data_in, u32 width, u32 height,
|
||||
u8* video_data_out, u32 video_data_out_size, u8* buffer_memory,
|
||||
std::string_view caller)
|
||||
{
|
||||
if (vc.output_format != CELL_GEM_NO_VIDEO_OUTPUT && !video_data_out)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 required_in_size = get_buffer_size_by_format(static_cast<s32>(input_format), width, height);
|
||||
const s32 required_out_size = cellGemGetVideoConvertSize(output_format);
|
||||
const s32 required_out_size = cellGemGetVideoConvertSize(vc.output_format);
|
||||
|
||||
if (video_data_in.size() != required_in_size)
|
||||
{
|
||||
|
|
@ -721,7 +866,7 @@ namespace gem
|
|||
|
||||
if (required_out_size < 0 || video_data_out_size != static_cast<u32>(required_out_size))
|
||||
{
|
||||
cellGem.error("convert: out_size unknown: required=%d, actual=%d, format %d (called from %s)", required_out_size, video_data_out_size, output_format, caller);
|
||||
cellGem.error("convert: out_size unknown: required=%d, actual=%d, format %d (called from %s)", required_out_size, video_data_out_size, vc.output_format, caller);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -730,7 +875,121 @@ namespace gem
|
|||
return false;
|
||||
}
|
||||
|
||||
switch (output_format)
|
||||
thread_local std::vector<u8> corrected_buffer;
|
||||
thread_local std::vector<u8> combined_buffer;
|
||||
thread_local std::vector<u8> conversion_buffer;
|
||||
|
||||
const u8* src_data = video_data_in.data();
|
||||
const u8 alpha = vc.alpha;
|
||||
const f32 gain_r = vc.gain * vc.blue_gain;
|
||||
const f32 gain_g = vc.gain * vc.green_gain;
|
||||
const f32 gain_b = vc.gain * vc.red_gain;
|
||||
|
||||
// Only RAW8 should be relevant for cellGem unless I'm mistaken
|
||||
if (input_format == CELL_CAMERA_RAW8)
|
||||
{
|
||||
// TODO: CELL_GEM_AUTO_WHITE_BALANCE
|
||||
// TODO: CELL_GEM_GAMMA_BOOST
|
||||
|
||||
// Correct outliers
|
||||
if (vc.conversion_flags & CELL_GEM_FILTER_OUTLIER_PIXELS)
|
||||
{
|
||||
corrected_buffer.resize(width * height);
|
||||
|
||||
for (u32 y = 0; y < height; y++)
|
||||
{
|
||||
const u8* src = src_data + y * 640;
|
||||
u8* dst = &corrected_buffer[y * 640];
|
||||
|
||||
for (u32 x = 0; x < width; x++, src++)
|
||||
{
|
||||
// Let's just say these 2 are outliers
|
||||
if (const u8 val = *src; val > 0 && val < 255)
|
||||
{
|
||||
*dst++ = val;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Just take the 4 neighbours for now
|
||||
s32 sum = 0;
|
||||
if (y >= 2) sum += *(src - (2 * 640));
|
||||
if (x >= 2) sum += *(src - 2);
|
||||
if (x < 638) sum += *(src + 2);
|
||||
if (y < 478) sum += *(src + (2 * 640));
|
||||
|
||||
*dst++ = sum / 4; // Ignore count. It will only be less than 4 on the edges
|
||||
}
|
||||
}
|
||||
|
||||
src_data = corrected_buffer.data();
|
||||
}
|
||||
|
||||
// Combine with previous frame
|
||||
if (buffer_memory && (vc.conversion_flags & CELL_GEM_COMBINE_PREVIOUS_INPUT_FRAME))
|
||||
{
|
||||
combined_buffer.resize(width * height);
|
||||
|
||||
for (u32 i = 0; i < combined_buffer.size(); i++)
|
||||
{
|
||||
const u8 val = src_data[i];
|
||||
u8& old = buffer_memory[i];
|
||||
combined_buffer[i] = (old + val) / 2;
|
||||
old = val;
|
||||
}
|
||||
|
||||
src_data = combined_buffer.data();
|
||||
}
|
||||
|
||||
switch (vc.output_format)
|
||||
{
|
||||
case CELL_GEM_YUV_640x480:
|
||||
case CELL_GEM_YUV422_640x480:
|
||||
case CELL_GEM_YUV411_640x480:
|
||||
{
|
||||
// Let's debayer the image first for YUV formats
|
||||
conversion_buffer.resize(cellGemGetVideoConvertSize(CELL_GEM_RGBA_640x480));
|
||||
|
||||
debayer_raw8(src_data, conversion_buffer.data(), alpha, gain_r, gain_g, gain_b);
|
||||
|
||||
src_data = conversion_buffer.data();
|
||||
input_format = CELL_CAMERA_RGBA;
|
||||
width = 640;
|
||||
height = 480;
|
||||
break;
|
||||
}
|
||||
case CELL_GEM_BAYER_RESTORED:
|
||||
case CELL_GEM_BAYER_RESTORED_RGGB:
|
||||
case CELL_GEM_BAYER_RESTORED_RASTERIZED:
|
||||
{
|
||||
// Let's apply gain
|
||||
if (gain_r != 1.0f || gain_g != 1.0f || gain_b != 1.0f)
|
||||
{
|
||||
conversion_buffer.resize(cellGemGetVideoConvertSize(CELL_GEM_RGBA_640x480));
|
||||
|
||||
const f32 bggr_gains[2][2] = {{gain_b, gain_g}, {gain_g, gain_r}};
|
||||
const u8* src = src_data;
|
||||
u8* dst = conversion_buffer.data();
|
||||
|
||||
for (u32 y = 0; y < 480; y++)
|
||||
{
|
||||
const f32* gains = bggr_gains[y % 2];
|
||||
|
||||
for (u32 x = 0; x < 640; x++)
|
||||
{
|
||||
*dst++ = static_cast<u8>(std::clamp(*src++ * gains[x % 2], 0.0f, 255.0f));
|
||||
}
|
||||
}
|
||||
|
||||
src_data = conversion_buffer.data();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (vc.output_format)
|
||||
{
|
||||
case CELL_GEM_RGBA_640x480: // RGBA output; 640*480*4-byte output buffer required
|
||||
{
|
||||
|
|
@ -738,51 +997,18 @@ namespace gem
|
|||
{
|
||||
case CELL_CAMERA_RAW8:
|
||||
{
|
||||
const u32 in_pitch = width;
|
||||
const u32 out_pitch = width * 4;
|
||||
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * in_pitch];
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst0 = video_data_out + y * out_pitch;
|
||||
u8* dst1 = dst0 + out_pitch;
|
||||
|
||||
for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst0 += 8, dst1 += 8)
|
||||
{
|
||||
const u8 b = src0[0];
|
||||
const u8 g0 = src0[1];
|
||||
const u8 g1 = src1[0];
|
||||
const u8 r = src1[1];
|
||||
|
||||
const u8 top[4] = { r, g0, b, 255 };
|
||||
const u8 bottom[4] = { r, g1, b, 255 };
|
||||
|
||||
// Top-Left
|
||||
std::memcpy(dst0, top, 4);
|
||||
|
||||
// Top-Right Pixel
|
||||
std::memcpy(dst0 + 4, top, 4);
|
||||
|
||||
// Bottom-Left Pixel
|
||||
std::memcpy(dst1, bottom, 4);
|
||||
|
||||
// Bottom-Right Pixel
|
||||
std::memcpy(dst1 + 4, bottom, 4);
|
||||
}
|
||||
}
|
||||
debayer_raw8(src_data, video_data_out, alpha, gain_r, gain_g, gain_b);
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_RGBA:
|
||||
{
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -792,17 +1018,18 @@ namespace gem
|
|||
{
|
||||
if (input_format == CELL_CAMERA_RAW8)
|
||||
{
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GEM_YUV_640x480: // YUV output; 640*480+640*480+640*480-byte output buffer required (contiguous)
|
||||
{
|
||||
// YUV 4:4:4 planar. 1 value each per pixel
|
||||
const u32 yuv_pitch = width;
|
||||
|
||||
u8* dst_y = video_data_out;
|
||||
|
|
@ -813,61 +1040,21 @@ namespace gem
|
|||
{
|
||||
case CELL_CAMERA_RAW8:
|
||||
{
|
||||
const u32 in_pitch = width;
|
||||
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * in_pitch];
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst_y0 = dst_y + y * yuv_pitch;
|
||||
u8* dst_y1 = dst_y0 + yuv_pitch;
|
||||
|
||||
u8* dst_u0 = dst_u + y * yuv_pitch;
|
||||
u8* dst_u1 = dst_u0 + yuv_pitch;
|
||||
|
||||
u8* dst_v0 = dst_v + y * yuv_pitch;
|
||||
u8* dst_v1 = dst_v0 + yuv_pitch;
|
||||
|
||||
for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst_y0 += 2, dst_y1 += 2, dst_u0 += 2, dst_u1 += 2, dst_v0 += 2, dst_v1 += 2)
|
||||
{
|
||||
const u8 b = src0[0];
|
||||
const u8 g0 = src0[1];
|
||||
const u8 g1 = src1[0];
|
||||
const u8 r = src1[1];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv_top = YUV(r, g0, b);
|
||||
const YUV yuv_bottom = YUV(r, g1, b);
|
||||
|
||||
dst_y0[0] = dst_y0[1] = yuv_top.y;
|
||||
dst_y1[0] = dst_y1[1] = yuv_bottom.y;
|
||||
|
||||
dst_u0[0] = dst_u0[1] = yuv_top.u;
|
||||
dst_u1[0] = dst_u1[1] = yuv_bottom.u;
|
||||
|
||||
dst_v0[0] = dst_v0[1] = yuv_top.v;
|
||||
dst_v1[0] = dst_v1[1] = yuv_bottom.v;
|
||||
}
|
||||
}
|
||||
fmt::throw_exception("Unreachable: should already be debayered");
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_RGBA:
|
||||
{
|
||||
const u32 in_pitch = width / 4;
|
||||
const u32 in_pitch = width * 4;
|
||||
|
||||
for (u32 y = 0; y < height; y++)
|
||||
{
|
||||
const u8* src = &video_data_in[y * in_pitch];
|
||||
const u8* src = src_data + y * in_pitch;
|
||||
|
||||
for (u32 x = 0; x < width; x++, src += 4)
|
||||
{
|
||||
const u8 r = src[0];
|
||||
const u8 g = src[1];
|
||||
const u8 b = src[2];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv = YUV(r, g, b);
|
||||
const YUV yuv = YUV(src);
|
||||
|
||||
*dst_y++ = yuv.y;
|
||||
*dst_u++ = yuv.u;
|
||||
|
|
@ -878,8 +1065,8 @@ namespace gem
|
|||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -887,6 +1074,7 @@ namespace gem
|
|||
}
|
||||
case CELL_GEM_YUV422_640x480: // YUV output; 640*480+320*480+320*480-byte output buffer required (contiguous)
|
||||
{
|
||||
// YUV 4:2:2 planar. 1 Y value per pixel, 1 U/V value per 2 horizontal pixels
|
||||
const u32 y_pitch = width;
|
||||
const u32 uv_pitch = width / 2;
|
||||
|
||||
|
|
@ -898,43 +1086,7 @@ namespace gem
|
|||
{
|
||||
case CELL_CAMERA_RAW8:
|
||||
{
|
||||
const u32 in_pitch = width;
|
||||
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * in_pitch];
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst_y0 = dst_y + y * y_pitch;
|
||||
u8* dst_y1 = dst_y0 + y_pitch;
|
||||
|
||||
u8* dst_u0 = dst_u + y * uv_pitch;
|
||||
u8* dst_u1 = dst_u0 + uv_pitch;
|
||||
|
||||
u8* dst_v0 = dst_v + y * uv_pitch;
|
||||
u8* dst_v1 = dst_v0 + uv_pitch;
|
||||
|
||||
for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst_y0 += 2, dst_y1 += 2)
|
||||
{
|
||||
const u8 b = src0[0];
|
||||
const u8 g0 = src0[1];
|
||||
const u8 g1 = src1[0];
|
||||
const u8 r = src1[1];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv_top = YUV(r, g0, b);
|
||||
const YUV yuv_bottom = YUV(r, g1, b);
|
||||
|
||||
dst_y0[0] = dst_y0[1] = yuv_top.y;
|
||||
dst_y1[0] = dst_y1[1] = yuv_bottom.y;
|
||||
|
||||
*dst_u0++ = yuv_top.u;
|
||||
*dst_u1++ = yuv_bottom.u;
|
||||
|
||||
*dst_v0++ = yuv_top.v;
|
||||
*dst_v1++ = yuv_bottom.v;
|
||||
}
|
||||
}
|
||||
fmt::throw_exception("Unreachable: should already be debayered");
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_RGBA:
|
||||
|
|
@ -943,33 +1095,28 @@ namespace gem
|
|||
|
||||
for (u32 y = 0; y < height; y++)
|
||||
{
|
||||
const u8* src = &video_data_in[y * in_pitch];
|
||||
const u8* src = src_data + y * in_pitch;
|
||||
|
||||
for (u32 x = 0; x < width - 1; x += 2, src += 8, dst_y += 2)
|
||||
{
|
||||
const u8 r_0 = src[0];
|
||||
const u8 g_0 = src[1];
|
||||
const u8 b_0 = src[2];
|
||||
const u8 r_1 = src[4];
|
||||
const u8 g_1 = src[5];
|
||||
const u8 b_1 = src[6];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv_0 = YUV(r_0, g_0, b_0);
|
||||
const u8 y_1 = YUV::Y(r_1, g_1, b_1);
|
||||
const YUV yuv_0 = YUV(src);
|
||||
const YUV yuv_1 = YUV(src + 4);
|
||||
|
||||
dst_y[0] = yuv_0.y;
|
||||
dst_y[1] = y_1;
|
||||
*dst_u++ = yuv_0.u;
|
||||
*dst_v++ = yuv_0.v;
|
||||
dst_y[1] = yuv_1.y;
|
||||
|
||||
// Average U/V from 2 horizontal pixels
|
||||
*dst_u++ = (yuv_0.u + yuv_1.u) / 2;
|
||||
*dst_v++ = (yuv_0.v + yuv_1.v) / 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -977,109 +1124,54 @@ namespace gem
|
|||
}
|
||||
case CELL_GEM_YUV411_640x480: // YUV411 output; 640*480+320*240+320*240-byte output buffer required (contiguous)
|
||||
{
|
||||
const u32 y_pitch = width;
|
||||
const u32 uv_pitch = width / 4;
|
||||
|
||||
// YUV 4:1:1 planar. 1 Y value per pixel, 1 U/V value per 2x2 pixel block
|
||||
u8* dst_y = video_data_out;
|
||||
u8* dst_u = dst_y + y_pitch * height;
|
||||
u8* dst_v = dst_u + uv_pitch * height;
|
||||
u8* dst_u = dst_y + 640 * 480;
|
||||
u8* dst_v = dst_u + 320 * 240;
|
||||
|
||||
switch (input_format)
|
||||
{
|
||||
case CELL_CAMERA_RAW8:
|
||||
{
|
||||
const u32 in_pitch = width;
|
||||
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * in_pitch];
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst_y0 = dst_y + y * y_pitch;
|
||||
u8* dst_y1 = dst_y0 + y_pitch;
|
||||
|
||||
u8* dst_u0 = dst_u + y * uv_pitch;
|
||||
u8* dst_u1 = dst_u0 + uv_pitch;
|
||||
|
||||
u8* dst_v0 = dst_v + y * uv_pitch;
|
||||
u8* dst_v1 = dst_v0 + uv_pitch;
|
||||
|
||||
for (u32 x = 0; x < width - 3; x += 4, src0 += 4, src1 += 4, dst_y0 += 4, dst_y1 += 4)
|
||||
{
|
||||
const u8 b_left = src0[0];
|
||||
const u8 g0_left = src0[1];
|
||||
const u8 b_right = src0[2];
|
||||
const u8 g0_right = src0[3];
|
||||
|
||||
const u8 g1_left = src1[0];
|
||||
const u8 r_left = src1[1];
|
||||
const u8 g1_right = src1[2];
|
||||
const u8 r_right = src1[3];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv_top_left = YUV(r_left, g0_left, b_left); // Re-used for top-right
|
||||
const u8 y_top_right = YUV::Y(r_right, g0_right, b_right);
|
||||
const YUV yuv_bottom_left = YUV(r_left, g1_left, b_left); // Re-used for bottom-right
|
||||
const u8 y_bottom_right = YUV::Y(r_right, g1_right, b_right);
|
||||
|
||||
dst_y0[0] = dst_y0[1] = yuv_top_left.y;
|
||||
dst_y0[2] = dst_y0[3] = y_top_right;
|
||||
|
||||
dst_y1[0] = dst_y1[1] = yuv_bottom_left.y;
|
||||
dst_y1[2] = dst_y1[3] = y_bottom_right;
|
||||
|
||||
*dst_u0++ = yuv_top_left.u;
|
||||
*dst_u1++ = yuv_bottom_left.u;
|
||||
|
||||
*dst_v0++ = yuv_top_left.v;
|
||||
*dst_v1++ = yuv_bottom_left.v;
|
||||
}
|
||||
}
|
||||
fmt::throw_exception("Unreachable: should already be debayered");
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_RGBA:
|
||||
{
|
||||
const u32 in_pitch = width * 4;
|
||||
|
||||
for (u32 y = 0; y < height; y++)
|
||||
// 2 rows at a time to get a 2x2 pixel block
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src = &video_data_in[y * in_pitch];
|
||||
const u8* src = src_data + y * in_pitch;
|
||||
const u8* src2 = src + in_pitch;
|
||||
u8* dst_y1 = dst_y + y * 640;
|
||||
u8* dst_y2 = dst_y1 + 640;
|
||||
|
||||
for (u32 x = 0; x < width - 3; x += 4, src += 16, dst_y += 4)
|
||||
for (u32 x = 0; x < width - 1; x += 2, src += 8, src2 += 8, dst_y1 += 2, dst_y2 += 2)
|
||||
{
|
||||
const u8 r_0 = src[0];
|
||||
const u8 g_0 = src[1];
|
||||
const u8 b_0 = src[2];
|
||||
const u8 r_1 = src[4];
|
||||
const u8 g_1 = src[5];
|
||||
const u8 b_1 = src[6];
|
||||
const u8 r_2 = src[8];
|
||||
const u8 g_2 = src[9];
|
||||
const u8 b_2 = src[10];
|
||||
const u8 r_3 = src[12];
|
||||
const u8 g_3 = src[13];
|
||||
const u8 b_3 = src[14];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv_0 = YUV(r_0, g_0, b_0);
|
||||
const u8 y_1 = YUV::Y(r_1, g_1, b_1);
|
||||
const u8 y_2 = YUV::Y(r_2, g_2, b_2);
|
||||
const u8 y_3 = YUV::Y(r_3, g_3, b_3);
|
||||
const YUV yuv_0 = YUV(src);
|
||||
const YUV yuv_1 = YUV(src + 4);
|
||||
const YUV yuv_2 = YUV(src2);
|
||||
const YUV yuv_3 = YUV(src2 + 4);
|
||||
|
||||
dst_y[0] = yuv_0.y;
|
||||
dst_y[1] = y_1;
|
||||
dst_y[2] = y_2;
|
||||
dst_y[3] = y_3;
|
||||
*dst_u++ = yuv_0.u;
|
||||
*dst_v++ = yuv_0.v;
|
||||
dst_y1[0] = yuv_0.y;
|
||||
dst_y1[1] = yuv_1.y;
|
||||
dst_y2[0] = yuv_2.y;
|
||||
dst_y2[1] = yuv_3.y;
|
||||
|
||||
// Average U/V from 2x2 pixel block
|
||||
*dst_u++ = (yuv_0.u + yuv_1.u + yuv_2.u + yuv_3.u) / 4;
|
||||
*dst_v++ = (yuv_0.v + yuv_1.v + yuv_2.v + yuv_3.v) / 4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1096,7 +1188,7 @@ namespace gem
|
|||
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * in_pitch];
|
||||
const u8* src0 = src_data + y * in_pitch;
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst0 = video_data_out + (y / 2) * out_pitch;
|
||||
|
|
@ -1109,8 +1201,8 @@ namespace gem
|
|||
const u8 g1 = src1[0];
|
||||
const u8 r = src1[1];
|
||||
|
||||
const u8 top[4] = { r, g0, b, 255 };
|
||||
const u8 bottom[4] = { r, g1, b, 255 };
|
||||
const u8 top[4] = { r, g0, b, alpha };
|
||||
const u8 bottom[4] = { r, g1, b, alpha };
|
||||
|
||||
// Top-Left
|
||||
std::memcpy(dst0, top, 4);
|
||||
|
|
@ -1128,7 +1220,7 @@ namespace gem
|
|||
|
||||
for (u32 y = 0; y < height / 2; y++)
|
||||
{
|
||||
const u8* src = &video_data_in[y * 2 * in_pitch];
|
||||
const u8* src = src_data + y * 2 * in_pitch;
|
||||
u8* dst = video_data_out + y * out_pitch;
|
||||
|
||||
for (u32 x = 0; x < width / 2; x++, src += 4 * 2, dst += 4)
|
||||
|
|
@ -1140,19 +1232,97 @@ namespace gem
|
|||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GEM_BAYER_RESTORED_RGGB: // Restored Bayer output, 2x2 pixels rearranged into 320x240 RG1G2B
|
||||
{
|
||||
if (input_format == CELL_CAMERA_RAW8)
|
||||
{
|
||||
const u32 dst_w = std::min(320u, width / 2);
|
||||
const u32 dst_h = std::min(240u, height / 2);
|
||||
const u32 in_pitch = width;
|
||||
constexpr u32 out_pitch = 320 * 4;
|
||||
|
||||
for (u32 y = 0; y < dst_h; y++)
|
||||
{
|
||||
const u8* src0 = src_data + y * 2 * in_pitch;
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst = video_data_out + y * out_pitch;
|
||||
|
||||
for (u32 x = 0; x < dst_w; x++, src0 += 2, src1 += 2, dst += 4)
|
||||
{
|
||||
const u8 b = src0[0];
|
||||
const u8 g0 = src0[1];
|
||||
const u8 g1 = src1[0];
|
||||
const u8 r = src1[1];
|
||||
|
||||
dst[0] = r;
|
||||
dst[1] = g0;
|
||||
dst[2] = g1;
|
||||
dst[3] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GEM_BAYER_RESTORED_RASTERIZED: // Restored Bayer output, R,G1,G2,B rearranged into 4 contiguous 320x240 1-channel rasters
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
if (input_format == CELL_CAMERA_RAW8)
|
||||
{
|
||||
const u32 dst_w = std::min(320u, width / 2);
|
||||
const u32 dst_h = std::min(240u, height / 2);
|
||||
const u32 in_pitch = width;
|
||||
constexpr u32 out_plane = 320 * 240;
|
||||
constexpr u32 out_pitch = 320;
|
||||
|
||||
u8* dst_plane_r = video_data_out;
|
||||
u8* dst_plane_g1 = video_data_out + out_plane;
|
||||
u8* dst_plane_g2 = video_data_out + out_plane * 2;
|
||||
u8* dst_plane_b = video_data_out + out_plane * 3;
|
||||
|
||||
for (u32 y = 0; y < dst_h; y++)
|
||||
{
|
||||
const u8* src0 = src_data + y * 2 * in_pitch;
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst_r = dst_plane_r + y * out_pitch;
|
||||
u8* dst_g1 = dst_plane_g1 + y * out_pitch;
|
||||
u8* dst_g2 = dst_plane_g2 + y * out_pitch;
|
||||
u8* dst_b = dst_plane_b + y * out_pitch;
|
||||
|
||||
for (u32 x = 0; x < dst_w; x++, src0 += 2, src1 += 2)
|
||||
{
|
||||
const u8 b = src0[0];
|
||||
const u8 g0 = src0[1];
|
||||
const u8 g1 = src1[0];
|
||||
const u8 r = src1[1];
|
||||
|
||||
dst_r[x] = r;
|
||||
dst_g1[x] = g0;
|
||||
dst_g2[x] = g1;
|
||||
dst_b[x] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GEM_NO_VIDEO_OUTPUT: // Disable video output
|
||||
{
|
||||
|
|
@ -1161,7 +1331,7 @@ namespace gem
|
|||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Trying to convert %s to %s (called from %s)", input_format, output_format, caller);
|
||||
cellGem.error("Trying to convert %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1340,13 +1510,15 @@ void gem_config_data::operator()()
|
|||
|
||||
const auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
|
||||
if (gem::convert_image_format(shared_data.format, vc.output_format, video_data_in, shared_data.width, shared_data.height, vc_attribute.video_data_out ? vc_attribute.video_data_out.get_ptr() : nullptr, video_data_out_size, "cellGem"))
|
||||
if (gem::convert_image_format(shared_data.format, vc, video_data_in, shared_data.width, shared_data.height,
|
||||
vc.video_data_out ? vc.video_data_out.get_ptr() : nullptr, video_data_out_size,
|
||||
vc.buffer_memory ? vc.buffer_memory.get_ptr() : nullptr, "cellGem"))
|
||||
{
|
||||
cellGem.trace("Converted video frame of format %s to %s", shared_data.format.load(), vc.output_format.get());
|
||||
|
||||
if (g_cfg.io.paint_move_spheres)
|
||||
{
|
||||
paint_spheres(vc.output_format, shared_data.width, shared_data.height, vc_attribute.video_data_out ? vc_attribute.video_data_out.get_ptr() : nullptr, video_data_out_size);
|
||||
paint_spheres(vc.output_format, shared_data.width, shared_data.height, vc.video_data_out ? vc.video_data_out.get_ptr() : nullptr, video_data_out_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1617,7 +1789,7 @@ static inline void pos_to_gem_image_state(u32 gem_num, gem_config::gem_controlle
|
|||
|
||||
const f32 scaling_width = x_max / static_cast<f32>(shared_data.width);
|
||||
const f32 scaling_height = y_max / static_cast<f32>(shared_data.height);
|
||||
const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius;
|
||||
const f32 mmPerPixel = controller.radius <= 0.0f ? 0.0f : (CELL_GEM_SPHERE_RADIUS_MM / controller.radius);
|
||||
|
||||
// Image coordinates in pixels
|
||||
const f32 image_x = static_cast<f32>(x_pos) / scaling_width;
|
||||
|
|
@ -1657,7 +1829,7 @@ static inline void pos_to_gem_image_state(u32 gem_num, gem_config::gem_controlle
|
|||
}
|
||||
}
|
||||
|
||||
static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max, const ps_move_data& move_data)
|
||||
static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max, ps_move_data& move_data)
|
||||
{
|
||||
const auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
|
||||
|
|
@ -1670,7 +1842,7 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
|
|||
|
||||
const f32 scaling_width = x_max / static_cast<f32>(shared_data.width);
|
||||
const f32 scaling_height = y_max / static_cast<f32>(shared_data.height);
|
||||
const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius;
|
||||
const f32 mmPerPixel = controller.radius <= 0.0f ? 0.0f : (CELL_GEM_SPHERE_RADIUS_MM / controller.radius);
|
||||
|
||||
// Image coordinates in pixels
|
||||
const f32 image_x = static_cast<f32>(x_pos) / scaling_width;
|
||||
|
|
@ -1703,10 +1875,10 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
|
|||
// Calculate orientation
|
||||
if (g_cfg.io.move == move_handler::real || (g_cfg.io.move == move_handler::fake && move_data.orientation_enabled))
|
||||
{
|
||||
gem_state->quat[0] = move_data.quaternion[0]; // x
|
||||
gem_state->quat[1] = move_data.quaternion[1]; // y
|
||||
gem_state->quat[2] = move_data.quaternion[2]; // z
|
||||
gem_state->quat[3] = move_data.quaternion[3]; // w
|
||||
gem_state->quat[0] = move_data.quaternion.x();
|
||||
gem_state->quat[1] = move_data.quaternion.y();
|
||||
gem_state->quat[2] = move_data.quaternion.z();
|
||||
gem_state->quat[3] = move_data.quaternion.w();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1733,6 +1905,17 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
|
|||
gem_state->quat[3] = q_w;
|
||||
}
|
||||
|
||||
if constexpr (!ps_move_data::use_imu_for_velocity)
|
||||
{
|
||||
move_data.update_velocity(shared_data.frame_timestamp_us, gem_state->pos);
|
||||
|
||||
for (u32 i = 0; i < 3; i++)
|
||||
{
|
||||
gem_state->vel[i] = move_data.vel_world[i];
|
||||
gem_state->accel[i] = move_data.accel_world[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Update visibility for fake handlers
|
||||
if (g_cfg.io.move != move_handler::real)
|
||||
{
|
||||
|
|
@ -1906,9 +2089,17 @@ static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& co
|
|||
if constexpr (std::is_same_v<T, vm::ptr<CellGemState>>)
|
||||
{
|
||||
gem_state->temperature = pad->move_data.temperature;
|
||||
gem_state->accel[0] = pad->move_data.accelerometer_x * 1000; // linear velocity in mm/s²
|
||||
gem_state->accel[1] = pad->move_data.accelerometer_y * 1000; // linear velocity in mm/s²
|
||||
gem_state->accel[2] = pad->move_data.accelerometer_z * 1000; // linear velocity in mm/s²
|
||||
|
||||
for (u32 i = 0; i < 3; i++)
|
||||
{
|
||||
if constexpr (ps_move_data::use_imu_for_velocity)
|
||||
{
|
||||
gem_state->vel[i] = pad->move_data.vel_world[i];
|
||||
gem_state->accel[i] = pad->move_data.accel_world[i];
|
||||
}
|
||||
gem_state->angvel[i] = pad->move_data.angvel_world[i];
|
||||
gem_state->angaccel[i] = pad->move_data.angaccel_world[i];
|
||||
}
|
||||
|
||||
pos_to_gem_state(gem_num, controller, gem_state, info.x_pos, info.y_pos, info.x_max, info.y_max, pad->move_data);
|
||||
}
|
||||
|
|
@ -2147,7 +2338,8 @@ static void mouse_pos_to_gem_state(u32 mouse_no, gem_config::gem_controller& con
|
|||
|
||||
if constexpr (std::is_same_v<T, vm::ptr<CellGemState>>)
|
||||
{
|
||||
pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max, {});
|
||||
ps_move_data& move_data = ::at32(g_fxo->get<gem_config>().fake_move_data, mouse_no);
|
||||
pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max, move_data);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, vm::ptr<CellGemImageState>>)
|
||||
{
|
||||
|
|
@ -2215,7 +2407,8 @@ static void gun_pos_to_gem_state(u32 gem_no, gem_config::gem_controller& control
|
|||
|
||||
if constexpr (std::is_same_v<T, vm::ptr<CellGemState>>)
|
||||
{
|
||||
pos_to_gem_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max, {});
|
||||
ps_move_data& move_data = ::at32(g_fxo->get<gem_config>().fake_move_data, gem_no);
|
||||
pos_to_gem_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max, move_data);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, vm::ptr<CellGemImageState>>)
|
||||
{
|
||||
|
|
@ -2769,7 +2962,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
|
|||
|
||||
inertial_state->timestamp = (get_guest_system_time() - gem.start_timestamp_us);
|
||||
inertial_state->counter = gem.inertial_counter++;
|
||||
inertial_state->accelerometer[0] = 10; // Current gravity in m/s²
|
||||
inertial_state->accelerometer[2] = 1.0f; // Current gravity in G units (9.81 == 1 unit)
|
||||
|
||||
switch (g_cfg.io.move)
|
||||
{
|
||||
|
|
@ -2786,12 +2979,12 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
|
|||
if (pad && pad->is_connected() && !pad->is_copilot())
|
||||
{
|
||||
inertial_state->temperature = pad->move_data.temperature;
|
||||
inertial_state->accelerometer[0] = pad->move_data.accelerometer_x;
|
||||
inertial_state->accelerometer[1] = pad->move_data.accelerometer_y;
|
||||
inertial_state->accelerometer[2] = pad->move_data.accelerometer_z;
|
||||
inertial_state->gyro[0] = pad->move_data.gyro_x;
|
||||
inertial_state->gyro[1] = pad->move_data.gyro_y;
|
||||
inertial_state->gyro[2] = pad->move_data.gyro_z;
|
||||
|
||||
for (u32 i = 0; i < 3; i++)
|
||||
{
|
||||
inertial_state->accelerometer[i] = pad->move_data.accelerometer[i];
|
||||
inertial_state->gyro[i] = pad->move_data.gyro[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -246,15 +246,15 @@ struct CellGemInfo
|
|||
// z increases towards user (away from the camera)
|
||||
struct CellGemState
|
||||
{
|
||||
be_t<f32> pos[4]; // center of sphere (mm)
|
||||
be_t<f32> vel[4]; // velocity of sphere (mm/s)
|
||||
be_t<f32> accel[4]; // acceleration of sphere (mm/s²)
|
||||
be_t<f32> pos[4]; // center of sphere in world coordinates (mm)
|
||||
be_t<f32> vel[4]; // velocity of sphere in world coordinates (mm/s)
|
||||
be_t<f32> accel[4]; // acceleration of sphere in world coordinates (mm/s²)
|
||||
be_t<f32> quat[4]; // quaternion orientation (x,y,z,w) of controller relative to default (facing the camera with buttons up)
|
||||
be_t<f32> angvel[4]; // angular velocity of controller (radians/s)
|
||||
be_t<f32> angaccel[4]; // angular acceleration of controller (radians/s²)
|
||||
be_t<f32> handle_pos[4]; // center of controller handle (mm)
|
||||
be_t<f32> handle_vel[4]; // velocity of controller handle (mm/s)
|
||||
be_t<f32> handle_accel[4]; // acceleration of controller handle (mm/s²)
|
||||
be_t<f32> angvel[4]; // angular velocity of controller in world coordinates (radians/s)
|
||||
be_t<f32> angaccel[4]; // angular acceleration of controller in world coordinates (radians/s²)
|
||||
be_t<f32> handle_pos[4]; // center of controller handle in world coordinates (mm)
|
||||
be_t<f32> handle_vel[4]; // velocity of controller handle in world coordinates (mm/s)
|
||||
be_t<f32> handle_accel[4]; // acceleration of controller handle in world coordinates (mm/s²)
|
||||
CellGemPadData pad;
|
||||
CellGemExtPortData ext;
|
||||
be_t<u64> timestamp; // system_time_t (microseconds)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ using SceNpBasicMessageRecvAction = u32;
|
|||
|
||||
using SceNpClanId = u32;
|
||||
using SceNpClansMessageId = u32;
|
||||
using SceNpClansMemberRole = u32;
|
||||
using SceNpClansMemberStatus = s32;
|
||||
|
||||
using SceNpCustomMenuIndexMask = u32;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "stdafx.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
|
|
@ -540,7 +541,9 @@ error_code sceNpMatching2GetClanLobbyId(SceNpMatching2ContextId ctxId, SceNpClan
|
|||
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
// Returning this rather than `CELL_OK` allows for games to
|
||||
// not need Matching2 Clans support to connect, when Clans are enabled.
|
||||
return SCE_NP_MATCHING2_SERVER_ERROR_SERVICE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
error_code sceNpMatching2GetLobbyMemberDataInternal(
|
||||
|
|
@ -819,10 +822,7 @@ error_code sceNpMatching2AbortRequest(SceNpMatching2ContextId ctxId, SceNpMatchi
|
|||
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!nph.abort_request(reqId))
|
||||
return SCE_NP_MATCHING2_ERROR_REQUEST_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
return nph.abort_request(reqId);
|
||||
}
|
||||
|
||||
error_code sceNpMatching2GetServerInfo(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/NP/np_handler.h"
|
||||
#include "Emu/NP/clans_client.h"
|
||||
|
||||
#include "sceNp.h"
|
||||
#include <memory>
|
||||
#include "sceNpClans.h"
|
||||
|
||||
|
||||
LOG_CHANNEL(sceNpClans);
|
||||
|
||||
template<>
|
||||
|
|
@ -14,6 +18,7 @@ void fmt_class_string<SceNpClansError>::format(std::string& out, u64 arg)
|
|||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(SCE_NP_CLANS_SUCCESS);
|
||||
STR_CASE(SCE_NP_CLANS_ERROR_ALREADY_INITIALIZED);
|
||||
STR_CASE(SCE_NP_CLANS_ERROR_NOT_INITIALIZED);
|
||||
STR_CASE(SCE_NP_CLANS_ERROR_NOT_SUPPORTED);
|
||||
|
|
@ -94,6 +99,9 @@ error_code sceNpClansInit(vm::cptr<SceNpCommunicationId> commId, vm::cptr<SceNpC
|
|||
return SCE_NP_CLANS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Allocate space for a client somewhere
|
||||
std::shared_ptr<clan::clans_client> client = std::make_shared<clan::clans_client>();
|
||||
clans_manager.client = client;
|
||||
clans_manager.is_initialized = true;
|
||||
|
||||
return CELL_OK;
|
||||
|
|
@ -110,6 +118,7 @@ error_code sceNpClansTerm()
|
|||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
clans_manager.client.reset();
|
||||
clans_manager.is_initialized = false;
|
||||
|
||||
return CELL_OK;
|
||||
|
|
@ -117,7 +126,7 @@ error_code sceNpClansTerm()
|
|||
|
||||
error_code sceNpClansCreateRequest(vm::ptr<SceNpClansRequestHandle> handle, u64 flags)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansCreateRequest(handle=*0x%x, flags=0x%llx)", handle, flags);
|
||||
sceNpClans.warning("sceNpClansCreateRequest(handle=*0x%x, flags=0x%x)", handle, flags);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -134,36 +143,58 @@ error_code sceNpClansCreateRequest(vm::ptr<SceNpClansRequestHandle> handle, u64
|
|||
return SCE_NP_CLANS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
s32 reqId = 0;
|
||||
SceNpClansError res = clans_manager.client->create_request(&reqId);
|
||||
if (res != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*handle = reqId;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansDestroyRequest(vm::ptr<SceNpClansRequestHandle> handle)
|
||||
error_code sceNpClansDestroyRequest(SceNpClansRequestHandle handle)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansDestroyRequest(handle=*0x%x)", handle);
|
||||
sceNpClans.warning("sceNpClansDestroyRequest(handle=*0x%x)", handle);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansError res = clans_manager.client->destroy_request(handle);
|
||||
if (res != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansAbortRequest(vm::ptr<SceNpClansRequestHandle> handle)
|
||||
error_code sceNpClansAbortRequest(SceNpClansRequestHandle handle)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansAbortRequest(handle=*0x%x)", handle);
|
||||
sceNpClans.warning("sceNpClansAbortRequest(handle=*0x%x)", handle);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
clans_manager.client->destroy_request(handle);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansCreateClan(vm::ptr<SceNpClansRequestHandle> handle, vm::cptr<char> name, vm::cptr<char> tag, vm::ptr<SceNpClanId> clanId)
|
||||
error_code sceNpClansCreateClan(SceNpClansRequestHandle handle, vm::cptr<char> name, vm::cptr<char> tag, vm::ptr<SceNpClanId> clanId)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansCreateClan(handle=*0x%x, name=%s, tag=%s, clanId=*0x%x)", handle, name, tag, clanId);
|
||||
sceNpClans.warning("sceNpClansCreateClan(handle=*0x%x, name=%s, tag=%s, clanId=*0x%x)", handle, name, tag, clanId);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -180,31 +211,60 @@ error_code sceNpClansCreateClan(vm::ptr<SceNpClansRequestHandle> handle, vm::cpt
|
|||
return SCE_NP_CLANS_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
std::string name_str;
|
||||
vm::read_string(name.addr(), SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH, name_str);
|
||||
|
||||
std::string tag_str;
|
||||
vm::read_string(tag.addr(), SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH, tag_str);
|
||||
|
||||
SceNpClansError res = clans_manager.client->create_clan(nph, handle, name_str, tag_str, clanId);
|
||||
if (res != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansDisbandClan(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId)
|
||||
error_code sceNpClansDisbandClan(SceNpClansRequestHandle handle, SceNpClanId clanId)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansDisbandClan(handle=*0x%x, clanId=*0x%x)", handle, clanId);
|
||||
sceNpClans.warning("sceNpClansDisbandClan(handle=*0x%x, clanId=*0x%x)", handle, clanId);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!clanId)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansError res = clans_manager.client->disband_dlan(nph, handle, clanId);
|
||||
if (res != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansGetClanList(vm::ptr<SceNpClansRequestHandle> handle, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansEntry> clanList, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
error_code sceNpClansGetClanList(SceNpClansRequestHandle handle, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansEntry> clanList, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansGetClanList(handle=*0x%x, paging=*0x%x, clanList=*0x%x, pageResult=*0x%x)", handle, paging, clanList, pageResult);
|
||||
sceNpClans.warning("sceNpClansGetClanList(handle=*0x%x, paging=*0x%x, clanList=*0x%x, pageResult=*0x%x)", handle, paging, clanList, pageResult);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!pageResult || (paging && !clanList)) // TODO: confirm
|
||||
if (!pageResult || (paging && !clanList))
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
|
@ -217,10 +277,35 @@ error_code sceNpClansGetClanList(vm::ptr<SceNpClansRequestHandle> handle, vm::cp
|
|||
}
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansPagingRequest host_paging = {};
|
||||
if (paging)
|
||||
{
|
||||
std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
|
||||
}
|
||||
|
||||
SceNpClansEntry host_clanList[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {};
|
||||
SceNpClansPagingResult host_pageResult = {};
|
||||
|
||||
SceNpClansError ret = clans_manager.client->get_clan_list(nph, handle, &host_paging, host_clanList, &host_pageResult);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (clanList && host_pageResult.count > 0)
|
||||
{
|
||||
std::memcpy(clanList.get_ptr(), host_clanList, sizeof(SceNpClansEntry) * host_pageResult.count);
|
||||
}
|
||||
std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansGetClanListByNpId(vm::ptr<SceNpClansRequestHandle> handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpId> npid, vm::ptr<SceNpClansEntry> clanList, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
// TODO: seems to not be needed, even by the PS3..?
|
||||
error_code sceNpClansGetClanListByNpId(SceNpClansRequestHandle handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpId> npid, vm::ptr<SceNpClansEntry> clanList, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansGetClanListByNpId(handle=*0x%x, paging=*0x%x, npid=*0x%x, clanList=*0x%x, pageResult=*0x%x)", handle, paging, npid, clanList, pageResult);
|
||||
|
||||
|
|
@ -245,7 +330,8 @@ error_code sceNpClansGetClanListByNpId(vm::ptr<SceNpClansRequestHandle> handle,
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansSearchByProfile(vm::ptr<SceNpClansRequestHandle> handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpClansSearchableProfile> search, vm::ptr<SceNpClansClanBasicInfo> results, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
// TODO: seems to not be needed, even by the PS3..?
|
||||
error_code sceNpClansSearchByProfile(SceNpClansRequestHandle handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpClansSearchableProfile> search, vm::ptr<SceNpClansClanBasicInfo> results, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansSearchByProfile(handle=*0x%x, paging=*0x%x, search=*0x%x, results=*0x%x, pageResult=*0x%x)", handle, paging, search, results, pageResult);
|
||||
|
||||
|
|
@ -270,9 +356,9 @@ error_code sceNpClansSearchByProfile(vm::ptr<SceNpClansRequestHandle> handle, vm
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansSearchByName(vm::ptr<SceNpClansRequestHandle> handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpClansSearchableName> search, vm::ptr<SceNpClansClanBasicInfo> results, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
error_code sceNpClansSearchByName(SceNpClansRequestHandle handle, vm::cptr<SceNpClansPagingRequest> paging, vm::cptr<SceNpClansSearchableName> search, vm::ptr<SceNpClansClanBasicInfo> results, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansSearchByName(handle=*0x%x, paging=*0x%x, search=*0x%x, results=*0x%x, pageResult=*0x%x)", handle, paging, search, results, pageResult);
|
||||
sceNpClans.warning("sceNpClansSearchByName(handle=*0x%x, paging=*0x%x, search=*0x%x, results=*0x%x, pageResult=*0x%x)", handle, paging, search, results, pageResult);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -292,12 +378,38 @@ error_code sceNpClansSearchByName(vm::ptr<SceNpClansRequestHandle> handle, vm::c
|
|||
}
|
||||
}
|
||||
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansPagingRequest host_paging = {};
|
||||
if (paging)
|
||||
{
|
||||
std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
|
||||
}
|
||||
|
||||
SceNpClansSearchableName host_search = {};
|
||||
std::memcpy(&host_search, search.get_ptr(), sizeof(SceNpClansSearchableName));
|
||||
|
||||
SceNpClansClanBasicInfo host_results[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {};
|
||||
SceNpClansPagingResult host_pageResult = {};
|
||||
|
||||
SceNpClansError ret = clans_manager.client->clan_search(handle, &host_paging, &host_search, host_results, &host_pageResult);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (results && host_pageResult.count > 0)
|
||||
{
|
||||
std::memcpy(results.get_ptr(), host_results, sizeof(SceNpClansClanBasicInfo) * host_pageResult.count);
|
||||
}
|
||||
std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansGetClanInfo(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::ptr<SceNpClansClanInfo> info)
|
||||
error_code sceNpClansGetClanInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::ptr<SceNpClansClanInfo> info)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansGetClanInfo(handle=*0x%x, clanId=%d, info=*0x%x)", handle, clanId, info);
|
||||
sceNpClans.warning("sceNpClansGetClanInfo(handle=*0x%x, clanId=*0x%x, info=*0x%x)", handle, clanId, info);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -310,12 +422,24 @@ error_code sceNpClansGetClanInfo(vm::ptr<SceNpClansRequestHandle> handle, SceNpC
|
|||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansClanInfo host_info = {};
|
||||
|
||||
SceNpClansError ret = clans_manager.client->get_clan_info(handle, clanId, &host_info);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::memcpy(info.get_ptr(), &host_info, sizeof(SceNpClansClanInfo));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansUpdateClanInfo(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansUpdatableClanInfo> info)
|
||||
error_code sceNpClansUpdateClanInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansUpdatableClanInfo> info)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansUpdateClanInfo(handle=*0x%x, clanId=%d, info=*0x%x)", handle, clanId, info);
|
||||
sceNpClans.warning("sceNpClansUpdateClanInfo(handle=*0x%x, clanId=*0x%x, info=*0x%x)", handle, clanId, info);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -328,17 +452,24 @@ error_code sceNpClansUpdateClanInfo(vm::ptr<SceNpClansRequestHandle> handle, Sce
|
|||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
//if (info->something > X)
|
||||
//{
|
||||
// return SCE_NP_CLANS_ERROR_EXCEEDS_MAX;
|
||||
//}
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansUpdatableClanInfo host_info = {};
|
||||
std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableClanInfo));
|
||||
|
||||
SceNpClansError ret = clans_manager.client->update_clan_info(nph, handle, clanId, &host_info);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansGetMemberList(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, SceNpClansMemberStatus status, vm::ptr<SceNpClansMemberEntry> memList, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
error_code sceNpClansGetMemberList(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, SceNpClansMemberStatus status, vm::ptr<SceNpClansMemberEntry> memList, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansGetMemberList(handle=*0x%x, clanId=%d, paging=*0x%x, status=%d, memList=*0x%x, pageResult=*0x%x)", handle, clanId, paging, status, memList, pageResult);
|
||||
sceNpClans.warning("sceNpClansGetMemberList(handle=*0x%x, clanId=*0x%x, paging=*0x%x, status=0x%x, memList=*0x%x, pageResult=*0x%x)", handle, clanId, paging, status, memList, pageResult);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -358,12 +489,36 @@ error_code sceNpClansGetMemberList(vm::ptr<SceNpClansRequestHandle> handle, SceN
|
|||
}
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansPagingRequest host_paging = {};
|
||||
if (paging)
|
||||
{
|
||||
std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
|
||||
}
|
||||
|
||||
SceNpClansMemberEntry host_memList_addr[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {};
|
||||
SceNpClansPagingResult host_pageResult = {};
|
||||
|
||||
SceNpClansError ret = clans_manager.client->get_member_list(nph, handle, clanId, &host_paging, status, host_memList_addr, &host_pageResult);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (memList && host_pageResult.count > 0)
|
||||
{
|
||||
std::memcpy(memList.get_ptr(), host_memList_addr, sizeof(SceNpClansMemberEntry) * host_pageResult.count);
|
||||
}
|
||||
std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansGetMemberInfo(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::ptr<SceNpClansMemberEntry> memInfo)
|
||||
error_code sceNpClansGetMemberInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::ptr<SceNpClansMemberEntry> memInfo)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansGetMemberInfo(handle=*0x%x, clanId=%d, npid=*0x%x, memInfo=*0x%x)", handle, clanId, npid, memInfo);
|
||||
sceNpClans.warning("sceNpClansGetMemberInfo(handle=*0x%x, clanId=*0x%x, npid=*0x%x, memInfo=*0x%x)", handle, clanId, npid, memInfo);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -375,12 +530,28 @@ error_code sceNpClansGetMemberInfo(vm::ptr<SceNpClansRequestHandle> handle, SceN
|
|||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpId host_npid = {};
|
||||
std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
|
||||
|
||||
SceNpClansMemberEntry host_memInfo = {};
|
||||
|
||||
SceNpClansError ret = clans_manager.client->get_member_info(nph, handle, clanId, host_npid, &host_memInfo);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::memcpy(memInfo.get_ptr(), &host_memInfo, sizeof(SceNpClansMemberEntry));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansUpdateMemberInfo(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansUpdatableMemberInfo> info)
|
||||
error_code sceNpClansUpdateMemberInfo(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansUpdatableMemberInfo> info)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansUpdateMemberInfo(handle=*0x%x, clanId=%d, memInfo=*0x%x)", handle, clanId, info);
|
||||
sceNpClans.warning("sceNpClansUpdateMemberInfo(handle=*0x%x, clanId=*0x%x, info=*0x%x)", handle, clanId, info);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -389,21 +560,27 @@ error_code sceNpClansUpdateMemberInfo(vm::ptr<SceNpClansRequestHandle> handle, S
|
|||
|
||||
if (!info)
|
||||
{
|
||||
// TODO: add more checks for info
|
||||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
//if (info->something > X)
|
||||
//{
|
||||
// return SCE_NP_CLANS_ERROR_EXCEEDS_MAX;
|
||||
//}
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansUpdatableMemberInfo host_info = {};
|
||||
std::memcpy(&host_info, info.get_ptr(), sizeof(SceNpClansUpdatableMemberInfo));
|
||||
|
||||
SceNpClansError ret = clans_manager.client->update_member_info(nph, handle, clanId, &host_info);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansChangeMemberRole(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, u32 role)
|
||||
error_code sceNpClansChangeMemberRole(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, u32 role)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansChangeMemberRole(handle=*0x%x, clanId=%d, npid=*0x%x, role=%d)", handle, clanId, npid, role);
|
||||
sceNpClans.warning("sceNpClansChangeMemberRole(handle=*0x%x, clanId=*0x%x, npid=*0x%x, role=0x%x)", handle, clanId, npid, role);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -415,10 +592,23 @@ error_code sceNpClansChangeMemberRole(vm::ptr<SceNpClansRequestHandle> handle, S
|
|||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpId host_npid = {};
|
||||
std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
|
||||
|
||||
SceNpClansError ret = clans_manager.client->change_member_role(nph, handle, clanId, host_npid, static_cast<SceNpClansMemberRole>(role));
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansGetAutoAcceptStatus(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::ptr<b8> enable)
|
||||
// TODO: no struct currently implements `autoAccept` as a field
|
||||
error_code sceNpClansGetAutoAcceptStatus(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::ptr<b8> enable)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansGetAutoAcceptStatus(handle=*0x%x, clanId=%d, enable=*0x%x)", handle, clanId, enable);
|
||||
|
||||
|
|
@ -435,7 +625,8 @@ error_code sceNpClansGetAutoAcceptStatus(vm::ptr<SceNpClansRequestHandle> handle
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansUpdateAutoAcceptStatus(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, b8 enable)
|
||||
// TODO: no struct currently implements `autoAccept` as a field
|
||||
error_code sceNpClansUpdateAutoAcceptStatus(SceNpClansRequestHandle handle, SceNpClanId clanId, b8 enable)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansUpdateAutoAcceptStatus(handle=*0x%x, clanId=%d, enable=%d)", handle, clanId, enable);
|
||||
|
||||
|
|
@ -447,33 +638,51 @@ error_code sceNpClansUpdateAutoAcceptStatus(vm::ptr<SceNpClansRequestHandle> han
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansJoinClan(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId)
|
||||
error_code sceNpClansJoinClan(SceNpClansRequestHandle handle, SceNpClanId clanId)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansJoinClan(handle=*0x%x, clanId=%d)", handle, clanId);
|
||||
sceNpClans.warning("sceNpClansJoinClan(handle=*0x%x, clanId=*0x%x)", handle, clanId);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansError ret = clans_manager.client->join_clan(nph, handle, clanId);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansLeaveClan(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId)
|
||||
error_code sceNpClansLeaveClan(SceNpClansRequestHandle handle, SceNpClanId clanId)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansLeaveClan(handle=*0x%x, clanId=%d)", handle, clanId);
|
||||
sceNpClans.warning("sceNpClansLeaveClan(handle=*0x%x, clanId=*0x%x)", handle, clanId);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansError ret = clans_manager.client->leave_clan(nph, handle, clanId);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansKickMember(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message)
|
||||
error_code sceNpClansKickMember(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansKickMember(handle=*0x%x, clanId=%d, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message);
|
||||
sceNpClans.warning("sceNpClansKickMember(handle=*0x%x, clanId=*0x%x, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -493,12 +702,30 @@ error_code sceNpClansKickMember(vm::ptr<SceNpClansRequestHandle> handle, SceNpCl
|
|||
}
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpId host_npid = {};
|
||||
std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
|
||||
|
||||
SceNpClansMessage host_message = {};
|
||||
if (message)
|
||||
{
|
||||
std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
|
||||
}
|
||||
|
||||
SceNpClansError ret = clans_manager.client->kick_member(nph, handle, clanId, host_npid, &host_message);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansSendInvitation(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message)
|
||||
error_code sceNpClansSendInvitation(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansSendInvitation(handle=*0x%x, clanId=%d, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message);
|
||||
sceNpClans.warning("sceNpClansSendInvitation(handle=*0x%x, clanId=*0x%x, npid=*0x%x, message=*0x%x)", handle, clanId, npid, message);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -518,12 +745,30 @@ error_code sceNpClansSendInvitation(vm::ptr<SceNpClansRequestHandle> handle, Sce
|
|||
}
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpId host_npid = {};
|
||||
std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
|
||||
|
||||
SceNpClansMessage host_message = {};
|
||||
if (message)
|
||||
{
|
||||
std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
|
||||
}
|
||||
|
||||
SceNpClansError ret = clans_manager.client->send_invitation(nph, handle, clanId, host_npid, &host_message);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansCancelInvitation(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid)
|
||||
error_code sceNpClansCancelInvitation(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansCancelInvitation(handle=*0x%x, clanId=%d, npid=*0x%x)", handle, clanId, npid);
|
||||
sceNpClans.warning("sceNpClansCancelInvitation(handle=*0x%x, clanId=*0x%x, npid=*0x%x)", handle, clanId, npid);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -535,12 +780,24 @@ error_code sceNpClansCancelInvitation(vm::ptr<SceNpClansRequestHandle> handle, S
|
|||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpId host_npid = {};
|
||||
std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
|
||||
|
||||
SceNpClansError ret = clans_manager.client->cancel_invitation(nph, handle, clanId, host_npid);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansSendInvitationResponse(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansMessage> message, b8 accept)
|
||||
error_code sceNpClansSendInvitationResponse(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansMessage> message, b8 accept)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansSendInvitationResponse(handle=*0x%x, clanId=%d, message=*0x%x, accept=%d)", handle, clanId, message, accept);
|
||||
sceNpClans.warning("sceNpClansSendInvitationResponse(handle=*0x%x, clanId=*0x%x, message=*0x%x, accept=%d)", handle, clanId, message, accept);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -555,12 +812,32 @@ error_code sceNpClansSendInvitationResponse(vm::ptr<SceNpClansRequestHandle> han
|
|||
}
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansMessage host_message = {};
|
||||
if (message)
|
||||
{
|
||||
std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
|
||||
}
|
||||
|
||||
if (message)
|
||||
{
|
||||
std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
|
||||
}
|
||||
|
||||
SceNpClansError ret = clans_manager.client->send_invitation_response(nph, handle, clanId, &host_message, accept);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansSendMembershipRequest(vm::ptr<SceNpClansRequestHandle> handle, u32 clanId, vm::cptr<SceNpClansMessage> message)
|
||||
error_code sceNpClansSendMembershipRequest(SceNpClansRequestHandle handle, u32 clanId, vm::cptr<SceNpClansMessage> message)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansSendMembershipRequest(handle=*0x%x, clanId=%d, message=*0x%x)", handle, clanId, message);
|
||||
sceNpClans.warning("sceNpClansSendMembershipRequest(handle=*0x%x, clanId=*0x%x, message=*0x%x)", handle, clanId, message);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -575,24 +852,49 @@ error_code sceNpClansSendMembershipRequest(vm::ptr<SceNpClansRequestHandle> hand
|
|||
}
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansMessage host_message = {};
|
||||
if (message)
|
||||
{
|
||||
std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
|
||||
}
|
||||
|
||||
SceNpClansError ret = clans_manager.client->request_membership(nph, handle, clanId, &host_message);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansCancelMembershipRequest(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId)
|
||||
error_code sceNpClansCancelMembershipRequest(SceNpClansRequestHandle handle, SceNpClanId clanId)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansCancelMembershipRequest(handle=*0x%x, clanId=%d)", handle, clanId);
|
||||
sceNpClans.warning("sceNpClansCancelMembershipRequest(handle=*0x%x, clanId=*0x%x)", handle, clanId);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansError ret = clans_manager.client->cancel_request_membership(nph, handle, clanId);
|
||||
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansSendMembershipResponse(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message, b8 allow)
|
||||
error_code sceNpClansSendMembershipResponse(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> npid, vm::cptr<SceNpClansMessage> message, b8 allow)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansSendMembershipResponse(handle=*0x%x, clanId=%d, npid=*0x%x, message=*0x%x, allow=%d)", handle, clanId, npid, message, allow);
|
||||
sceNpClans.warning("sceNpClansSendMembershipResponse(handle=*0x%x, clanId=*0x%x, npid=*0x%x, message=*0x%x, allow=%d)", handle, clanId, npid, message, allow);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -612,12 +914,30 @@ error_code sceNpClansSendMembershipResponse(vm::ptr<SceNpClansRequestHandle> han
|
|||
}
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpId host_npid = {};
|
||||
std::memcpy(&host_npid, npid.get_ptr(), sizeof(SceNpId));
|
||||
|
||||
SceNpClansMessage host_message = {};
|
||||
if (message)
|
||||
{
|
||||
std::memcpy(&host_message, message.get_ptr(), sizeof(SceNpClansMessage));
|
||||
}
|
||||
|
||||
SceNpClansError ret = clans_manager.client->send_membership_response(nph, handle, clanId, host_npid, &host_message, allow);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansGetBlacklist(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansBlacklistEntry> bl, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
error_code sceNpClansGetBlacklist(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansBlacklistEntry> bl, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansGetBlacklist(handle=*0x%x, clanId=%d, paging=*0x%x, bl=*0x%x, pageResult=*0x%x)", handle, clanId, paging, bl, pageResult);
|
||||
sceNpClans.warning("sceNpClansGetBlacklist(handle=*0x%x, clanId=*0x%x, paging=*0x%x, bl=*0x%x, pageResult=*0x%x)", handle, clanId, paging, bl, pageResult);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -637,53 +957,101 @@ error_code sceNpClansGetBlacklist(vm::ptr<SceNpClansRequestHandle> handle, SceNp
|
|||
}
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpClansPagingRequest host_paging = {};
|
||||
if (paging)
|
||||
{
|
||||
std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
|
||||
}
|
||||
|
||||
SceNpClansBlacklistEntry host_blacklist[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {};
|
||||
SceNpClansPagingResult host_pageResult = {};
|
||||
|
||||
SceNpClansError ret = clans_manager.client->get_blacklist(nph, handle, clanId, &host_paging, host_blacklist, &host_pageResult);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bl && host_pageResult.count > 0)
|
||||
{
|
||||
std::memcpy(bl.get_ptr(), host_blacklist, sizeof(SceNpClansBlacklistEntry) * host_pageResult.count);
|
||||
}
|
||||
std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansAddBlacklistEntry(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid)
|
||||
error_code sceNpClansAddBlacklistEntry(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> member)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansAddBlacklistEntry(handle=*0x%x, clanId=%d, npid=*0x%x)", handle, clanId, npid);
|
||||
sceNpClans.warning("sceNpClansAddBlacklistEntry(handle=*0x%x, clanId=*0x%x, member=*0x%x)", handle, clanId, member);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!npid)
|
||||
if (!member)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpId host_member = {};
|
||||
std::memcpy(&host_member, member.get_ptr(), sizeof(SceNpId));
|
||||
|
||||
SceNpClansError ret = clans_manager.client->add_blacklist_entry(nph, handle, clanId, host_member);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansRemoveBlacklistEntry(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpId> npid)
|
||||
error_code sceNpClansRemoveBlacklistEntry(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpId> member)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansRemoveBlacklistEntry(handle=*0x%x, clanId=%d, npid=*0x%x)", handle, clanId, npid);
|
||||
sceNpClans.warning("sceNpClansRemoveBlacklistEntry(handle=*0x%x, clanId=*0x%x, member=*0x%x)", handle, clanId, member);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!npid)
|
||||
if (!member)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
|
||||
SceNpId host_member = {};
|
||||
std::memcpy(&host_member, member.get_ptr(), sizeof(SceNpId));
|
||||
|
||||
SceNpClansError ret = clans_manager.client->remove_blacklist_entry(nph, handle, clanId, host_member);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansRetrieveAnnouncements(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mlist, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
error_code sceNpClansRetrieveAnnouncements(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mlist, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansRetrieveAnnouncements(handle=*0x%x, clanId=%d, paging=*0x%x, mlist=*0x%x, pageResult=*0x%x)", handle, clanId, paging, mlist, pageResult);
|
||||
sceNpClans.warning("sceNpClansRetrieveAnnouncements(handle=*0x%x, clanId=*0x%x, paging=*0x%x, mlist=*0x%x, pageResult=*0x%x)", handle, clanId, paging, mlist, pageResult);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!pageResult || (paging && !mlist)) // TODO: confirm
|
||||
if (!pageResult || (paging && !mlist) || clanId == UINT32_MAX) // TODO: confirm
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
|
@ -696,12 +1064,36 @@ error_code sceNpClansRetrieveAnnouncements(vm::ptr<SceNpClansRequestHandle> hand
|
|||
}
|
||||
}
|
||||
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
SceNpClansPagingRequest host_paging = {};
|
||||
if (paging)
|
||||
{
|
||||
std::memcpy(&host_paging, paging.get_ptr(), sizeof(SceNpClansPagingRequest));
|
||||
}
|
||||
|
||||
SceNpClansMessageEntry host_announcements[SCE_NP_CLANS_PAGING_REQUEST_PAGE_MAX] = {};
|
||||
SceNpClansPagingResult host_pageResult = {};
|
||||
|
||||
SceNpClansError ret = clans_manager.client->retrieve_announcements(nph, handle, clanId, &host_paging, host_announcements, &host_pageResult);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mlist && host_pageResult.count > 0)
|
||||
{
|
||||
std::memcpy(mlist.get_ptr(), host_announcements, sizeof(SceNpClansMessageEntry) * host_pageResult.count);
|
||||
}
|
||||
std::memcpy(pageResult.get_ptr(), &host_pageResult, sizeof(SceNpClansPagingResult));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansPostAnnouncement(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansMessage> message, vm::cptr<SceNpClansMessageData> data, u32 duration, vm::ptr<SceNpClansMessageId> mId)
|
||||
error_code sceNpClansPostAnnouncement(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansMessage> message, vm::cptr<SceNpClansMessageData> data, u32 duration, vm::ptr<SceNpClansMessageId> mId)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansPostAnnouncement(handle=*0x%x, clanId=%d, message=*0x%x, data=*0x%x, duration=%d, mId=*0x%x)", handle, clanId, message, data, duration, mId);
|
||||
sceNpClans.warning("sceNpClansPostAnnouncement(handle=*0x%x, clanId=*0x%x, message=*0x%x, data=*0x%x, duration=*0x%x, mId=*0x%x)", handle, clanId, message, data, duration, mId);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
|
|
@ -713,32 +1105,57 @@ error_code sceNpClansPostAnnouncement(vm::ptr<SceNpClansRequestHandle> handle, S
|
|||
return SCE_NP_CLANS_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!data) // TODO verify
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (strlen(message->body) > SCE_NP_CLANS_ANNOUNCEMENT_MESSAGE_BODY_MAX_LENGTH || strlen(message->subject) > SCE_NP_CLANS_MESSAGE_SUBJECT_MAX_LENGTH) // TODO: correct max?
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
SceNpClansMessage host_announcement = {};
|
||||
std::memcpy(&host_announcement, message.get_ptr(), sizeof(SceNpClansMessage));
|
||||
|
||||
SceNpClansMessageData host_data = {};
|
||||
if (data)
|
||||
{
|
||||
std::memcpy(&host_data, data.get_ptr(), sizeof(SceNpClansMessageData));
|
||||
}
|
||||
|
||||
SceNpClansMessageId host_announcementId = 0;
|
||||
SceNpClansError ret = clans_manager.client->post_announcement(nph, handle, clanId, &host_announcement, &host_data, duration, &host_announcementId);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
*mId = host_announcementId;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansRemoveAnnouncement(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, SceNpClansMessageId mId)
|
||||
error_code sceNpClansRemoveAnnouncement(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClansMessageId mId)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansPostAnnouncement(handle=*0x%x, clanId=%d, mId=%d)", handle, clanId, mId);
|
||||
sceNpClans.warning("sceNpClansRemoveAnnouncement(handle=*0x%x, clanId=*0x%x, mId=*0x%x)", handle, clanId, mId);
|
||||
|
||||
if (!g_fxo->get<sce_np_clans_manager>().is_initialized)
|
||||
{
|
||||
return SCE_NP_CLANS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
auto& clans_manager = g_fxo->get<sce_np_clans_manager>();
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
SceNpClansError ret = clans_manager.client->delete_announcement(nph, handle, clanId, mId);
|
||||
if (ret != SCE_NP_CLANS_SUCCESS)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansPostChallenge(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr<SceNpClansMessage> message, vm::cptr<SceNpClansMessageData> data, u32 duration, vm::ptr<SceNpClansMessageId> mId)
|
||||
error_code sceNpClansPostChallenge(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr<SceNpClansMessage> message, vm::cptr<SceNpClansMessageData> data, u32 duration, vm::ptr<SceNpClansMessageId> mId)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansPostChallenge(handle=*0x%x, clanId=%d, targetClan=%d, message=*0x%x, data=*0x%x, duration=%d, mId=*0x%x)", handle, clanId, targetClan, message, data, duration, mId);
|
||||
|
||||
|
|
@ -765,7 +1182,7 @@ error_code sceNpClansPostChallenge(vm::ptr<SceNpClansRequestHandle> handle, SceN
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansRetrievePostedChallenges(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mList, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
error_code sceNpClansRetrievePostedChallenges(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClanId targetClan, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mList, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansRetrievePostedChallenges(handle=*0x%x, clanId=%d, targetClan=%d, paging=*0x%x, mList=*0x%x, pageResult=*0x%x)", handle, clanId, targetClan, paging, mList, pageResult);
|
||||
|
||||
|
|
@ -790,7 +1207,7 @@ error_code sceNpClansRetrievePostedChallenges(vm::ptr<SceNpClansRequestHandle> h
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansRemovePostedChallenge(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, SceNpClanId targetClan, SceNpClansMessageId mId)
|
||||
error_code sceNpClansRemovePostedChallenge(SceNpClansRequestHandle handle, SceNpClanId clanId, SceNpClanId targetClan, SceNpClansMessageId mId)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansRemovePostedChallenge(handle=*0x%x, clanId=%d, targetClan=%d, mId=%d)", handle, clanId, targetClan, mId);
|
||||
|
||||
|
|
@ -802,7 +1219,7 @@ error_code sceNpClansRemovePostedChallenge(vm::ptr<SceNpClansRequestHandle> hand
|
|||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpClansRetrieveChallenges(vm::ptr<SceNpClansRequestHandle> handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mList, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
error_code sceNpClansRetrieveChallenges(SceNpClansRequestHandle handle, SceNpClanId clanId, vm::cptr<SceNpClansPagingRequest> paging, vm::ptr<SceNpClansMessageEntry> mList, vm::ptr<SceNpClansPagingResult> pageResult)
|
||||
{
|
||||
sceNpClans.todo("sceNpClansRetrieveChallenges(handle=*0x%x, clanId=%d, paging=*0x%x, mList=*0x%x, pageResult=*0x%x)", handle, clanId, paging, mList, pageResult);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
// Return codes
|
||||
enum SceNpClansError : u32
|
||||
{
|
||||
SCE_NP_CLANS_SUCCESS = CELL_OK,
|
||||
|
||||
SCE_NP_CLANS_ERROR_ALREADY_INITIALIZED = 0x80022701,
|
||||
SCE_NP_CLANS_ERROR_NOT_INITIALIZED = 0x80022702,
|
||||
SCE_NP_CLANS_ERROR_NOT_SUPPORTED = 0x80022703,
|
||||
|
|
@ -138,7 +140,7 @@ enum
|
|||
};
|
||||
|
||||
// Request handle for clan API
|
||||
using SceNpClansRequestHandle = vm::ptr<struct SceNpClansRequest>;
|
||||
using SceNpClansRequestHandle = u32;
|
||||
|
||||
// Paging request structure
|
||||
struct SceNpClansPagingRequest
|
||||
|
|
@ -159,8 +161,8 @@ struct SceNpClansClanBasicInfo
|
|||
{
|
||||
be_t<u32> clanId;
|
||||
be_t<u32> numMembers;
|
||||
s8 name[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1];
|
||||
s8 tag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1];
|
||||
char name[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1];
|
||||
char tag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1];
|
||||
u8 reserved[2];
|
||||
};
|
||||
|
||||
|
|
@ -197,7 +199,7 @@ struct SceNpClansSearchableProfile
|
|||
be_t<s32> intAttr2SearchOp;
|
||||
be_t<s32> intAttr3SearchOp;
|
||||
be_t<s32> binAttr1SearchOp;
|
||||
s8 tag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1];
|
||||
char tag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1];
|
||||
u8 reserved[3];
|
||||
};
|
||||
|
||||
|
|
@ -205,7 +207,7 @@ struct SceNpClansSearchableProfile
|
|||
struct SceNpClansSearchableName
|
||||
{
|
||||
be_t<s32> nameSearchOp;
|
||||
s8 name[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1];
|
||||
char name[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1];
|
||||
u8 reserved[3];
|
||||
};
|
||||
|
||||
|
|
@ -213,7 +215,7 @@ struct SceNpClansSearchableName
|
|||
struct SceNpClansUpdatableClanInfo
|
||||
{
|
||||
be_t<u32> fields;
|
||||
s8 description[SCE_NP_CLANS_CLAN_DESCRIPTION_MAX_LENGTH + 1];
|
||||
char description[SCE_NP_CLANS_CLAN_DESCRIPTION_MAX_LENGTH + 1];
|
||||
SceNpClansSearchableAttr attr;
|
||||
u8 binData1;
|
||||
be_t<u32> binData1Size;
|
||||
|
|
@ -233,8 +235,8 @@ struct SceNpClansUpdatableMemberInfo
|
|||
be_t<u32> fields;
|
||||
u8 binData1;
|
||||
be_t<u32> binData1Size;
|
||||
u8 binAttr1[SCE_NP_CLANS_CLAN_BINARY_ATTRIBUTE1_MAX_SIZE + 1];
|
||||
s8 description[SCE_NP_CLANS_MEMBER_DESCRIPTION_MAX_LENGTH + 1];
|
||||
u8 binAttr1[SCE_NP_CLANS_MEMBER_BINARY_ATTRIBUTE1_MAX_SIZE];
|
||||
char description[SCE_NP_CLANS_MEMBER_DESCRIPTION_MAX_LENGTH + 1];
|
||||
b8 allowMsg;
|
||||
u8 reserved[3];
|
||||
};
|
||||
|
|
@ -271,7 +273,7 @@ struct SceNpClansMessageEntry
|
|||
SceNpClansMessage message;
|
||||
SceNpClansMessageData data;
|
||||
SceNpId npid;
|
||||
u8 reserved[4];
|
||||
SceNpClanId postedBy;
|
||||
};
|
||||
|
||||
// Blacklist entry structure
|
||||
|
|
@ -280,10 +282,3 @@ struct SceNpClansBlacklistEntry
|
|||
SceNpId entry;
|
||||
SceNpId registeredBy;
|
||||
};
|
||||
|
||||
// fxm objects
|
||||
|
||||
struct sce_np_clans_manager
|
||||
{
|
||||
atomic_t<bool> is_initialized = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3931,6 +3931,17 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
|||
continue;
|
||||
}
|
||||
|
||||
for (auto it2 = it->second.begin(); it2 != it->second.end();)
|
||||
{
|
||||
if (*it2 < lsa || *it2 >= limit)
|
||||
{
|
||||
it2 = it->second.erase(it2);
|
||||
continue;
|
||||
}
|
||||
|
||||
it2++;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
|
|
@ -7320,7 +7331,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
|||
}
|
||||
|
||||
// spu_log.success("PUTLLC0 Pattern Detected! (put_pc=0x%x, %s) (putllc0=%d, putllc16+0=%d, all=%d)", pattern.put_pc, func_hash, ++stats.nowrite, ++stats.single, +stats.all);
|
||||
// add_pattern(false, inst_attr::putllc0, pattern.put_pc - lsa, value.data);
|
||||
// add_pattern(inst_attr::putllc0, pattern.put_pc - lsa, value.data);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -7411,7 +7422,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
|||
|
||||
if (allow_pattern)
|
||||
{
|
||||
add_pattern(false, inst_attr::putllc16, pattern.put_pc - result.entry_point, value.data);
|
||||
add_pattern(inst_attr::putllc16, pattern.put_pc - result.entry_point, value.data);
|
||||
}
|
||||
|
||||
spu_log.success("PUTLLC16 Pattern Detected! (mem_count=%d, put_pc=0x%x, pc_rel=%d, offset=0x%x, const=%u, two_regs=%d, reg=%u, runtime=%d, 0x%x-%s, pattern-hash=%s) (putllc0=%d, putllc16+0=%d, all=%d)"
|
||||
|
|
@ -7433,7 +7444,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
|||
|
||||
if (inst_attr attr = m_inst_attrs[(read_pc - entry_point) / 4]; attr == inst_attr::none)
|
||||
{
|
||||
add_pattern(false, inst_attr::rchcnt_loop, read_pc - result.entry_point, 0);
|
||||
add_pattern(inst_attr::rchcnt_loop, read_pc - result.entry_point, 0);
|
||||
|
||||
spu_log.error("Channel Loop Pattern Detected! Report to developers! (read_pc=0x%x, branch_pc=0x%x, branch_target=0x%x, 0x%x-%s)", read_pc, pattern.branch_pc, pattern.branch_target, entry_point, func_hash);
|
||||
}
|
||||
|
|
@ -8519,7 +8530,7 @@ std::array<reg_state_t, s_reg_max>& block_reg_info::evaluate_start_state(const s
|
|||
return walkby_state;
|
||||
}
|
||||
|
||||
void spu_recompiler_base::add_pattern(bool fill_all, inst_attr attr, u32 start, u64 info)
|
||||
void spu_recompiler_base::add_pattern(inst_attr attr, u32 start, u64 info)
|
||||
{
|
||||
m_patterns[start] = pattern_info{info};
|
||||
m_inst_attrs[start / 4] = attr;
|
||||
|
|
|
|||
|
|
@ -2617,7 +2617,11 @@ public:
|
|||
{
|
||||
if (b2 != bqbi)
|
||||
{
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
auto ins = &*b2->block->getFirstNonPHIIt();
|
||||
#else
|
||||
auto ins = b2->block->getFirstNonPHI();
|
||||
#endif
|
||||
|
||||
if (b2->bb->preds.size() == 1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -402,7 +402,7 @@ protected:
|
|||
|
||||
std::unordered_map<u32, pattern_info> m_patterns;
|
||||
|
||||
void add_pattern(bool fill_all, inst_attr attr, u32 start, u64 info);
|
||||
void add_pattern(inst_attr attr, u32 start, u64 info);
|
||||
|
||||
private:
|
||||
// For private use
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ waitpkg_func static void __tpause(u32 cycles, u32 cstate)
|
|||
|
||||
namespace vm
|
||||
{
|
||||
std::array<atomic_t<reservation_waiter_t>, 2048> g_resrv_waiters_count{};
|
||||
std::array<atomic_t<reservation_waiter_t, 128>, 1024> g_resrv_waiters_count{};
|
||||
}
|
||||
|
||||
void do_cell_atomic_128_store(u32 addr, const void* to_write);
|
||||
|
|
@ -499,7 +499,7 @@ const spu_decoder<spu_itype> s_spu_itype;
|
|||
|
||||
namespace vm
|
||||
{
|
||||
extern atomic_t<u64, 64> g_range_lock_set[64];
|
||||
extern atomic_t<u64, 128> g_range_lock_set[64];
|
||||
|
||||
// Defined here for performance reasons
|
||||
writer_lock::~writer_lock() noexcept
|
||||
|
|
@ -2000,7 +2000,7 @@ void spu_thread::do_dma_transfer(spu_thread* _this, const spu_mfc_cmd& args, u8*
|
|||
|
||||
cpu_thread* _cpu = _this ? _this : get_current_cpu_thread();
|
||||
|
||||
atomic_t<u64, 64>* range_lock = nullptr;
|
||||
atomic_t<u64, 128>* range_lock = nullptr;
|
||||
|
||||
if (!_this) [[unlikely]]
|
||||
{
|
||||
|
|
@ -4928,12 +4928,12 @@ bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data, u32 cu
|
|||
return !res;
|
||||
}
|
||||
|
||||
bool spu_thread::reservation_check(u32 addr, u32 hash, atomic_t<u64, 64>* range_lock)
|
||||
bool spu_thread::reservation_check(u32 addr, u32 hash, atomic_t<u64, 128>* range_lock)
|
||||
{
|
||||
if ((addr >> 28) < 2 || (addr >> 28) == 0xd)
|
||||
{
|
||||
// Always-allocated memory does not need strict checking (vm::main or vm::stack)
|
||||
return compute_rdata_hash32(*vm::get_super_ptr<decltype(rdata)>(addr)) == hash;
|
||||
return compute_rdata_hash32(*vm::get_super_ptr<decltype(rdata)>(addr)) != hash;
|
||||
}
|
||||
|
||||
// Ensure data is allocated (HACK: would raise LR event if not)
|
||||
|
|
@ -5067,6 +5067,8 @@ void spu_thread::deregister_cache_line_waiter(usz index)
|
|||
return;
|
||||
}
|
||||
|
||||
ensure(index < std::size(g_spu_waiters_by_value));
|
||||
|
||||
g_spu_waiters_by_value[index].atomic_op([](u64& x)
|
||||
{
|
||||
x--;
|
||||
|
|
|
|||
|
|
@ -708,7 +708,7 @@ public:
|
|||
const decltype(rdata)* resrv_mem{};
|
||||
|
||||
// Range Lock pointer
|
||||
atomic_t<u64, 64>* range_lock{};
|
||||
atomic_t<u64, 128>* range_lock{};
|
||||
|
||||
u32 srr0 = 0;
|
||||
u32 ch_tag_upd = 0;
|
||||
|
|
@ -903,7 +903,7 @@ public:
|
|||
// It is safe to use on any address, even if not directly accessed by SPU (so it's slower)
|
||||
// Optionally pass a known allocated address for internal optimization (the current Effective-Address of the MFC command)
|
||||
bool reservation_check(u32 addr, const decltype(rdata)& data, u32 current_eal = 0) const;
|
||||
static bool reservation_check(u32 addr, u32 hash, atomic_t<u64, 64>* range_lock);
|
||||
static bool reservation_check(u32 addr, u32 hash, atomic_t<u64, 128>* range_lock);
|
||||
usz register_cache_line_waiter(u32 addr);
|
||||
void deregister_cache_line_waiter(usz index);
|
||||
|
||||
|
|
@ -915,7 +915,7 @@ public:
|
|||
static atomic_t<u32> g_raw_spu_id[5];
|
||||
static atomic_t<u32> g_spu_work_count;
|
||||
|
||||
static atomic_t<u64> g_spu_waiters_by_value[6];
|
||||
static atomic_t<u64, 128> g_spu_waiters_by_value[6];
|
||||
|
||||
static u32 find_raw_spu(u32 id)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2228,11 +2228,8 @@ void lv2_obj::notify_all() noexcept
|
|||
break;
|
||||
}
|
||||
|
||||
if (cpu != &g_to_notify)
|
||||
{
|
||||
// Note: by the time of notification the thread could have been deallocated which is why the direct function is used
|
||||
atomic_wait_engine::notify_all(cpu);
|
||||
}
|
||||
// Note: by the time of notification the thread could have been deallocated which is why the direct function is used
|
||||
atomic_wait_engine::notify_all(cpu);
|
||||
}
|
||||
|
||||
g_to_notify[0] = nullptr;
|
||||
|
|
@ -2260,7 +2257,7 @@ void lv2_obj::notify_all() noexcept
|
|||
// There may be 6 waiters, but checking them all may be performance expensive
|
||||
// Instead, check 2 at max, but use the CPU ID index to tell which index to start checking so the work would be distributed across all threads
|
||||
|
||||
atomic_t<u64, 64>* range_lock = nullptr;
|
||||
atomic_t<u64, 128>* range_lock = nullptr;
|
||||
|
||||
if (cpu->get_class() == thread_class::spu)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -848,7 +848,7 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s3
|
|||
|
||||
fs::file file(local_path, open_mode);
|
||||
|
||||
if (!file && open_mode == fs::read && fs::g_tls_error == fs::error::noent)
|
||||
if (!file && open_mode == fs::read && fs::g_tls_error == fs::error::noent && mp.mp != &g_mp_sys_dev_hdd1)
|
||||
{
|
||||
// Try to gather split file (TODO)
|
||||
std::vector<fs::file> fragments;
|
||||
|
|
@ -1389,7 +1389,7 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
|
|||
}
|
||||
|
||||
// Add additional entries for split file candidates (while ends with .66600)
|
||||
while (data.back().name.ends_with(".66600"))
|
||||
while (mp.mp != &g_mp_sys_dev_hdd1 && data.back().name.ends_with(".66600"))
|
||||
{
|
||||
data.emplace_back(data.back()).name.resize(data.back().name.size() - 6);
|
||||
}
|
||||
|
|
@ -1505,6 +1505,7 @@ error_code sys_fs_closedir(ppu_thread& ppu, u32 fd)
|
|||
error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat> sb)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.warning("sys_fs_stat(path=%s, sb=*0x%x)", path, sb);
|
||||
|
||||
|
|
@ -1552,8 +1553,18 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
|
|||
// Try to analyse split file (TODO)
|
||||
u64 total_size = 0;
|
||||
|
||||
for (u32 i = 66601; i <= 66699; i++)
|
||||
// Use attributes from the first fragment (consistently with sys_fs_open+fstat
|
||||
fs::stat_t info_split{};
|
||||
if (mp.mp != &g_mp_sys_dev_hdd1 && fs::get_stat(local_path + ".66600", info_split) && !info_split.is_directory)
|
||||
{
|
||||
// Success
|
||||
total_size += info_split.size;
|
||||
}
|
||||
|
||||
for (u32 i = 66601; total_size && i <= 66699; i++)
|
||||
{
|
||||
info = {};
|
||||
|
||||
if (fs::get_stat(fmt::format("%s.%u", local_path, i), info) && !info.is_directory)
|
||||
{
|
||||
total_size += info.size;
|
||||
|
|
@ -1564,11 +1575,11 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
|
|||
}
|
||||
}
|
||||
|
||||
// Use attributes from the first fragment (consistently with sys_fs_open+fstat)
|
||||
if (fs::get_stat(local_path + ".66600", info) && !info.is_directory)
|
||||
if (total_size)
|
||||
{
|
||||
// Success
|
||||
info.size += total_size;
|
||||
info_split.size = total_size;
|
||||
info = info_split;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -2895,7 +2906,7 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr<char> path, s32 mode)
|
|||
|
||||
for (u32 i = 66601; i <= 66699; i++)
|
||||
{
|
||||
if (!fs::get_stat(fmt::format("%s.%u", local_path, i), info) && !info.is_directory)
|
||||
if (mp != &g_mp_sys_dev_hdd1 && !fs::get_stat(fmt::format("%s.%u", local_path, i), info) && !info.is_directory)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ error_code sys_mutex_unlock(ppu_thread& ppu, u32 mutex_id)
|
|||
const auto mutex = idm::check<lv2_obj, lv2_mutex>(mutex_id, [&, notify = lv2_obj::notify_all_t()](lv2_mutex& mutex) -> CellError
|
||||
{
|
||||
// At unlock, we have some time to do other jobs when the thread is unlikely to be in other critical sections
|
||||
notify.enqueue_on_top(vm::reservation_notifier_notify(ppu.res_notify, ppu.res_notify_time));
|
||||
notify.enqueue_on_top(vm::reservation_notifier_notify(ppu.res_notify, ppu.res_notify_time, true));
|
||||
|
||||
auto result = mutex.try_unlock(ppu);
|
||||
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ static void LIBUSB_CALL log_cb(libusb_context* /*ctx*/, enum libusb_log_level le
|
|||
if (!str)
|
||||
return;
|
||||
|
||||
const std::string msg = fmt::trim(str, " \t\n");
|
||||
const std::string_view msg = fmt::trim_sv(str, " \t\n");
|
||||
|
||||
switch (level)
|
||||
{
|
||||
|
|
@ -555,7 +555,8 @@ usb_handler_thread::usb_handler_thread()
|
|||
usb_devices.push_back(std::make_shared<usb_device_vfs>(usb_info, get_new_location()));
|
||||
}
|
||||
|
||||
const std::vector<std::string> devices_list = fmt::split(g_cfg.io.midi_devices.to_string(), { "@@@" });
|
||||
const std::string midi_devices = g_cfg.io.midi_devices.to_string();
|
||||
const std::vector<std::string_view> devices_list = fmt::split_sv(midi_devices, { "@@@" });
|
||||
for (usz index = 0; index < std::min(max_midi_devices, devices_list.size()); index++)
|
||||
{
|
||||
const midi_device device = midi_device::from_string(::at32(devices_list, index));
|
||||
|
|
|
|||
|
|
@ -874,12 +874,12 @@ void PadHandlerBase::set_raw_orientation(ps_move_data& move_data, f32 accel_x, f
|
|||
// The default position is flat on the ground, pointing forward.
|
||||
// The accelerometers constantly measure G forces.
|
||||
// The gyros measure changes in orientation and will reset when the device isn't moved anymore.
|
||||
move_data.accelerometer_x = -accel_x; // move_data: Increases if the device is rolled to the left
|
||||
move_data.accelerometer_y = accel_z; // move_data: Increases if the device is pitched upwards
|
||||
move_data.accelerometer_z = accel_y; // move_data: Increases if the device is moved upwards
|
||||
move_data.gyro_x = degree_to_rad(-gyro_x); // move_data: Increases if the device is pitched upwards
|
||||
move_data.gyro_y = degree_to_rad(gyro_z); // move_data: Increases if the device is rolled to the right
|
||||
move_data.gyro_z = degree_to_rad(-gyro_y); // move_data: Increases if the device is yawed to the left
|
||||
move_data.accelerometer.x() = -accel_x; // move_data: Increases if the device is rolled to the left
|
||||
move_data.accelerometer.y() = accel_z; // move_data: Increases if the device is pitched upwards
|
||||
move_data.accelerometer.z() = accel_y; // move_data: Increases if the device is moved upwards
|
||||
move_data.gyro.x() = degree_to_rad(-gyro_x); // move_data: Increases if the device is pitched upwards
|
||||
move_data.gyro.y() = degree_to_rad(gyro_z); // move_data: Increases if the device is rolled to the right
|
||||
move_data.gyro.z() = degree_to_rad(-gyro_y); // move_data: Increases if the device is yawed to the left
|
||||
}
|
||||
|
||||
void PadHandlerBase::set_raw_orientation(Pad& pad)
|
||||
|
|
@ -950,7 +950,7 @@ void PadDevice::update_orientation(ps_move_data& move_data)
|
|||
|
||||
// Get elapsed time since last update
|
||||
const u64 now_us = get_system_time();
|
||||
const float elapsed_sec = (last_ahrs_update_time_us == 0) ? 0.0f : ((now_us - last_ahrs_update_time_us) / 1'000'000.0f);
|
||||
const f32 elapsed_sec = (last_ahrs_update_time_us == 0) ? 0.0f : ((now_us - last_ahrs_update_time_us) / 1'000'000.0f);
|
||||
last_ahrs_update_time_us = now_us;
|
||||
|
||||
// The ps move handler's axis may differ from the Fusion axis, so we have to map them correctly.
|
||||
|
|
@ -959,17 +959,17 @@ void PadDevice::update_orientation(ps_move_data& move_data)
|
|||
|
||||
const FusionVector accelerometer{
|
||||
.axis {
|
||||
.x = -move_data.accelerometer_x,
|
||||
.y = +move_data.accelerometer_y,
|
||||
.z = +move_data.accelerometer_z
|
||||
.x = -move_data.accelerometer.x(),
|
||||
.y = +move_data.accelerometer.y(),
|
||||
.z = +move_data.accelerometer.z()
|
||||
}
|
||||
};
|
||||
|
||||
const FusionVector gyroscope{
|
||||
.axis {
|
||||
.x = +PadHandlerBase::rad_to_degree(move_data.gyro_x),
|
||||
.y = +PadHandlerBase::rad_to_degree(move_data.gyro_z),
|
||||
.z = -PadHandlerBase::rad_to_degree(move_data.gyro_y)
|
||||
.x = +PadHandlerBase::rad_to_degree(move_data.gyro.x()),
|
||||
.y = +PadHandlerBase::rad_to_degree(move_data.gyro.z()),
|
||||
.z = -PadHandlerBase::rad_to_degree(move_data.gyro.y())
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -979,9 +979,9 @@ void PadDevice::update_orientation(ps_move_data& move_data)
|
|||
{
|
||||
magnetometer = FusionVector{
|
||||
.axis {
|
||||
.x = move_data.magnetometer_x,
|
||||
.y = move_data.magnetometer_y,
|
||||
.z = move_data.magnetometer_z
|
||||
.x = move_data.magnetometer.x(),
|
||||
.y = move_data.magnetometer.y(),
|
||||
.z = move_data.magnetometer.z()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -995,4 +995,5 @@ void PadDevice::update_orientation(ps_move_data& move_data)
|
|||
move_data.quaternion[1] = quaternion.array[2];
|
||||
move_data.quaternion[2] = quaternion.array[3];
|
||||
move_data.quaternion[3] = quaternion.array[0];
|
||||
move_data.update_orientation(elapsed_sec);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ protected:
|
|||
std::set<T> key_codes;
|
||||
|
||||
const std::string& def = cfg_string.def;
|
||||
const std::vector<std::string> names = cfg_pad::get_buttons(cfg_string);
|
||||
const std::vector<std::string> names = cfg_pad::get_buttons(cfg_string.to_string());
|
||||
T def_code = umax;
|
||||
|
||||
for (const std::string& nam : names)
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ Note str_to_note(const std::string_view name)
|
|||
|
||||
std::optional<std::pair<Id, Note>> parse_midi_override(const std::string_view config)
|
||||
{
|
||||
auto split = fmt::split(config, {"="});
|
||||
const auto split = fmt::split_sv(config, {"="});
|
||||
if (split.size() != 2)
|
||||
{
|
||||
return {};
|
||||
|
|
@ -236,8 +236,9 @@ std::unordered_map<Id, Note> create_id_to_note_mapping()
|
|||
};
|
||||
|
||||
// Apply configured overrides.
|
||||
const std::vector<std::string> segments = fmt::split(g_cfg_rb3drums.midi_overrides.to_string(), {","});
|
||||
for (const std::string& segment : segments)
|
||||
const std::string midi_overrides = g_cfg_rb3drums.midi_overrides.to_string();
|
||||
const std::vector<std::string_view> segments = fmt::split_sv(midi_overrides, {","});
|
||||
for (const std::string_view& segment : segments)
|
||||
{
|
||||
if (const auto midi_override = parse_midi_override(segment))
|
||||
{
|
||||
|
|
@ -259,7 +260,7 @@ std::vector<u8> parse_combo(const std::string_view name, const std::string_view
|
|||
return {};
|
||||
}
|
||||
std::vector<u8> notes;
|
||||
const auto& note_names = fmt::split(csv, {","});
|
||||
const auto note_names = fmt::split_sv(csv, {","});
|
||||
for (const auto& note_name : note_names)
|
||||
{
|
||||
const auto note = str_to_note(note_name);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ void cfg_camera::save() const
|
|||
}
|
||||
}
|
||||
|
||||
cfg_camera::camera_setting cfg_camera::get_camera_setting(const std::string& camera, bool& success)
|
||||
cfg_camera::camera_setting cfg_camera::get_camera_setting(std::string_view camera, bool& success)
|
||||
{
|
||||
camera_setting setting;
|
||||
const std::string value = cameras.get_value(camera);
|
||||
|
|
@ -64,7 +64,7 @@ std::string cfg_camera::camera_setting::to_string() const
|
|||
return fmt::format("%d,%d,%f,%f,%d", width, height, min_fps, max_fps, format);
|
||||
}
|
||||
|
||||
void cfg_camera::camera_setting::from_string(const std::string& text)
|
||||
void cfg_camera::camera_setting::from_string(std::string_view text)
|
||||
{
|
||||
if (text.empty())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ struct cfg_camera final : cfg::node
|
|||
static constexpr u32 member_count = 5;
|
||||
|
||||
std::string to_string() const;
|
||||
void from_string(const std::string& text);
|
||||
void from_string(std::string_view text);
|
||||
};
|
||||
camera_setting get_camera_setting(const std::string& camera, bool& success);
|
||||
camera_setting get_camera_setting(std::string_view camera, bool& success);
|
||||
void set_camera_setting(const std::string& camera, const camera_setting& setting);
|
||||
|
||||
const std::string path;
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ void fmt_class_string<midi_device>::format(std::string& out, u64 arg)
|
|||
fmt::append(out, "%sßßß%s", obj.type, obj.name);
|
||||
}
|
||||
|
||||
midi_device midi_device::from_string(const std::string& str)
|
||||
midi_device midi_device::from_string(std::string_view str)
|
||||
{
|
||||
midi_device res{};
|
||||
|
||||
if (const std::vector<std::string> parts = fmt::split(str, {"ßßß"}); !parts.empty())
|
||||
if (const std::vector<std::string_view> parts = fmt::split_sv(str, {"ßßß"}); !parts.empty())
|
||||
{
|
||||
u64 result;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
static constexpr usz max_midi_devices = 3;
|
||||
|
||||
|
|
@ -17,5 +18,5 @@ struct midi_device
|
|||
midi_device_type type{};
|
||||
std::string name;
|
||||
|
||||
static midi_device from_string(const std::string& str);
|
||||
static midi_device from_string(std::string_view str);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
extern std::string g_input_config_override;
|
||||
|
||||
std::vector<std::string> cfg_pad::get_buttons(const std::string& str)
|
||||
std::vector<std::string> cfg_pad::get_buttons(std::string_view str)
|
||||
{
|
||||
std::vector<std::string> vec = fmt::split(str, {","});
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ struct cfg_pad final : cfg::node
|
|||
cfg_pad() {};
|
||||
cfg_pad(node* owner, const std::string& name) : cfg::node(owner, name) {}
|
||||
|
||||
static std::vector<std::string> get_buttons(const std::string& str);
|
||||
static std::vector<std::string> get_buttons(std::string_view str);
|
||||
static std::string get_buttons(std::vector<std::string> vec);
|
||||
|
||||
u8 get_motor_speed(VibrateMotor& motor, f32 multiplier) const;
|
||||
|
|
|
|||
|
|
@ -159,20 +159,6 @@ u32 get_axis_keycode(u32 offset, u16 value)
|
|||
}
|
||||
}
|
||||
|
||||
void ps_move_data::reset_sensors()
|
||||
{
|
||||
quaternion = default_quaternion;
|
||||
accelerometer_x = 0.0f;
|
||||
accelerometer_y = 0.0f;
|
||||
accelerometer_z = 0.0f;
|
||||
gyro_x = 0.0f;
|
||||
gyro_y = 0.0f;
|
||||
gyro_z = 0.0f;
|
||||
magnetometer_x = 0.0f;
|
||||
magnetometer_y = 0.0f;
|
||||
magnetometer_z = 0.0f;
|
||||
}
|
||||
|
||||
bool Pad::get_pressure_intensity_button_active(bool is_toggle_mode, u32 player_id)
|
||||
{
|
||||
if (m_pressure_intensity_button_index < 0)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
#include "util/types.hpp"
|
||||
#include "util/endian.hpp"
|
||||
#include "Emu/Io/pad_config_types.h"
|
||||
#include "pad_config_types.h"
|
||||
#include "ps_move_data.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
|
@ -469,38 +470,6 @@ struct VibrateMotor
|
|||
{}
|
||||
};
|
||||
|
||||
struct ps_move_data
|
||||
{
|
||||
u32 external_device_id = 0;
|
||||
std::array<u8, 38> external_device_read{}; // CELL_GEM_EXTERNAL_PORT_DEVICE_INFO_SIZE
|
||||
std::array<u8, 40> external_device_write{}; // CELL_GEM_EXTERNAL_PORT_OUTPUT_SIZE
|
||||
std::array<u8, 5> external_device_data{};
|
||||
bool external_device_connected = false;
|
||||
bool external_device_read_requested = false;
|
||||
bool external_device_write_requested = false;
|
||||
|
||||
bool calibration_requested = false;
|
||||
bool calibration_succeeded = false;
|
||||
|
||||
bool magnetometer_enabled = false;
|
||||
bool orientation_enabled = false;
|
||||
|
||||
static constexpr std::array<f32, 4> default_quaternion { 1.0f, 0.0f, 0.0f, 0.0f };
|
||||
std::array<f32, 4> quaternion = default_quaternion; // quaternion orientation (x,y,z,w) of controller relative to default (facing the camera with buttons up)
|
||||
f32 accelerometer_x = 0.0f; // linear velocity in m/s²
|
||||
f32 accelerometer_y = 0.0f; // linear velocity in m/s²
|
||||
f32 accelerometer_z = 0.0f; // linear velocity in m/s²
|
||||
f32 gyro_x = 0.0f; // angular velocity in rad/s
|
||||
f32 gyro_y = 0.0f; // angular velocity in rad/s
|
||||
f32 gyro_z = 0.0f; // angular velocity in rad/s
|
||||
f32 magnetometer_x = 0.0f;
|
||||
f32 magnetometer_y = 0.0f;
|
||||
f32 magnetometer_z = 0.0f;
|
||||
s16 temperature = 0;
|
||||
|
||||
void reset_sensors();
|
||||
};
|
||||
|
||||
struct Pad
|
||||
{
|
||||
const pad_handler m_pad_handler;
|
||||
|
|
|
|||
137
rpcs3/Emu/Io/ps_move_data.cpp
Normal file
137
rpcs3/Emu/Io/ps_move_data.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#include "stdafx.h"
|
||||
#include "ps_move_data.h"
|
||||
|
||||
const ps_move_data::vect<4> ps_move_data::default_quaternion = ps_move_data::vect<4>({ 0.0f, 0.0f, 0.0f, 1.0f });
|
||||
|
||||
ps_move_data::ps_move_data()
|
||||
: quaternion(default_quaternion)
|
||||
{
|
||||
}
|
||||
|
||||
void ps_move_data::reset_sensors()
|
||||
{
|
||||
quaternion = default_quaternion;
|
||||
accelerometer = {};
|
||||
gyro = {};
|
||||
prev_gyro = {};
|
||||
angular_acceleration = {};
|
||||
magnetometer = {};
|
||||
//prev_pos_world = {}; // probably no reset needed ?
|
||||
vel_world = {};
|
||||
prev_vel_world = {};
|
||||
accel_world = {};
|
||||
angvel_world = {};
|
||||
angaccel_world = {};
|
||||
}
|
||||
|
||||
void ps_move_data::update_orientation(f32 delta_time)
|
||||
{
|
||||
if (!delta_time)
|
||||
return;
|
||||
|
||||
// Rotate vector v by quaternion q
|
||||
const auto rotate_vector = [](const vect<4>& q, const vect<3>& v)
|
||||
{
|
||||
const vect<4> qv({0.0f, v.x(), v.y(), v.z()});
|
||||
const vect<4> q_inv({q.w(), -q.x(), -q.y(), -q.z()});
|
||||
|
||||
// t = q * v
|
||||
vect<4> t;
|
||||
t.w() = -q.x() * qv.x() - q.y() * qv.y() - q.z() * qv.z();
|
||||
t.x() = q.w() * qv.x() + q.y() * qv.z() - q.z() * qv.y();
|
||||
t.y() = q.w() * qv.y() - q.x() * qv.z() + q.z() * qv.x();
|
||||
t.z() = q.w() * qv.z() + q.x() * qv.y() - q.y() * qv.x();
|
||||
|
||||
// r = t * q_inv
|
||||
vect<4> r;
|
||||
r.w() = -t.x() * q_inv.x() - t.y() * q_inv.y() - t.z() * q_inv.z();
|
||||
r.x() = t.w() * q_inv.x() + t.y() * q_inv.z() - t.z() * q_inv.y();
|
||||
r.y() = t.w() * q_inv.y() - t.x() * q_inv.z() + t.z() * q_inv.x();
|
||||
r.z() = t.w() * q_inv.z() + t.x() * q_inv.y() - t.y() * q_inv.x();
|
||||
|
||||
return vect<3>({r.x(), r.y(), r.z()});
|
||||
};
|
||||
|
||||
if constexpr (use_imu_for_velocity)
|
||||
{
|
||||
// Gravity in world frame
|
||||
constexpr f32 gravity = 9.81f;
|
||||
constexpr vect<3> g({0.0f, 0.0f, -gravity});
|
||||
|
||||
// Rotate gravity into sensor frame
|
||||
const vect<3> g_sensor = rotate_vector(quaternion, g);
|
||||
|
||||
// Remove gravity
|
||||
vect<3> linear_local;
|
||||
for (u32 i = 0; i < 3; i++)
|
||||
{
|
||||
linear_local[i] = (accelerometer[i] * gravity) - g_sensor[i];
|
||||
}
|
||||
|
||||
// Linear acceleration (rotate to world coordinates)
|
||||
accel_world = rotate_vector(quaternion, linear_local);
|
||||
|
||||
// convert to mm/s²
|
||||
for (u32 i = 0; i < 3; i++)
|
||||
{
|
||||
accel_world[i] *= 1000.0f;
|
||||
}
|
||||
|
||||
// Linear velocity (integrate acceleration)
|
||||
for (u32 i = 0; i < 3; i++)
|
||||
{
|
||||
vel_world[i] = prev_vel_world[i] + accel_world[i] * delta_time;
|
||||
}
|
||||
|
||||
prev_vel_world = vel_world;
|
||||
}
|
||||
|
||||
// Compute raw angular acceleration
|
||||
for (u32 i = 0; i < 3; i++)
|
||||
{
|
||||
const f32 alpha = (gyro[i] - prev_gyro[i]) / delta_time;
|
||||
|
||||
// Filtering
|
||||
constexpr f32 weight = 0.8f;
|
||||
constexpr f32 weight_inv = 1.0f - weight;
|
||||
angular_acceleration[i] = weight * angular_acceleration[i] + weight_inv * alpha;
|
||||
}
|
||||
|
||||
// Angular velocity (rotate to world coordinates)
|
||||
angvel_world = rotate_vector(quaternion, gyro);
|
||||
|
||||
// Angular acceleration (rotate to world coordinates)
|
||||
angaccel_world = rotate_vector(quaternion, angular_acceleration);
|
||||
|
||||
prev_gyro = gyro;
|
||||
}
|
||||
|
||||
void ps_move_data::update_velocity(u64 timestamp, be_t<f32> pos_world[4])
|
||||
{
|
||||
if constexpr (use_imu_for_velocity)
|
||||
return;
|
||||
|
||||
if (last_velocity_update_time_us == timestamp)
|
||||
return;
|
||||
|
||||
// Get elapsed time since last update
|
||||
const f32 delta_time = (last_velocity_update_time_us == 0) ? 0.0f : ((timestamp - last_velocity_update_time_us) / 1'000'000.0f);
|
||||
last_velocity_update_time_us = timestamp;
|
||||
|
||||
if (!delta_time)
|
||||
return;
|
||||
|
||||
for (u32 i = 0; i < 3; i++)
|
||||
{
|
||||
// Linear velocity
|
||||
constexpr f32 weight = 0.8f;
|
||||
constexpr f32 weight_inv = 1.0f - weight;
|
||||
vel_world[i] = weight * ((pos_world[i] - prev_pos_world[i]) / delta_time) + weight_inv * prev_vel_world[i];
|
||||
prev_pos_world[i] = pos_world[i];
|
||||
|
||||
// Linear acceleration
|
||||
accel_world[i] = (vel_world[i] - prev_vel_world[i]) / delta_time;
|
||||
}
|
||||
|
||||
prev_vel_world = vel_world;
|
||||
}
|
||||
75
rpcs3/Emu/Io/ps_move_data.h
Normal file
75
rpcs3/Emu/Io/ps_move_data.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
struct ps_move_data
|
||||
{
|
||||
template <int Size, typename T = f32>
|
||||
struct vect
|
||||
{
|
||||
public:
|
||||
constexpr vect() = default;
|
||||
constexpr vect(const std::array<T, Size>& vec) : data(vec) {};
|
||||
|
||||
template <typename I>
|
||||
T& operator[](I i) { return data[i]; }
|
||||
|
||||
template <typename I>
|
||||
const T& operator[](I i) const { return data[i]; }
|
||||
|
||||
T x() const requires (Size >= 1) { return data[0]; }
|
||||
T y() const requires (Size >= 2) { return data[1]; }
|
||||
T z() const requires (Size >= 3) { return data[2]; }
|
||||
T w() const requires (Size >= 4) { return data[3]; }
|
||||
|
||||
T& x() requires (Size >= 1) { return data[0]; }
|
||||
T& y() requires (Size >= 2) { return data[1]; }
|
||||
T& z() requires (Size >= 3) { return data[2]; }
|
||||
T& w() requires (Size >= 4) { return data[3]; }
|
||||
|
||||
private:
|
||||
std::array<T, Size> data {};
|
||||
};
|
||||
|
||||
ps_move_data();
|
||||
|
||||
u32 external_device_id = 0;
|
||||
std::array<u8, 38> external_device_read{}; // CELL_GEM_EXTERNAL_PORT_DEVICE_INFO_SIZE
|
||||
std::array<u8, 40> external_device_write{}; // CELL_GEM_EXTERNAL_PORT_OUTPUT_SIZE
|
||||
std::array<u8, 5> external_device_data{};
|
||||
bool external_device_connected = false;
|
||||
bool external_device_read_requested = false;
|
||||
bool external_device_write_requested = false;
|
||||
|
||||
bool calibration_requested = false;
|
||||
bool calibration_succeeded = false;
|
||||
|
||||
bool magnetometer_enabled = false;
|
||||
bool orientation_enabled = false;
|
||||
|
||||
// Disable IMU tracking of velocity and acceleration (massive drift)
|
||||
static constexpr bool use_imu_for_velocity = false;
|
||||
u64 last_velocity_update_time_us = 0;
|
||||
|
||||
static const vect<4> default_quaternion;
|
||||
vect<4> quaternion {}; // quaternion orientation (x,y,z,w) of controller relative to default (facing the camera with buttons up)
|
||||
|
||||
// Raw values (local)
|
||||
vect<3> accelerometer {}; // linear acceleration in G units (9.81 = 1 unit)
|
||||
vect<3> gyro {}; // angular velocity in rad/s
|
||||
vect<3> prev_gyro {}; // previous angular velocity in rad/s
|
||||
vect<3> angular_acceleration {}; // angular acceleration in rad/s²
|
||||
vect<3> magnetometer {};
|
||||
|
||||
// In world coordinates
|
||||
vect<3> prev_pos_world {};
|
||||
vect<3> vel_world {}; // velocity of sphere in world coordinates (mm/s)
|
||||
vect<3> prev_vel_world {}; // previous velocity of sphere in world coordinates (mm/s)
|
||||
vect<3> accel_world {}; // acceleration of sphere in world coordinates (mm/s²)
|
||||
vect<3> angvel_world {}; // angular velocity of controller in world coordinates (radians/s)
|
||||
vect<3> angaccel_world {}; // angular acceleration of controller in world coordinates (radians/s²)
|
||||
|
||||
s16 temperature = 0;
|
||||
|
||||
void reset_sensors();
|
||||
void update_orientation(f32 delta_time);
|
||||
void update_velocity(u64 timestamp, be_t<f32> pos_world[4]);
|
||||
};
|
||||
|
|
@ -74,7 +74,7 @@ namespace vm
|
|||
std::array<atomic_t<cpu_thread*>, g_cfg.core.ppu_threads.max> g_locks{};
|
||||
|
||||
// Range lock slot allocation bits
|
||||
atomic_t<u64, 64> g_range_lock_bits[2]{};
|
||||
atomic_t<u64, 128> g_range_lock_bits[2]{};
|
||||
|
||||
auto& get_range_lock_bits(bool is_exclusive_range)
|
||||
{
|
||||
|
|
@ -82,7 +82,7 @@ namespace vm
|
|||
}
|
||||
|
||||
// Memory range lock slots (sparse atomics)
|
||||
atomic_t<u64, 64> g_range_lock_set[64]{};
|
||||
atomic_t<u64, 128> g_range_lock_set[64]{};
|
||||
|
||||
// Memory pages
|
||||
std::array<memory_page, 0x100000000 / 4096> g_pages;
|
||||
|
|
@ -142,7 +142,7 @@ namespace vm
|
|||
}
|
||||
}
|
||||
|
||||
atomic_t<u64, 64>* alloc_range_lock()
|
||||
atomic_t<u64, 128>* alloc_range_lock()
|
||||
{
|
||||
const auto [bits, ok] = get_range_lock_bits(false).fetch_op([](u64& bits)
|
||||
{
|
||||
|
|
@ -167,7 +167,7 @@ namespace vm
|
|||
template <typename F>
|
||||
static u64 for_all_range_locks(u64 input, F func);
|
||||
|
||||
void range_lock_internal(atomic_t<u64, 64>* range_lock, u32 begin, u32 size)
|
||||
void range_lock_internal(atomic_t<u64, 128>* range_lock, u32 begin, u32 size)
|
||||
{
|
||||
perf_meter<"RHW_LOCK"_u64> perf0(0);
|
||||
|
||||
|
|
@ -275,7 +275,7 @@ namespace vm
|
|||
}
|
||||
}
|
||||
|
||||
void free_range_lock(atomic_t<u64, 64>* range_lock) noexcept
|
||||
void free_range_lock(atomic_t<u64, 128>* range_lock) noexcept
|
||||
{
|
||||
if (range_lock < g_range_lock_set || range_lock >= std::end(g_range_lock_set))
|
||||
{
|
||||
|
|
@ -316,7 +316,7 @@ namespace vm
|
|||
return result;
|
||||
}
|
||||
|
||||
static atomic_t<u64, 64>* _lock_main_range_lock(u64 flags, u32 addr, u32 size)
|
||||
static atomic_t<u64, 128>* _lock_main_range_lock(u64 flags, u32 addr, u32 size)
|
||||
{
|
||||
// Shouldn't really happen
|
||||
if (size == 0)
|
||||
|
|
@ -460,7 +460,7 @@ namespace vm
|
|||
{
|
||||
}
|
||||
|
||||
writer_lock::writer_lock(u32 const addr, atomic_t<u64, 64>* range_lock, u32 const size, u64 const flags) noexcept
|
||||
writer_lock::writer_lock(u32 const addr, atomic_t<u64, 128>* range_lock, u32 const size, u64 const flags) noexcept
|
||||
: range_lock(range_lock)
|
||||
{
|
||||
cpu_thread* cpu{};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace vm
|
|||
range_bits = 3,
|
||||
};
|
||||
|
||||
extern atomic_t<u64, 64> g_range_lock_bits[2];
|
||||
extern atomic_t<u64, 128> g_range_lock_bits[2];
|
||||
|
||||
extern atomic_t<u64> g_shmem[];
|
||||
|
||||
|
|
@ -36,13 +36,13 @@ namespace vm
|
|||
void passive_lock(cpu_thread& cpu);
|
||||
|
||||
// Register range lock for further use
|
||||
atomic_t<u64, 64>* alloc_range_lock();
|
||||
atomic_t<u64, 128>* alloc_range_lock();
|
||||
|
||||
void range_lock_internal(atomic_t<u64, 64>* range_lock, u32 begin, u32 size);
|
||||
void range_lock_internal(atomic_t<u64, 128>* range_lock, u32 begin, u32 size);
|
||||
|
||||
// Lock memory range ignoring memory protection (Size!=0 also implies aligned begin)
|
||||
template <uint Size = 0>
|
||||
FORCE_INLINE void range_lock(atomic_t<u64, 64>* range_lock, u32 begin, u32 _size)
|
||||
FORCE_INLINE void range_lock(atomic_t<u64, 128>* range_lock, u32 begin, u32 _size)
|
||||
{
|
||||
if constexpr (Size == 0)
|
||||
{
|
||||
|
|
@ -80,7 +80,7 @@ namespace vm
|
|||
}
|
||||
|
||||
// Release it
|
||||
void free_range_lock(atomic_t<u64, 64>*) noexcept;
|
||||
void free_range_lock(atomic_t<u64, 128>*) noexcept;
|
||||
|
||||
// Unregister reader
|
||||
void passive_unlock(cpu_thread& cpu);
|
||||
|
|
@ -91,12 +91,12 @@ namespace vm
|
|||
|
||||
struct writer_lock final
|
||||
{
|
||||
atomic_t<u64, 64>* range_lock;
|
||||
atomic_t<u64, 128>* range_lock;
|
||||
|
||||
writer_lock(const writer_lock&) = delete;
|
||||
writer_lock& operator=(const writer_lock&) = delete;
|
||||
writer_lock() noexcept;
|
||||
writer_lock(u32 addr, atomic_t<u64, 64>* range_lock = nullptr, u32 size = 128, u64 flags = range_locked) noexcept;
|
||||
writer_lock(u32 addr, atomic_t<u64, 128>* range_lock = nullptr, u32 size = 128, u64 flags = range_locked) noexcept;
|
||||
~writer_lock() noexcept;
|
||||
};
|
||||
} // namespace vm
|
||||
|
|
|
|||
|
|
@ -34,32 +34,33 @@ namespace vm
|
|||
void reservation_update(u32 addr);
|
||||
std::pair<bool, u64> try_reservation_update(u32 addr);
|
||||
|
||||
struct reservation_waiter_t
|
||||
struct alignas(8) reservation_waiter_t
|
||||
{
|
||||
u32 wait_flag = 0;
|
||||
u32 waiters_count = 0;
|
||||
};
|
||||
|
||||
static inline atomic_t<reservation_waiter_t>* reservation_notifier(u32 raddr, u64 rtime)
|
||||
static inline atomic_t<reservation_waiter_t, 128>* reservation_notifier(u32 raddr, u64 rtime)
|
||||
{
|
||||
constexpr u32 wait_vars_for_each = 64;
|
||||
constexpr u32 wait_vars_for_each = 32;
|
||||
constexpr u32 unique_address_bit_mask = 0b1111;
|
||||
constexpr u32 unique_rtime_bit_mask = 0b1;
|
||||
|
||||
extern std::array<atomic_t<reservation_waiter_t>, wait_vars_for_each * (unique_address_bit_mask + 1) * (unique_rtime_bit_mask + 1)> g_resrv_waiters_count;
|
||||
extern std::array<atomic_t<reservation_waiter_t, 128>, wait_vars_for_each * (unique_address_bit_mask + 1) * (unique_rtime_bit_mask + 1)> g_resrv_waiters_count;
|
||||
|
||||
// Storage efficient method to distinguish different nearby addresses (which are likely)
|
||||
const usz index = std::popcount(raddr & -2048) * (1 << 5) + ((rtime / 128) & unique_rtime_bit_mask) * (1 << 4) + ((raddr / 128) & unique_address_bit_mask);
|
||||
const usz index = std::min<usz>(std::popcount(raddr & -2048), 31) * (1 << 5) + ((rtime / 128) & unique_rtime_bit_mask) * (1 << 4) + ((raddr / 128) & unique_address_bit_mask);
|
||||
return &g_resrv_waiters_count[index];
|
||||
}
|
||||
|
||||
// Returns waiter count
|
||||
static inline u32 reservation_notifier_count(u32 raddr, u64 rtime)
|
||||
{
|
||||
return reservation_notifier(raddr, rtime)->load().waiters_count;
|
||||
reservation_waiter_t v = reservation_notifier(raddr, rtime)->load();
|
||||
return v.wait_flag % 2 == 1 ? v.waiters_count : 0;
|
||||
}
|
||||
|
||||
static inline void reservation_notifier_end_wait(atomic_t<reservation_waiter_t>& waiter)
|
||||
static inline void reservation_notifier_end_wait(atomic_t<reservation_waiter_t, 128>& waiter)
|
||||
{
|
||||
waiter.atomic_op([](reservation_waiter_t& value)
|
||||
{
|
||||
|
|
@ -73,9 +74,9 @@ namespace vm
|
|||
});
|
||||
}
|
||||
|
||||
static inline std::pair<atomic_t<reservation_waiter_t>*, u32> reservation_notifier_begin_wait(u32 raddr, u64 rtime)
|
||||
static inline std::pair<atomic_t<reservation_waiter_t, 128>*, u32> reservation_notifier_begin_wait(u32 raddr, u64 rtime)
|
||||
{
|
||||
atomic_t<reservation_waiter_t>& waiter = *reservation_notifier(raddr, rtime);
|
||||
atomic_t<reservation_waiter_t, 128>& waiter = *reservation_notifier(raddr, rtime);
|
||||
|
||||
u32 wait_flag = 0;
|
||||
|
||||
|
|
|
|||
1151
rpcs3/Emu/NP/clans_client.cpp
Normal file
1151
rpcs3/Emu/NP/clans_client.cpp
Normal file
File diff suppressed because it is too large
Load diff
129
rpcs3/Emu/NP/clans_client.h
Normal file
129
rpcs3/Emu/NP/clans_client.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#pragma once
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include <Emu/Cell/Modules/sceNpClans.h>
|
||||
#include <Emu/NP/np_handler.h>
|
||||
#include <Emu/IdManager.h>
|
||||
|
||||
inline const char* CLANS_ENTITLEMENT_ID = "NPWR00432_00";
|
||||
inline const char* CLANS_SERVICE_ID = "IV0001-NPXS01001_00";
|
||||
|
||||
namespace clan
|
||||
{
|
||||
enum class ClanManagerOperationType
|
||||
{
|
||||
VIEW,
|
||||
UPDATE
|
||||
};
|
||||
|
||||
enum class ClanRequestType
|
||||
{
|
||||
FUNC,
|
||||
SEC
|
||||
};
|
||||
|
||||
enum class ClanSearchFilterOperator : u8
|
||||
{
|
||||
Equal,
|
||||
NotEqual,
|
||||
GreaterThan,
|
||||
GreaterThanOrEqual,
|
||||
LessThan,
|
||||
LessThanOrEqual,
|
||||
Like,
|
||||
};
|
||||
|
||||
enum class ClanRequestAction
|
||||
{
|
||||
GetClanList,
|
||||
GetClanInfo,
|
||||
GetMemberInfo,
|
||||
GetMemberList,
|
||||
GetBlacklist,
|
||||
RecordBlacklistEntry,
|
||||
DeleteBlacklistEntry,
|
||||
ClanSearch,
|
||||
CreateClan,
|
||||
DisbandClan,
|
||||
RequestMembership,
|
||||
CancelRequestMembership,
|
||||
AcceptMembershipRequest,
|
||||
DeclineMembershipRequest,
|
||||
SendInvitation,
|
||||
CancelInvitation,
|
||||
AcceptInvitation,
|
||||
DeclineInvitation,
|
||||
UpdateMemberInfo,
|
||||
UpdateClanInfo,
|
||||
JoinClan,
|
||||
LeaveClan,
|
||||
KickMember,
|
||||
ChangeMemberRole,
|
||||
RetrieveAnnouncements,
|
||||
PostAnnouncement,
|
||||
DeleteAnnouncement
|
||||
};
|
||||
|
||||
class clans_client
|
||||
{
|
||||
private:
|
||||
|
||||
|
||||
static size_t curl_write_callback(void* data, size_t size, size_t nmemb, void* clientp);
|
||||
SceNpClansError send_request(u32 reqId, ClanRequestAction action, ClanManagerOperationType type, pugi::xml_document* xml_body, pugi::xml_document* out_response);
|
||||
|
||||
/// @brief Forge and get a V2.1 Ticket for clan operations
|
||||
std::string get_clan_ticket(np::np_handler& nph);
|
||||
|
||||
public:
|
||||
clans_client();
|
||||
~clans_client();
|
||||
|
||||
SceNpClansError create_request(s32* req_id);
|
||||
SceNpClansError destroy_request(u32 req_id);
|
||||
|
||||
SceNpClansError clan_search(u32 req_id, SceNpClansPagingRequest* paging, SceNpClansSearchableName* search, SceNpClansClanBasicInfo* clan_list, SceNpClansPagingResult* page_result);
|
||||
|
||||
SceNpClansError create_clan(np::np_handler& nph, u32 req_id, std::string_view name, std::string_view tag, vm::ptr<SceNpClanId> clan_id);
|
||||
SceNpClansError disband_dlan(np::np_handler& nph, u32 req_id, SceNpClanId clan_id);
|
||||
|
||||
SceNpClansError get_clan_list(np::np_handler& nph, u32 req_id, SceNpClansPagingRequest* paging, SceNpClansEntry* clan_list, SceNpClansPagingResult* page_result);
|
||||
SceNpClansError get_clan_info(u32 req_id, SceNpClanId clan_id, SceNpClansClanInfo* clan_info);
|
||||
|
||||
SceNpClansError get_member_info(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpId np_id, SceNpClansMemberEntry* mem_info);
|
||||
SceNpClansError get_member_list(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpClansPagingRequest* paging, SceNpClansMemberStatus status, SceNpClansMemberEntry* mem_list, SceNpClansPagingResult* page_result);
|
||||
|
||||
SceNpClansError get_blacklist(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpClansPagingRequest* paging, SceNpClansBlacklistEntry* bl, SceNpClansPagingResult* page_result);
|
||||
SceNpClansError add_blacklist_entry(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpId np_id);
|
||||
SceNpClansError remove_blacklist_entry(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpId np_id);
|
||||
|
||||
SceNpClansError request_membership(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpClansMessage* message);
|
||||
SceNpClansError cancel_request_membership(np::np_handler& nph, u32 req_id, SceNpClanId clan_id);
|
||||
SceNpClansError send_membership_response(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpId np_id, SceNpClansMessage* message, b8 allow);
|
||||
|
||||
SceNpClansError send_invitation(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpId np_id, SceNpClansMessage* message);
|
||||
SceNpClansError cancel_invitation(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpId np_id);
|
||||
SceNpClansError send_invitation_response(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpClansMessage* message, b8 accept);
|
||||
|
||||
SceNpClansError join_clan(np::np_handler& nph, u32 req_id, SceNpClanId clan_id);
|
||||
SceNpClansError leave_clan(np::np_handler& nph, u32 req_id, SceNpClanId clan_id);
|
||||
|
||||
SceNpClansError update_member_info(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpClansUpdatableMemberInfo* info);
|
||||
SceNpClansError update_clan_info(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpClansUpdatableClanInfo* info);
|
||||
|
||||
SceNpClansError kick_member(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpId np_id, SceNpClansMessage* message);
|
||||
SceNpClansError change_member_role(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpId np_id, SceNpClansMemberRole role);
|
||||
|
||||
SceNpClansError retrieve_announcements(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpClansPagingRequest* paging, SceNpClansMessageEntry* announcements, SceNpClansPagingResult* page_result);
|
||||
SceNpClansError post_announcement(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpClansMessage* announcement, SceNpClansMessageData* data, u32 duration, SceNpClansMessageId* announcement_id);
|
||||
SceNpClansError delete_announcement(np::np_handler& nph, u32 req_id, SceNpClanId clan_id, SceNpClansMessageId announcement_id);
|
||||
};
|
||||
} // namespace clan
|
||||
|
||||
struct sce_np_clans_manager
|
||||
{
|
||||
atomic_t<bool> is_initialized = false;
|
||||
std::shared_ptr<clan::clans_client> client;
|
||||
};
|
||||
154
rpcs3/Emu/NP/clans_config.cpp
Normal file
154
rpcs3/Emu/NP/clans_config.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
#include "Utilities/StrUtil.h"
|
||||
#include "stdafx.h"
|
||||
#include "clans_config.h"
|
||||
#include "Utilities/File.h"
|
||||
|
||||
cfg_clans g_cfg_clans;
|
||||
|
||||
LOG_CHANNEL(clans_config_log, "clans_config");
|
||||
|
||||
void cfg_clans::load()
|
||||
{
|
||||
const std::string path = cfg_clans::get_path();
|
||||
|
||||
fs::file cfg_file(path, fs::read);
|
||||
if (cfg_file)
|
||||
{
|
||||
clans_config_log.notice("Loading Clans config. Path: %s", path);
|
||||
from_string(cfg_file.to_string());
|
||||
}
|
||||
else
|
||||
{
|
||||
clans_config_log.notice("Clans config missing. Using default settings. Path: %s", path);
|
||||
from_default();
|
||||
}
|
||||
}
|
||||
|
||||
void cfg_clans::save() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const std::string path_to_cfg = fs::get_config_dir(true);
|
||||
if (!fs::create_path(path_to_cfg))
|
||||
{
|
||||
clans_config_log.error("Could not create path: %s", path_to_cfg);
|
||||
}
|
||||
#endif
|
||||
|
||||
const std::string path = cfg_clans::get_path();
|
||||
|
||||
if (!cfg::node::save(path))
|
||||
{
|
||||
clans_config_log.error("Could not save config: %s (error=%s)", path, fs::g_tls_error);
|
||||
}
|
||||
}
|
||||
|
||||
std::string cfg_clans::get_path()
|
||||
{
|
||||
return fs::get_config_dir(true) + "clans.yml";
|
||||
}
|
||||
|
||||
std::string cfg_clans::get_host() const
|
||||
{
|
||||
return host.to_string();
|
||||
}
|
||||
|
||||
bool cfg_clans::get_use_https() const
|
||||
{
|
||||
return use_https.get();
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> cfg_clans::get_hosts()
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> vec_hosts;
|
||||
const std::string host_str = hosts.to_string();
|
||||
const auto hosts_list = fmt::split_sv(host_str, {"|||"});
|
||||
|
||||
for (const auto& cur_host : hosts_list)
|
||||
{
|
||||
const auto desc_and_host = fmt::split(cur_host, {"|"});
|
||||
if (desc_and_host.size() != 2)
|
||||
{
|
||||
clans_config_log.error("Invalid host in the list of hosts: %s", cur_host);
|
||||
continue;
|
||||
}
|
||||
vec_hosts.push_back(std::make_pair(std::move(desc_and_host[0]), std::move(desc_and_host[1])));
|
||||
}
|
||||
|
||||
if (vec_hosts.empty())
|
||||
{
|
||||
hosts.from_default();
|
||||
save();
|
||||
return get_hosts();
|
||||
}
|
||||
|
||||
return vec_hosts;
|
||||
}
|
||||
|
||||
void cfg_clans::set_host(std::string_view host)
|
||||
{
|
||||
this->host.from_string(host);
|
||||
}
|
||||
|
||||
void cfg_clans::set_use_https(bool use_https)
|
||||
{
|
||||
this->use_https.set(use_https);
|
||||
}
|
||||
|
||||
void cfg_clans::set_hosts(const std::vector<std::pair<std::string, std::string>>& vec_hosts)
|
||||
{
|
||||
std::string final_string;
|
||||
for (const auto& [cur_desc, cur_host] : vec_hosts)
|
||||
{
|
||||
fmt::append(final_string, "%s|%s|||", cur_desc, cur_host);
|
||||
}
|
||||
|
||||
if (final_string.empty())
|
||||
{
|
||||
hosts.from_default();
|
||||
return;
|
||||
}
|
||||
|
||||
final_string.resize(final_string.size() - 3);
|
||||
hosts.from_string(final_string);
|
||||
}
|
||||
|
||||
bool cfg_clans::add_host(std::string_view new_description, std::string_view new_host)
|
||||
{
|
||||
auto cur_hosts = get_hosts();
|
||||
|
||||
for (const auto& [cur_desc, cur_host] : cur_hosts)
|
||||
{
|
||||
if (cur_desc == new_description && cur_host == new_host)
|
||||
return false;
|
||||
}
|
||||
|
||||
cur_hosts.push_back(std::make_pair(std::string(new_description), std::string(new_host)));
|
||||
set_hosts(cur_hosts);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cfg_clans::del_host(std::string_view del_description, std::string_view del_host)
|
||||
{
|
||||
// Do not delete default servers
|
||||
const auto def_desc_and_host = fmt::split_sv(hosts.def, {"|"});
|
||||
ensure(def_desc_and_host.size() == 2);
|
||||
if (del_description == def_desc_and_host[0] && del_host == def_desc_and_host[1])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto cur_hosts = get_hosts();
|
||||
|
||||
for (auto it = cur_hosts.begin(); it != cur_hosts.end(); it++)
|
||||
{
|
||||
if (it->first == del_description && it->second == del_host)
|
||||
{
|
||||
cur_hosts.erase(it);
|
||||
set_hosts(cur_hosts);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
29
rpcs3/Emu/NP/clans_config.h
Normal file
29
rpcs3/Emu/NP/clans_config.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "Utilities/Config.h"
|
||||
|
||||
struct cfg_clans : cfg::node
|
||||
{
|
||||
cfg::uint32 version{this, "Version", 1};
|
||||
cfg::string host{this, "Host", "clans.rpcs3.net"};
|
||||
cfg::string hosts{this, "Hosts", "Official Clans Server|clans.rpcs3.net"};
|
||||
cfg::_bool use_https{this, "Use HTTPS", true};
|
||||
|
||||
void load();
|
||||
void save() const;
|
||||
|
||||
std::string get_host() const;
|
||||
bool get_use_https() const;
|
||||
std::vector<std::pair<std::string, std::string>> get_hosts();
|
||||
|
||||
void set_host(std::string_view host);
|
||||
void set_use_https(bool use_https);
|
||||
bool add_host(std::string_view description, std::string_view host);
|
||||
bool del_host(std::string_view description, std::string_view host);
|
||||
|
||||
private:
|
||||
static std::string get_path();
|
||||
void set_hosts(const std::vector<std::pair<std::string, std::string>>& vec_hosts);
|
||||
};
|
||||
|
||||
extern cfg_clans g_cfg_clans;
|
||||
|
|
@ -206,7 +206,7 @@ table SetRoomDataInternalRequest {
|
|||
flagAttr:uint32;
|
||||
roomBinAttrInternal:[BinAttr];
|
||||
passwordConfig:[RoomGroupPasswordConfig];
|
||||
passwordSlotMask:uint64;
|
||||
passwordSlotMask:[uint64];
|
||||
ownerPrivilegeRank:[uint16];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
static_assert(FLATBUFFERS_VERSION_MAJOR == 24 &&
|
||||
FLATBUFFERS_VERSION_MINOR == 3 &&
|
||||
FLATBUFFERS_VERSION_REVISION == 25,
|
||||
static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
FLATBUFFERS_VERSION_MINOR == 9 &&
|
||||
FLATBUFFERS_VERSION_REVISION == 23,
|
||||
"Non-compatible flatbuffers version included");
|
||||
|
||||
struct SignalingAddr;
|
||||
|
|
@ -2762,8 +2762,8 @@ struct SetRoomDataInternalRequest FLATBUFFERS_FINAL_CLASS : private ::flatbuffer
|
|||
const ::flatbuffers::Vector<::flatbuffers::Offset<RoomGroupPasswordConfig>> *passwordConfig() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<RoomGroupPasswordConfig>> *>(VT_PASSWORDCONFIG);
|
||||
}
|
||||
uint64_t passwordSlotMask() const {
|
||||
return GetField<uint64_t>(VT_PASSWORDSLOTMASK, 0);
|
||||
const ::flatbuffers::Vector<uint64_t> *passwordSlotMask() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<uint64_t> *>(VT_PASSWORDSLOTMASK);
|
||||
}
|
||||
const ::flatbuffers::Vector<uint16_t> *ownerPrivilegeRank() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<uint16_t> *>(VT_OWNERPRIVILEGERANK);
|
||||
|
|
@ -2779,7 +2779,8 @@ struct SetRoomDataInternalRequest FLATBUFFERS_FINAL_CLASS : private ::flatbuffer
|
|||
VerifyOffset(verifier, VT_PASSWORDCONFIG) &&
|
||||
verifier.VerifyVector(passwordConfig()) &&
|
||||
verifier.VerifyVectorOfTables(passwordConfig()) &&
|
||||
VerifyField<uint64_t>(verifier, VT_PASSWORDSLOTMASK, 8) &&
|
||||
VerifyOffset(verifier, VT_PASSWORDSLOTMASK) &&
|
||||
verifier.VerifyVector(passwordSlotMask()) &&
|
||||
VerifyOffset(verifier, VT_OWNERPRIVILEGERANK) &&
|
||||
verifier.VerifyVector(ownerPrivilegeRank()) &&
|
||||
verifier.EndTable();
|
||||
|
|
@ -2805,8 +2806,8 @@ struct SetRoomDataInternalRequestBuilder {
|
|||
void add_passwordConfig(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<RoomGroupPasswordConfig>>> passwordConfig) {
|
||||
fbb_.AddOffset(SetRoomDataInternalRequest::VT_PASSWORDCONFIG, passwordConfig);
|
||||
}
|
||||
void add_passwordSlotMask(uint64_t passwordSlotMask) {
|
||||
fbb_.AddElement<uint64_t>(SetRoomDataInternalRequest::VT_PASSWORDSLOTMASK, passwordSlotMask, 0);
|
||||
void add_passwordSlotMask(::flatbuffers::Offset<::flatbuffers::Vector<uint64_t>> passwordSlotMask) {
|
||||
fbb_.AddOffset(SetRoomDataInternalRequest::VT_PASSWORDSLOTMASK, passwordSlotMask);
|
||||
}
|
||||
void add_ownerPrivilegeRank(::flatbuffers::Offset<::flatbuffers::Vector<uint16_t>> ownerPrivilegeRank) {
|
||||
fbb_.AddOffset(SetRoomDataInternalRequest::VT_OWNERPRIVILEGERANK, ownerPrivilegeRank);
|
||||
|
|
@ -2829,12 +2830,12 @@ inline ::flatbuffers::Offset<SetRoomDataInternalRequest> CreateSetRoomDataIntern
|
|||
uint32_t flagAttr = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<BinAttr>>> roomBinAttrInternal = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<RoomGroupPasswordConfig>>> passwordConfig = 0,
|
||||
uint64_t passwordSlotMask = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<uint64_t>> passwordSlotMask = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<uint16_t>> ownerPrivilegeRank = 0) {
|
||||
SetRoomDataInternalRequestBuilder builder_(_fbb);
|
||||
builder_.add_passwordSlotMask(passwordSlotMask);
|
||||
builder_.add_roomId(roomId);
|
||||
builder_.add_ownerPrivilegeRank(ownerPrivilegeRank);
|
||||
builder_.add_passwordSlotMask(passwordSlotMask);
|
||||
builder_.add_passwordConfig(passwordConfig);
|
||||
builder_.add_roomBinAttrInternal(roomBinAttrInternal);
|
||||
builder_.add_flagAttr(flagAttr);
|
||||
|
|
@ -2849,10 +2850,11 @@ inline ::flatbuffers::Offset<SetRoomDataInternalRequest> CreateSetRoomDataIntern
|
|||
uint32_t flagAttr = 0,
|
||||
const std::vector<::flatbuffers::Offset<BinAttr>> *roomBinAttrInternal = nullptr,
|
||||
const std::vector<::flatbuffers::Offset<RoomGroupPasswordConfig>> *passwordConfig = nullptr,
|
||||
uint64_t passwordSlotMask = 0,
|
||||
const std::vector<uint64_t> *passwordSlotMask = nullptr,
|
||||
const std::vector<uint16_t> *ownerPrivilegeRank = nullptr) {
|
||||
auto roomBinAttrInternal__ = roomBinAttrInternal ? _fbb.CreateVector<::flatbuffers::Offset<BinAttr>>(*roomBinAttrInternal) : 0;
|
||||
auto passwordConfig__ = passwordConfig ? _fbb.CreateVector<::flatbuffers::Offset<RoomGroupPasswordConfig>>(*passwordConfig) : 0;
|
||||
auto passwordSlotMask__ = passwordSlotMask ? _fbb.CreateVector<uint64_t>(*passwordSlotMask) : 0;
|
||||
auto ownerPrivilegeRank__ = ownerPrivilegeRank ? _fbb.CreateVector<uint16_t>(*ownerPrivilegeRank) : 0;
|
||||
return CreateSetRoomDataInternalRequest(
|
||||
_fbb,
|
||||
|
|
@ -2861,7 +2863,7 @@ inline ::flatbuffers::Offset<SetRoomDataInternalRequest> CreateSetRoomDataIntern
|
|||
flagAttr,
|
||||
roomBinAttrInternal__,
|
||||
passwordConfig__,
|
||||
passwordSlotMask,
|
||||
passwordSlotMask__,
|
||||
ownerPrivilegeRank__);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "Emu/NP/np_allocator.h"
|
||||
#include "Emu/NP/np_cache.h"
|
||||
#include "Emu/NP/np_helpers.h"
|
||||
#include "Emu/NP/np_structs_extra.h"
|
||||
|
||||
LOG_CHANNEL(np_cache);
|
||||
|
||||
|
|
@ -155,6 +156,8 @@ namespace np
|
|||
slots.openPublicSlotNum = open_public_slots;
|
||||
slots.openPrivateSlotNum = open_private_slots;
|
||||
|
||||
extra_nps::print_SceNpMatching2RoomSlotInfo(&slots);
|
||||
|
||||
return {CELL_OK, slots};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ namespace np
|
|||
dnshook::dnshook()
|
||||
{
|
||||
// Init switch map for dns
|
||||
auto swaps = fmt::split(g_cfg.net.swap_list.to_string(), {"&&"});
|
||||
const std::string swap_list = g_cfg.net.swap_list.to_string();
|
||||
const auto swaps = fmt::split_sv(swap_list, {"&&"});
|
||||
for (usz i = 0; i < swaps.size(); i++)
|
||||
{
|
||||
auto host_and_ip = fmt::split(swaps[i], {"="});
|
||||
|
|
|
|||
|
|
@ -239,6 +239,25 @@ namespace np
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string ticket::get_service_id() const
|
||||
{
|
||||
if (!parse_success)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto& node = nodes[0].data.data_nodes[8];
|
||||
if (node.len != SCE_NP_SERVICE_ID_SIZE)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
// Trim null characters
|
||||
const auto& vec = node.data.data_vec;
|
||||
auto it = std::find(vec.begin(), vec.end(), 0);
|
||||
return std::string(vec.begin(), it);
|
||||
}
|
||||
|
||||
std::optional<ticket_data> ticket::parse_node(std::size_t index) const
|
||||
{
|
||||
if ((index + MIN_TICKET_DATA_SIZE) > size())
|
||||
|
|
@ -1273,19 +1292,20 @@ namespace np
|
|||
return false;
|
||||
}
|
||||
|
||||
u32 np_handler::generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, SceNpMatching2Event event_type)
|
||||
u32 np_handler::generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, SceNpMatching2Event event_type, bool abortable)
|
||||
{
|
||||
callback_info ret;
|
||||
|
||||
const auto ctx = get_match2_context(ctx_id);
|
||||
ensure(ctx);
|
||||
|
||||
const u32 req_id = get_req_id(optParam ? optParam->appReqId : ctx->default_match2_optparam.appReqId);
|
||||
|
||||
ret.ctx_id = ctx_id;
|
||||
ret.cb_arg = (optParam && optParam->cbFuncArg) ? optParam->cbFuncArg : ctx->default_match2_optparam.cbFuncArg;
|
||||
ret.cb = (optParam && optParam->cbFunc) ? optParam->cbFunc : ctx->default_match2_optparam.cbFunc;
|
||||
ret.event_type = event_type;
|
||||
callback_info ret{
|
||||
.ctx_id = ctx_id,
|
||||
.cb = (optParam && optParam->cbFunc) ? optParam->cbFunc : ctx->default_match2_optparam.cbFunc,
|
||||
.cb_arg = (optParam && optParam->cbFuncArg) ? optParam->cbFuncArg : ctx->default_match2_optparam.cbFuncArg,
|
||||
.event_type = event_type,
|
||||
.abortable = abortable,
|
||||
};
|
||||
|
||||
nph_log.trace("Callback used is 0x%x with req_id %d", ret.cb, req_id);
|
||||
|
||||
|
|
@ -1310,16 +1330,22 @@ namespace np
|
|||
return cb_info;
|
||||
}
|
||||
|
||||
bool np_handler::abort_request(u32 req_id)
|
||||
error_code np_handler::abort_request(u32 req_id)
|
||||
{
|
||||
auto cb_info_opt = take_pending_request(req_id);
|
||||
std::lock_guard lock(mutex_pending_requests);
|
||||
|
||||
if (!cb_info_opt)
|
||||
return false;
|
||||
if (!pending_requests.contains(req_id))
|
||||
return SCE_NP_MATCHING2_ERROR_REQUEST_NOT_FOUND;
|
||||
|
||||
cb_info_opt->queue_callback(req_id, 0, SCE_NP_MATCHING2_ERROR_ABORTED, 0);
|
||||
if (!::at32(pending_requests, req_id).abortable)
|
||||
return SCE_NP_MATCHING2_ERROR_CANNOT_ABORT;
|
||||
|
||||
return true;
|
||||
const auto cb_info = std::move(::at32(pending_requests, req_id));
|
||||
pending_requests.erase(req_id);
|
||||
|
||||
cb_info.queue_callback(req_id, 0, SCE_NP_MATCHING2_ERROR_ABORTED, 0);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
event_data& np_handler::allocate_req_result(u32 event_key, u32 max_size, u32 initial_size)
|
||||
|
|
@ -1345,6 +1371,24 @@ namespace np
|
|||
return history;
|
||||
}
|
||||
|
||||
u32 np_handler::get_clan_ticket_ready()
|
||||
{
|
||||
return clan_ticket_ready.load();
|
||||
}
|
||||
|
||||
ticket np_handler::get_clan_ticket()
|
||||
{
|
||||
clan_ticket_ready.wait(0, atomic_wait_timeout{60'000'000'000}); // 60 seconds
|
||||
|
||||
if (!clan_ticket_ready.load())
|
||||
{
|
||||
rpcn_log.error("Failed to get clan ticket within timeout.");
|
||||
return ticket{};
|
||||
}
|
||||
|
||||
return clan_ticket;
|
||||
}
|
||||
|
||||
constexpr usz MAX_HISTORY_ENTRIES = 200;
|
||||
|
||||
void np_handler::add_player_to_history(const SceNpId* npid, const char* description)
|
||||
|
|
@ -1702,4 +1746,17 @@ namespace np
|
|||
return ctx;
|
||||
}
|
||||
|
||||
void np_handler::callback_info::queue_callback(u32 req_id, u32 event_key, s32 error_code, u32 data_size) const
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
sysutil_register_cb([=, ctx_id = this->ctx_id, event_type = this->event_type, cb = this->cb, cb_arg = this->cb_arg](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
sceNp2.trace("Calling callback 0x%x with req_id %d, event_type: 0x%x, error_code: 0x%x", cb, req_id, event_type, error_code);
|
||||
cb(cb_ppu, ctx_id, req_id, event_type, event_key, error_code, data_size, cb_arg);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace np
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <queue>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/Modules/sceNp.h"
|
||||
|
|
@ -69,6 +70,7 @@ namespace np
|
|||
bool empty() const;
|
||||
|
||||
bool get_value(s32 param_id, vm::ptr<SceNpTicketParam> param) const;
|
||||
std::string get_service_id() const;
|
||||
|
||||
private:
|
||||
std::optional<ticket_data> parse_node(std::size_t index) const;
|
||||
|
|
@ -253,11 +255,13 @@ namespace np
|
|||
// Misc stuff
|
||||
void req_ticket(u32 version, const SceNpId* npid, const char* service_id, const u8* cookie, u32 cookie_size, const char* entitlement_id, u32 consumed_count);
|
||||
const ticket& get_ticket() const;
|
||||
u32 get_clan_ticket_ready();
|
||||
ticket get_clan_ticket();
|
||||
void add_player_to_history(const SceNpId* npid, const char* description);
|
||||
u32 add_players_to_history(const SceNpId* npids, const char* description, u32 count);
|
||||
u32 get_players_history_count(u32 options);
|
||||
bool get_player_history_entry(u32 options, u32 index, SceNpId* npid);
|
||||
bool abort_request(u32 req_id);
|
||||
error_code abort_request(u32 req_id);
|
||||
|
||||
// For signaling
|
||||
void req_sign_infos(const std::string& npid, u32 conn_id);
|
||||
|
|
@ -372,21 +376,12 @@ namespace np
|
|||
vm::ptr<SceNpMatching2RequestCallback> cb;
|
||||
vm::ptr<void> cb_arg;
|
||||
SceNpMatching2Event event_type;
|
||||
bool abortable;
|
||||
|
||||
void queue_callback(u32 req_id, u32 event_key, s32 error_code, u32 data_size) const
|
||||
{
|
||||
if (cb)
|
||||
{
|
||||
sysutil_register_cb([=, ctx_id = this->ctx_id, event_type = this->event_type, cb = this->cb, cb_arg = this->cb_arg](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
cb(cb_ppu, ctx_id, req_id, event_type, event_key, error_code, data_size, cb_arg);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
void queue_callback(u32 req_id, u32 event_key, s32 error_code, u32 data_size) const;
|
||||
};
|
||||
|
||||
u32 generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, SceNpMatching2Event event_type);
|
||||
u32 generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, SceNpMatching2Event event_type, bool abortable);
|
||||
std::optional<callback_info> take_pending_request(u32 req_id);
|
||||
|
||||
private:
|
||||
|
|
@ -415,6 +410,10 @@ namespace np
|
|||
|
||||
ticket current_ticket;
|
||||
|
||||
// Clan ticket
|
||||
atomic_t<u32> clan_ticket_ready = 0;
|
||||
ticket clan_ticket;
|
||||
|
||||
// IP & DNS info
|
||||
std::string hostname = "localhost";
|
||||
std::array<u8, 6> ether_address{};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
#include "stdafx.h"
|
||||
#include "Emu/Cell/Modules/sceNp.h"
|
||||
#include "Emu/Cell/Modules/sceNp2.h"
|
||||
#include "Emu/NP/clans_client.h"
|
||||
#include "Emu/NP/rpcn_types.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUCallback.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
#include "Emu/system_config.h"
|
||||
|
|
@ -69,7 +70,7 @@ namespace np
|
|||
u32 np_handler::get_server_status(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, u16 server_id)
|
||||
{
|
||||
// TODO: actually implement interaction with server for this?
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetServerInfo);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetServerInfo, true);
|
||||
const u32 event_key = get_event_key();
|
||||
|
||||
auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetServerInfo, sizeof(SceNpMatching2GetServerInfoResponse));
|
||||
|
|
@ -87,7 +88,7 @@ namespace np
|
|||
|
||||
u32 np_handler::create_server_context(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, u16 /*server_id*/)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_CreateServerContext);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_CreateServerContext, false);
|
||||
|
||||
const auto cb_info_opt = take_pending_request(req_id);
|
||||
ensure(cb_info_opt);
|
||||
|
|
@ -98,7 +99,7 @@ namespace np
|
|||
|
||||
u32 np_handler::delete_server_context(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, u16 /*server_id*/)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_DeleteServerContext);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_DeleteServerContext, false);
|
||||
|
||||
const auto cb_info_opt = take_pending_request(req_id);
|
||||
ensure(cb_info_opt);
|
||||
|
|
@ -109,7 +110,7 @@ namespace np
|
|||
|
||||
u32 np_handler::get_world_list(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, u16 server_id)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetWorldInfoList);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetWorldInfoList, true);
|
||||
|
||||
if (!get_rpcn()->get_world_list(req_id, get_match2_context(ctx_id)->communicationId, server_id))
|
||||
{
|
||||
|
|
@ -159,7 +160,7 @@ namespace np
|
|||
|
||||
u32 np_handler::create_join_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2CreateJoinRoomRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_CreateJoinRoom);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_CreateJoinRoom, false);
|
||||
|
||||
extra_nps::print_SceNpMatching2CreateJoinRoomRequest(req);
|
||||
|
||||
|
|
@ -221,7 +222,7 @@ namespace np
|
|||
|
||||
u32 np_handler::join_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2JoinRoomRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom, false);
|
||||
|
||||
extra_nps::print_SceNpMatching2JoinRoomRequest(req);
|
||||
|
||||
|
|
@ -311,7 +312,7 @@ namespace np
|
|||
|
||||
u32 np_handler::leave_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2LeaveRoomRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_LeaveRoom);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_LeaveRoom, false);
|
||||
|
||||
if (!get_rpcn()->leave_room(req_id, get_match2_context(ctx_id)->communicationId, req))
|
||||
{
|
||||
|
|
@ -356,7 +357,7 @@ namespace np
|
|||
|
||||
u32 np_handler::search_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SearchRoomRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SearchRoom);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SearchRoom, true);
|
||||
|
||||
extra_nps::print_SceNpMatching2SearchRoomRequest(req);
|
||||
|
||||
|
|
@ -395,7 +396,7 @@ namespace np
|
|||
|
||||
u32 np_handler::get_roomdata_external_list(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2GetRoomDataExternalListRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataExternalList);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataExternalList, true);
|
||||
|
||||
extra_nps::print_SceNpMatching2GetRoomDataExternalListRequest(req);
|
||||
|
||||
|
|
@ -435,7 +436,7 @@ namespace np
|
|||
|
||||
u32 np_handler::set_roomdata_external(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SetRoomDataExternalRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal, false);
|
||||
|
||||
extra_nps::print_SceNpMatching2SetRoomDataExternalRequest(req);
|
||||
|
||||
|
|
@ -470,7 +471,7 @@ namespace np
|
|||
|
||||
u32 np_handler::get_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2GetRoomDataInternalRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataInternal);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataInternal, true);
|
||||
|
||||
if (!get_rpcn()->get_roomdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req))
|
||||
{
|
||||
|
|
@ -524,7 +525,7 @@ namespace np
|
|||
|
||||
u32 np_handler::set_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SetRoomDataInternalRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataInternal);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataInternal, false);
|
||||
|
||||
extra_nps::print_SceNpMatching2SetRoomDataInternalRequest(req);
|
||||
|
||||
|
|
@ -558,7 +559,7 @@ namespace np
|
|||
|
||||
u32 np_handler::get_roommemberdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2GetRoomMemberDataInternalRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomMemberDataInternal);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomMemberDataInternal, true);
|
||||
extra_nps::print_SceNpMatching2GetRoomMemberDataInternalRequest(req);
|
||||
|
||||
if (!get_rpcn()->get_roommemberdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req))
|
||||
|
|
@ -610,7 +611,7 @@ namespace np
|
|||
|
||||
u32 np_handler::set_roommemberdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SetRoomMemberDataInternalRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomMemberDataInternal);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomMemberDataInternal, false);
|
||||
|
||||
extra_nps::print_SceNpMatching2SetRoomMemberDataInternalRequest(req);
|
||||
|
||||
|
|
@ -646,7 +647,7 @@ namespace np
|
|||
|
||||
u32 np_handler::set_userinfo(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SetUserInfoRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetUserInfo);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetUserInfo, false);
|
||||
|
||||
if (!get_rpcn()->set_userinfo(req_id, get_match2_context(ctx_id)->communicationId, req))
|
||||
{
|
||||
|
|
@ -675,7 +676,7 @@ namespace np
|
|||
|
||||
u32 np_handler::get_ping_info(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SignalingGetPingInfoRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SignalingGetPingInfo);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SignalingGetPingInfo, true);
|
||||
|
||||
if (!get_rpcn()->ping_room_owner(req_id, get_match2_context(ctx_id)->communicationId, req->roomId))
|
||||
{
|
||||
|
|
@ -722,7 +723,7 @@ namespace np
|
|||
|
||||
u32 np_handler::send_room_message(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SendRoomMessageRequest* req)
|
||||
{
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SendRoomMessage);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SendRoomMessage, false);
|
||||
|
||||
if (!get_rpcn()->send_room_message(req_id, get_match2_context(ctx_id)->communicationId, req))
|
||||
{
|
||||
|
|
@ -803,7 +804,7 @@ namespace np
|
|||
|
||||
extra_nps::print_SceNpMatching2GetLobbyInfoListRequest(req);
|
||||
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetLobbyInfoList);
|
||||
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetLobbyInfoList, false);
|
||||
auto cb_info_opt = take_pending_request(req_id);
|
||||
|
||||
if (!cb_info_opt)
|
||||
|
|
@ -863,7 +864,20 @@ namespace np
|
|||
auto ticket_raw = reply.get_rawdata();
|
||||
ensure(!reply.is_error(), "Malformed reply to RequestTicket command");
|
||||
|
||||
current_ticket = ticket(std::move(ticket_raw));
|
||||
auto incoming_ticket = ticket(std::move(ticket_raw));
|
||||
|
||||
// Clans: check if ticket belongs to the clan service.
|
||||
// If so, hijack the ticket and cache it for future use.
|
||||
if (incoming_ticket.get_service_id() == CLANS_SERVICE_ID)
|
||||
{
|
||||
clan_ticket = incoming_ticket;
|
||||
clan_ticket_ready.store(1);
|
||||
clan_ticket_ready.notify_all();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
current_ticket = incoming_ticket;
|
||||
auto ticket_size = static_cast<s32>(current_ticket.size());
|
||||
|
||||
if (manager_cb)
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ namespace extra_nps
|
|||
|
||||
for (u32 i = 0; i < req->roomSearchableIntAttrExternalNum && req->roomSearchableIntAttrExternal; i++)
|
||||
print_int_attr(&req->roomSearchableIntAttrExternal[i]);
|
||||
|
||||
|
||||
sceNp2.warning("roomSearchableBinAttrExternal: *0x%x", req->roomSearchableBinAttrExternal);
|
||||
sceNp2.warning("roomSearchableBinAttrExternalNum: %d", req->roomSearchableBinAttrExternalNum);
|
||||
|
||||
|
|
@ -186,7 +186,7 @@ namespace extra_nps
|
|||
sceNp2.warning("SceNpMatching2SearchRoomResponse:");
|
||||
print_range(&resp->range);
|
||||
|
||||
const SceNpMatching2RoomDataExternal *room_ptr = resp->roomDataExternal.get_ptr();
|
||||
const SceNpMatching2RoomDataExternal* room_ptr = resp->roomDataExternal.get_ptr();
|
||||
for (u32 i = 0; i < resp->range.total; i++)
|
||||
{
|
||||
sceNp2.warning("SceNpMatching2SearchRoomResponse[%d]:", i);
|
||||
|
|
@ -471,7 +471,7 @@ namespace extra_nps
|
|||
{
|
||||
sceNp.warning("ptr: *0x%x", data->value.data.ptr);
|
||||
sceNp.warning("size: %d", data->value.data.size);
|
||||
sceNp.warning("data:\n%s", fmt::buf_to_hexstring(static_cast<u8 *>(data->value.data.ptr.get_ptr()), data->value.data.size));
|
||||
sceNp.warning("data:\n%s", fmt::buf_to_hexstring(static_cast<u8*>(data->value.data.ptr.get_ptr()), data->value.data.size));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -577,4 +577,16 @@ namespace extra_nps
|
|||
}
|
||||
}
|
||||
|
||||
void print_SceNpMatching2RoomSlotInfo(const SceNpMatching2RoomSlotInfo* data)
|
||||
{
|
||||
sceNp.warning("SceNpMatching2RoomSlotInfo:");
|
||||
sceNp.warning("roomId: %d", data->roomId);
|
||||
sceNp.warning("joinedSlotMask: %x", data->joinedSlotMask);
|
||||
sceNp.warning("passwordSlotMask: %x", data->passwordSlotMask);
|
||||
sceNp.warning("publicSlotNum: %d", data->publicSlotNum);
|
||||
sceNp.warning("privateSlotNum: %d", data->privateSlotNum);
|
||||
sceNp.warning("openPublicSlotNum: %d", data->openPublicSlotNum);
|
||||
sceNp.warning("openPrivateSlotNum: %d", data->openPrivateSlotNum);
|
||||
}
|
||||
|
||||
} // namespace extra_nps
|
||||
|
|
|
|||
|
|
@ -41,4 +41,5 @@ namespace extra_nps
|
|||
void print_SceNpMatchingRoomStatus(const SceNpMatchingRoomStatus* data);
|
||||
void print_SceNpMatchingJoinedRoomInfo(const SceNpMatchingJoinedRoomInfo* data);
|
||||
void print_SceNpMatchingSearchJoinRoomInfo(const SceNpMatchingSearchJoinRoomInfo* data);
|
||||
void print_SceNpMatching2RoomSlotInfo(const SceNpMatching2RoomSlotInfo* data);
|
||||
} // namespace extra_nps
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ 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 = 26;
|
||||
constexpr u32 RPCN_PROTOCOL_VERSION = 27;
|
||||
constexpr usz RPCN_HEADER_SIZE = 15;
|
||||
|
||||
const char* error_to_explanation(rpcn::ErrorType error)
|
||||
|
|
@ -1495,7 +1495,7 @@ namespace rpcn
|
|||
return notifs;
|
||||
}
|
||||
|
||||
std::unordered_map<u32, std::pair<rpcn::CommandType, std::vector<u8>>> rpcn_client::get_replies()
|
||||
std::map<u32, std::pair<rpcn::CommandType, std::vector<u8>>> rpcn_client::get_replies()
|
||||
{
|
||||
std::lock_guard lock(mutex_replies);
|
||||
auto ret_replies = std::move(replies);
|
||||
|
|
@ -2019,9 +2019,13 @@ namespace rpcn
|
|||
}
|
||||
final_grouppasswordconfig_vec = builder.CreateVector(davec);
|
||||
}
|
||||
u64 final_passwordSlotMask = 0;
|
||||
|
||||
flatbuffers::Offset<flatbuffers::Vector<u64>> final_passwordSlotMask;
|
||||
if (req->passwordSlotMask)
|
||||
final_passwordSlotMask = *req->passwordSlotMask;
|
||||
{
|
||||
const u64 value = *req->passwordSlotMask;
|
||||
final_passwordSlotMask = builder.CreateVector(&value, 1);
|
||||
}
|
||||
|
||||
flatbuffers::Offset<flatbuffers::Vector<u16>> final_ownerprivilege_vec;
|
||||
if (req->ownerPrivilegeRankNum && req->ownerPrivilegeRank)
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ namespace rpcn
|
|||
std::optional<std::pair<std::string, friend_online_data>> get_friend_presence_by_npid(const std::string& npid);
|
||||
|
||||
std::vector<std::pair<rpcn::NotificationType, std::vector<u8>>> get_notifications();
|
||||
std::unordered_map<u32, std::pair<rpcn::CommandType, std::vector<u8>>> get_replies();
|
||||
std::map<u32, std::pair<rpcn::CommandType, std::vector<u8>>> get_replies();
|
||||
std::unordered_map<std::string, friend_online_data> get_presence_updates();
|
||||
std::map<std::string, friend_online_data> get_presence_states();
|
||||
|
||||
|
|
@ -428,8 +428,8 @@ namespace rpcn
|
|||
|
||||
shared_mutex mutex_notifs, mutex_replies, mutex_replies_sync, mutex_presence_updates;
|
||||
std::vector<std::pair<rpcn::NotificationType, std::vector<u8>>> notifications; // notif type / data
|
||||
std::unordered_map<u32, std::pair<rpcn::CommandType, std::vector<u8>>> replies; // req id / (command / data)
|
||||
std::unordered_map<u64, std::pair<rpcn::CommandType, std::vector<u8>>> replies_sync; // same but for sync replies(see handle_input())
|
||||
std::map<u32, std::pair<rpcn::CommandType, std::vector<u8>>> replies; // req id / (command / data)
|
||||
std::map<u64, std::pair<rpcn::CommandType, std::vector<u8>>> replies_sync; // same but for sync replies(see handle_input())
|
||||
std::unordered_map<std::string, friend_online_data> presence_updates; // npid / presence data
|
||||
|
||||
// Messages
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@ std::string cfg_rpcn::get_host() const
|
|||
std::vector<std::pair<std::string, std::string>> cfg_rpcn::get_hosts()
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> vec_hosts;
|
||||
auto hosts_list = fmt::split(hosts.to_string(), {"|||"});
|
||||
const std::string host_str = hosts.to_string();
|
||||
const auto hosts_list = fmt::split_sv(host_str, {"|||"});
|
||||
|
||||
for (const auto& cur_host : hosts_list)
|
||||
{
|
||||
|
|
@ -190,9 +191,8 @@ bool cfg_rpcn::add_host(std::string_view new_description, std::string_view new_h
|
|||
|
||||
bool cfg_rpcn::del_host(std::string_view del_description, std::string_view del_host)
|
||||
{
|
||||
// Do not delete default servers
|
||||
if ((del_description == "Official RPCN Server" && del_host == "np.rpcs3.net") ||
|
||||
(del_description == "RPCN Test Server" && del_host == "test-np.rpcs3.net"))
|
||||
// Do not delete default server
|
||||
if (del_description == "Official RPCN Server" && del_host == "np.rpcs3.net")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ struct cfg_rpcn : cfg::node
|
|||
cfg::string npid{this, "NPID", ""};
|
||||
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::string hosts{this, "Hosts", "Official RPCN Server|np.rpcs3.net"};
|
||||
cfg::_bool ipv6_support{this, "IPv6 support", true};
|
||||
|
||||
void load();
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ namespace rsx
|
|||
AUDIT(_loc < _size);
|
||||
|
||||
const auto remaining = (_size - _loc);
|
||||
memmove(pos + 1, pos, remaining * sizeof(Ty));
|
||||
std::memmove(pos + 1, pos, remaining * sizeof(Ty));
|
||||
|
||||
*pos = val;
|
||||
_size++;
|
||||
|
|
@ -365,7 +365,7 @@ namespace rsx
|
|||
AUDIT(_loc < _size);
|
||||
|
||||
const u32 remaining = (_size - _loc);
|
||||
memmove(pos + 1, pos, remaining * sizeof(Ty));
|
||||
std::memmove(pos + 1, pos, remaining * sizeof(Ty));
|
||||
|
||||
*pos = val;
|
||||
_size++;
|
||||
|
|
@ -373,6 +373,31 @@ namespace rsx
|
|||
return pos;
|
||||
}
|
||||
|
||||
iterator insert(iterator where, span_like<Ty> auto const& values)
|
||||
{
|
||||
ensure(where >= _data);
|
||||
const auto _loc = offset(where);
|
||||
const auto in_size = static_cast<u32>(values.size());
|
||||
const auto in_size_bytes = in_size * sizeof(Ty);
|
||||
|
||||
reserve(_size + in_size);
|
||||
|
||||
if (_loc >= _size)
|
||||
{
|
||||
where = _data + _size;
|
||||
std::memcpy(where, values.data(), in_size_bytes);
|
||||
_size += in_size;
|
||||
return where;
|
||||
}
|
||||
|
||||
const u32 remaining_bytes = (_size - _loc) * sizeof(Ty);
|
||||
where = _data + _loc;
|
||||
std::memmove(where + in_size, where, remaining_bytes);
|
||||
std::memmove(where, values.data(), in_size_bytes);
|
||||
_size += in_size;
|
||||
return where;
|
||||
}
|
||||
|
||||
void operator += (const rsx::simple_array<Ty>& that)
|
||||
{
|
||||
const auto old_size = _size;
|
||||
|
|
|
|||
|
|
@ -120,9 +120,15 @@ namespace rsx
|
|||
{
|
||||
result.font_names.emplace_back("Arial.ttf");
|
||||
result.font_names.emplace_back("arial.ttf");
|
||||
#ifndef _WIN32
|
||||
result.font_names.emplace_back("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"); // ubuntu
|
||||
result.font_names.emplace_back("/usr/share/fonts/TTF/DejaVuSans.ttf"); // arch
|
||||
#ifdef __APPLE__
|
||||
result.font_names.emplace_back("DejaVuSans.ttf");
|
||||
result.font_names.emplace_back("NotoSans-Regular.ttf");
|
||||
result.font_names.emplace_back("Roboto-Regular.ttf");
|
||||
result.font_names.emplace_back("OpenSans-Regular.ttf");
|
||||
result.font_names.emplace_back("FreeSans.ttf");
|
||||
#elif !defined(_WIN32)
|
||||
result.font_names.emplace_back("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"); // ubuntu
|
||||
result.font_names.emplace_back("/usr/share/fonts/TTF/DejaVuSans.ttf"); // arch
|
||||
#endif
|
||||
// Attempt to load a font from dev_flash as a last resort
|
||||
result.font_names.emplace_back("SCE-PS3-VR-R-LATIN.TTF");
|
||||
|
|
@ -207,7 +213,7 @@ namespace rsx
|
|||
return font_found;
|
||||
};
|
||||
|
||||
for (const auto& font_file : fs_settings.font_names)
|
||||
for (const std::string& font_file : fs_settings.font_names)
|
||||
{
|
||||
if (fs::is_file(font_file))
|
||||
{
|
||||
|
|
@ -256,7 +262,7 @@ namespace rsx
|
|||
{
|
||||
if (fallback_bytes.empty())
|
||||
{
|
||||
fmt::throw_exception("Failed to initialize font for character 0x%x on codepage %d.", static_cast<u32>(c), static_cast<u32>(codepage_id));
|
||||
fmt::throw_exception("Failed to initialize font for character 0x%x on codepage %d.\nLookup dirs:\n%s\nTarget fonts:\n%s", static_cast<u32>(c), static_cast<u32>(codepage_id), fmt::merge(fs_settings.lookup_font_dirs, "\n"), fmt::merge(fs_settings.font_names, "\n"));
|
||||
}
|
||||
|
||||
rsx_log.error("Failed to initialize font for character 0x%x on codepage %d. Falling back to font '%s'", static_cast<u32>(c), static_cast<u32>(codepage_id), fallback_file);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@ namespace rsx::assembler
|
|||
}
|
||||
};
|
||||
|
||||
struct CFGPass
|
||||
{
|
||||
virtual void run(FlowGraph& graph) = 0;
|
||||
};
|
||||
|
||||
FlowGraph deconstruct_fragment_program(const RSXFragmentProgram& prog);
|
||||
}
|
||||
|
||||
|
|
|
|||
455
rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp
Normal file
455
rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp
Normal file
|
|
@ -0,0 +1,455 @@
|
|||
#include "stdafx.h"
|
||||
#include "FPASM.h"
|
||||
#include "Emu/RSX/Program/RSXFragmentProgram.h"
|
||||
|
||||
#include <stack>
|
||||
|
||||
#ifndef _WIN32
|
||||
#define sscanf_s sscanf
|
||||
#endif
|
||||
|
||||
namespace rsx::assembler
|
||||
{
|
||||
struct FP_opcode_encoding_t
|
||||
{
|
||||
FP_opcode op;
|
||||
bool exec_if_lt;
|
||||
bool exec_if_eq;
|
||||
bool exec_if_gt;
|
||||
bool set_cond;
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string_view, FP_opcode_encoding_t> s_opcode_lookup
|
||||
{
|
||||
// Arithmetic
|
||||
{ "NOP", { .op = RSX_FP_OPCODE_NOP, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "MOV", { .op = RSX_FP_OPCODE_MOV, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "MUL", { .op = RSX_FP_OPCODE_MUL, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "ADD", { .op = RSX_FP_OPCODE_ADD, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "MAD", { .op = RSX_FP_OPCODE_MAD, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "FMA", { .op = RSX_FP_OPCODE_MAD, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "DP3", { .op = RSX_FP_OPCODE_DP3, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "DP4", { .op = RSX_FP_OPCODE_DP4, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
|
||||
// Constant load
|
||||
{ "SFL", {.op = RSX_FP_OPCODE_SFL, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "STR", {.op = RSX_FP_OPCODE_STR, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
|
||||
// Pack-unpack operations are great for testing dependencies
|
||||
{ "PKH", { .op = RSX_FP_OPCODE_PK2, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "UPH", { .op = RSX_FP_OPCODE_UP2, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "PK16U", { .op = RSX_FP_OPCODE_PK16, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "UP16U", { .op = RSX_FP_OPCODE_UP16, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "PK8U", { .op = RSX_FP_OPCODE_PKB, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "UP8U", { .op = RSX_FP_OPCODE_UPB, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "PK8G", { .op = RSX_FP_OPCODE_PKG, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "UP8G", { .op = RSX_FP_OPCODE_UPG, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "PK8S", { .op = RSX_FP_OPCODE_PK4, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "UP8S", { .op = RSX_FP_OPCODE_UP4, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
|
||||
// Basic conditionals
|
||||
{ "IF.LT", { .op = RSX_FP_OPCODE_IFE, .exec_if_lt = true, .exec_if_eq = false, .exec_if_gt = false, .set_cond = false } },
|
||||
{ "IF.LE", { .op = RSX_FP_OPCODE_IFE, .exec_if_lt = true, .exec_if_eq = true, .exec_if_gt = false, .set_cond = false } },
|
||||
{ "IF.EQ", { .op = RSX_FP_OPCODE_IFE, .exec_if_lt = false, .exec_if_eq = true, .exec_if_gt = false, .set_cond = false } },
|
||||
{ "IF.GE", { .op = RSX_FP_OPCODE_IFE, .exec_if_lt = false, .exec_if_eq = true, .exec_if_gt = true, .set_cond = false } },
|
||||
{ "IF.GT", { .op = RSX_FP_OPCODE_IFE, .exec_if_lt = false, .exec_if_eq = false, .exec_if_gt = true, .set_cond = false } },
|
||||
|
||||
{ "SLT", { .op = RSX_FP_OPCODE_SLT, .exec_if_lt = false, .exec_if_eq = false, .exec_if_gt = false, .set_cond = true } },
|
||||
{ "SEQ", { .op = RSX_FP_OPCODE_SEQ, .exec_if_lt = false, .exec_if_eq = false, .exec_if_gt = false, .set_cond = true } },
|
||||
{ "SGT", { .op = RSX_FP_OPCODE_SGT, .exec_if_lt = false, .exec_if_eq = false, .exec_if_gt = false, .set_cond = true } },
|
||||
|
||||
// TODO: Add more
|
||||
|
||||
};
|
||||
|
||||
Instruction* FPIR::load(const RegisterRef& ref, int operand, Instruction* prev)
|
||||
{
|
||||
Instruction* target = prev;
|
||||
if (!target)
|
||||
{
|
||||
m_instructions.push_back({});
|
||||
target = &m_instructions.back();
|
||||
}
|
||||
|
||||
SRC_Common src{ .HEX = target->bytecode[operand + 1] };
|
||||
src.reg_type = RSX_FP_REGISTER_TYPE_TEMP;
|
||||
src.fp16 = ref.reg.f16 ? 1 : 0;
|
||||
src.tmp_reg_index = static_cast<u32>(ref.reg.id);
|
||||
|
||||
src.swizzle_x = 0;
|
||||
src.swizzle_y = 1;
|
||||
src.swizzle_z = 2;
|
||||
src.swizzle_w = 3;
|
||||
|
||||
target->bytecode[operand + 1] = src.HEX;
|
||||
return target;
|
||||
}
|
||||
|
||||
Instruction* FPIR::load(const std::array<f32, 4>& constants, int operand, Instruction* prev)
|
||||
{
|
||||
Instruction* target = prev;
|
||||
if (!target)
|
||||
{
|
||||
m_instructions.push_back({});
|
||||
target = &m_instructions.back();
|
||||
}
|
||||
|
||||
// Unsupported for now
|
||||
ensure(target->length == 4, "FPIR cannot encode more than one constant load per instruction");
|
||||
|
||||
SRC_Common src{ .HEX = target->bytecode[operand + 1] };
|
||||
src.reg_type = RSX_FP_REGISTER_TYPE_CONSTANT;
|
||||
target->bytecode[operand + 1] = src.HEX;
|
||||
|
||||
src.swizzle_x = 0;
|
||||
src.swizzle_y = 1;
|
||||
src.swizzle_z = 2;
|
||||
src.swizzle_w = 3;
|
||||
|
||||
// Embed literal constant
|
||||
std::memcpy(&target->bytecode[4], constants.data(), 4 * sizeof(u32));
|
||||
target->length = 8;
|
||||
return target;
|
||||
}
|
||||
|
||||
Instruction* FPIR::store(const RegisterRef& ref, Instruction* prev)
|
||||
{
|
||||
Instruction* target = prev;
|
||||
if (!target)
|
||||
{
|
||||
m_instructions.push_back({});
|
||||
target = &m_instructions.back();
|
||||
}
|
||||
|
||||
OPDEST dst{ .HEX = target->bytecode[0] };
|
||||
dst.dest_reg = static_cast<u32>(ref.reg.id);
|
||||
dst.fp16 = ref.reg.f16 ? 1 : 0;
|
||||
dst.write_mask = ref.mask;
|
||||
dst.prec = ref.reg.f16 ? RSX_FP_PRECISION_HALF : RSX_FP_PRECISION_REAL;
|
||||
|
||||
target->bytecode[0] = dst.HEX;
|
||||
return target;
|
||||
}
|
||||
|
||||
void FPIR::mov(const RegisterRef& dst, f32 constant)
|
||||
{
|
||||
Instruction* inst = store(dst);
|
||||
inst = load(std::array<f32, 4>{ constant, constant, constant, constant }, 0);
|
||||
inst->opcode = RSX_FP_OPCODE_MOV;
|
||||
}
|
||||
|
||||
void FPIR::mov(const RegisterRef& dst, const RegisterRef& src)
|
||||
{
|
||||
Instruction* inst = store(dst);
|
||||
inst = load(src, 0);
|
||||
inst->opcode = RSX_FP_OPCODE_MOV;
|
||||
}
|
||||
|
||||
void FPIR::add(const RegisterRef& dst, const std::array<f32, 4>& constants)
|
||||
{
|
||||
Instruction* inst = store(dst);
|
||||
inst = load(constants, 0);
|
||||
inst->opcode = RSX_FP_OPCODE_ADD;
|
||||
}
|
||||
|
||||
void FPIR::add(const RegisterRef& dst, const RegisterRef& src)
|
||||
{
|
||||
Instruction* inst = store(dst);
|
||||
inst = load(src, 0);
|
||||
inst->opcode = RSX_FP_OPCODE_ADD;
|
||||
}
|
||||
|
||||
const std::vector<Instruction>& FPIR::instructions() const
|
||||
{
|
||||
return m_instructions;
|
||||
}
|
||||
|
||||
std::vector<u32> FPIR::compile() const
|
||||
{
|
||||
std::vector<u32> result;
|
||||
result.reserve(m_instructions.size() * 4);
|
||||
|
||||
for (const auto& inst : m_instructions)
|
||||
{
|
||||
const auto src = reinterpret_cast<const be_t<u16>*>(inst.bytecode);
|
||||
for (u32 j = 0; j < inst.length; ++j)
|
||||
{
|
||||
const u16 low = src[j * 2];
|
||||
const u16 hi = src[j * 2 + 1];
|
||||
const u32 word = static_cast<u16>(low) | (static_cast<u32>(hi) << 16u);
|
||||
result.push_back(word);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FPIR FPIR::from_source(std::string_view asm_)
|
||||
{
|
||||
std::vector<std::string> instructions = fmt::split(asm_, { "\n", ";" });
|
||||
if (instructions.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto transform_inst = [](std::string_view s)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(s.size());
|
||||
|
||||
bool literal = false;
|
||||
for (const auto& c : s)
|
||||
{
|
||||
if (c == ' ')
|
||||
{
|
||||
if (!literal && !result.empty() && result.back() != ',')
|
||||
{
|
||||
result += ','; // Replace token separator space with comma
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::isspace(c))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!literal && c == '{')
|
||||
{
|
||||
literal = true;
|
||||
}
|
||||
|
||||
if (literal && c == '}')
|
||||
{
|
||||
literal = false;
|
||||
}
|
||||
|
||||
if (c == ',')
|
||||
{
|
||||
result += (literal ? '|' : ',');
|
||||
continue;
|
||||
}
|
||||
|
||||
result += c;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
auto decode_instruction = [&](std::string_view inst, std::string& op, std::string& dst, std::vector<std::string>& sources)
|
||||
{
|
||||
const auto i = transform_inst(inst);
|
||||
if (i.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto tokens = fmt::split(i, { "," });
|
||||
ensure(!tokens.empty(), "Invalid input");
|
||||
|
||||
op = tokens.front();
|
||||
|
||||
if (tokens.size() > 1)
|
||||
{
|
||||
dst = tokens[1];
|
||||
}
|
||||
|
||||
for (size_t n = 2; n < tokens.size(); ++n)
|
||||
{
|
||||
sources.push_back(tokens[n]);
|
||||
}
|
||||
};
|
||||
|
||||
auto get_ref = [](std::string_view reg)
|
||||
{
|
||||
ensure(reg.length() > 1, "Invalid register specifier");
|
||||
|
||||
const auto parts = fmt::split(reg, { "." });
|
||||
ensure(parts.size() > 0 && parts.size() <= 2);
|
||||
|
||||
const auto index = std::stoi(parts[0].substr(1));
|
||||
RegisterRef ref
|
||||
{
|
||||
.reg { .id = index, .f16 = false },
|
||||
.mask = 0x0F
|
||||
};
|
||||
|
||||
if (parts.size() > 1 && parts[1].length() > 0)
|
||||
{
|
||||
// FIXME: No swizzles for now, just lane masking
|
||||
ref.mask = 0;
|
||||
if (parts[1].find("x") != std::string::npos) ref.mask |= (1u << 0);
|
||||
if (parts[1].find("y") != std::string::npos) ref.mask |= (1u << 1);
|
||||
if (parts[1].find("z") != std::string::npos) ref.mask |= (1u << 2);
|
||||
if (parts[1].find("w") != std::string::npos) ref.mask |= (1u << 3);
|
||||
}
|
||||
|
||||
if (reg[0] == 'H' || reg[0] == 'h')
|
||||
{
|
||||
ref.reg.f16 = true;
|
||||
}
|
||||
|
||||
return ref;
|
||||
};
|
||||
|
||||
auto get_constants = [](std::string_view reg) -> std::array<f32, 4>
|
||||
{
|
||||
float x, y, z, w;
|
||||
if (sscanf_s(reg.data(), "#{%f|%f|%f|%f}", &x, &y, &z, &w) == 4)
|
||||
{
|
||||
return { x, y, z, w };
|
||||
}
|
||||
|
||||
if (sscanf_s(reg.data(), "#{%f}", &x) == 1)
|
||||
{
|
||||
return { x, x, x, x };
|
||||
}
|
||||
|
||||
fmt::throw_exception("Invalid constant literal");
|
||||
};
|
||||
|
||||
auto encode_branch_else = [](Instruction* inst, u32 end)
|
||||
{
|
||||
SRC1 src1{ .HEX = inst->bytecode[2] };
|
||||
src1.else_offset = static_cast<u32>(end);
|
||||
inst->bytecode[2] = src1.HEX;
|
||||
};
|
||||
|
||||
auto encode_branch_end = [](Instruction *inst, u32 end)
|
||||
{
|
||||
SRC2 src2 { .HEX = inst->bytecode[3] };
|
||||
src2.end_offset = static_cast<u32>(end);
|
||||
inst->bytecode[3] = src2.HEX;
|
||||
|
||||
SRC1 src1{ .HEX = inst->bytecode[2] };
|
||||
if (!src1.else_offset)
|
||||
{
|
||||
src1.else_offset = static_cast<u32>(end);
|
||||
inst->bytecode[2] = src1.HEX;
|
||||
}
|
||||
};
|
||||
|
||||
auto encode_opcode = [](std::string_view op, Instruction* inst)
|
||||
{
|
||||
OPDEST d0 { .HEX = inst->bytecode[0] };
|
||||
SRC0 s0 { .HEX = inst->bytecode[1] };
|
||||
SRC1 s1 { .HEX = inst->bytecode[2] };
|
||||
|
||||
const auto found = s_opcode_lookup.find(op);
|
||||
if (found == s_opcode_lookup.end())
|
||||
{
|
||||
fmt::throw_exception("Unhandled instruction '%s'", op);
|
||||
}
|
||||
const auto& encoding = found->second;
|
||||
|
||||
inst->opcode = encoding.op;
|
||||
d0.opcode = encoding.op & 0x3F;
|
||||
s1.opcode_hi = (encoding.op > 0x3F)? 1 : 0;
|
||||
s0.exec_if_eq = encoding.exec_if_eq ? 1 : 0;
|
||||
s0.exec_if_gr = encoding.exec_if_gt ? 1 : 0;
|
||||
s0.exec_if_lt = encoding.exec_if_lt ? 1 : 0;
|
||||
d0.set_cond = encoding.set_cond ? 1 : 0;
|
||||
inst->bytecode[0] = d0.HEX;
|
||||
inst->bytecode[1] = s0.HEX;
|
||||
inst->bytecode[2] = s1.HEX;
|
||||
};
|
||||
|
||||
std::string op, dst;
|
||||
std::vector<std::string> sources;
|
||||
|
||||
std::stack<size_t> if_ops;
|
||||
std::stack<size_t> loop_ops;
|
||||
u32 pc = 0;
|
||||
|
||||
FPIR ir{};
|
||||
|
||||
for (const auto& instruction : instructions)
|
||||
{
|
||||
op.clear();
|
||||
dst.clear();
|
||||
sources.clear();
|
||||
decode_instruction(instruction, op, dst, sources);
|
||||
|
||||
if (op.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op.starts_with("IF."))
|
||||
{
|
||||
if_ops.push(ir.m_instructions.size());
|
||||
}
|
||||
else if (op == "LOOP")
|
||||
{
|
||||
loop_ops.push(ir.m_instructions.size());
|
||||
}
|
||||
else if (op == "ELSE")
|
||||
{
|
||||
ensure(!if_ops.empty());
|
||||
encode_branch_else(&ir.m_instructions[if_ops.top()], pc);
|
||||
continue;
|
||||
}
|
||||
else if (op == "ENDIF")
|
||||
{
|
||||
ensure(!if_ops.empty());
|
||||
encode_branch_end(&ir.m_instructions[if_ops.top()], pc);
|
||||
if_ops.pop();
|
||||
continue;
|
||||
}
|
||||
else if (op == "ENDLOOP")
|
||||
{
|
||||
ensure(!loop_ops.empty());
|
||||
encode_branch_end(&ir.m_instructions[loop_ops.top()], pc);
|
||||
loop_ops.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
ir.m_instructions.push_back({});
|
||||
Instruction* target = &ir.m_instructions.back();
|
||||
pc += 4;
|
||||
|
||||
encode_opcode(op, target);
|
||||
ensure(sources.size() == FP::get_operand_count(static_cast<FP_opcode>(target->opcode)), "Invalid operand count for opcode");
|
||||
|
||||
if (dst.empty())
|
||||
{
|
||||
OPDEST dst{ .HEX = target->bytecode[0] };
|
||||
dst.no_dest = 1;
|
||||
target->bytecode[0] = dst.HEX;
|
||||
}
|
||||
else
|
||||
{
|
||||
ir.store(get_ref(dst), target);
|
||||
}
|
||||
|
||||
int operand = 0;
|
||||
bool has_literal = false;
|
||||
for (const auto& source : sources)
|
||||
{
|
||||
if (source.front() == '#')
|
||||
{
|
||||
const auto literal = get_constants(source);
|
||||
ir.load(literal, operand++, target);
|
||||
has_literal = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
ir.load(get_ref(source), operand++, target);
|
||||
}
|
||||
|
||||
if (has_literal)
|
||||
{
|
||||
pc += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ir.m_instructions.empty())
|
||||
{
|
||||
OPDEST d0{ .HEX = ir.m_instructions.back().bytecode[0] };
|
||||
d0.end = 1;
|
||||
|
||||
ir.m_instructions.back().bytecode[0] = d0.HEX;
|
||||
}
|
||||
|
||||
return ir;
|
||||
}
|
||||
}
|
||||
29
rpcs3/Emu/RSX/Program/Assembler/FPASM.h
Normal file
29
rpcs3/Emu/RSX/Program/Assembler/FPASM.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "IR.h"
|
||||
|
||||
namespace rsx::assembler
|
||||
{
|
||||
class FPIR
|
||||
{
|
||||
public:
|
||||
void mov(const RegisterRef& dst, f32 constant);
|
||||
void mov(const RegisterRef& dst, const RegisterRef& src);
|
||||
|
||||
void add(const RegisterRef& dst, const std::array<f32, 4>& constants);
|
||||
void add(const RegisterRef& dst, const RegisterRef& src);
|
||||
|
||||
const std::vector<Instruction>& instructions() const;
|
||||
std::vector<u32> compile() const;
|
||||
|
||||
static FPIR from_source(std::string_view asm_);
|
||||
|
||||
private:
|
||||
Instruction* load(const RegisterRef& reg, int operand, Instruction* target = nullptr);
|
||||
Instruction* load(const std::array<f32, 4>& constants, int operand, Instruction* target = nullptr);
|
||||
Instruction* store(const RegisterRef& reg, Instruction* target = nullptr);
|
||||
|
||||
std::vector<Instruction> m_instructions;
|
||||
};
|
||||
}
|
||||
|
||||
426
rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.cpp
Normal file
426
rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.cpp
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
#include "stdafx.h"
|
||||
#include "FPOpcodes.h"
|
||||
|
||||
#include "Emu/RSX/Common/simple_array.hpp"
|
||||
#include "Emu/RSX/Program/RSXFragmentProgram.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace rsx::assembler::FP
|
||||
{
|
||||
u8 get_operand_count(FP_opcode opcode)
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case RSX_FP_OPCODE_NOP:
|
||||
return 0;
|
||||
case RSX_FP_OPCODE_MOV:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_MUL:
|
||||
case RSX_FP_OPCODE_ADD:
|
||||
return 2;
|
||||
case RSX_FP_OPCODE_MAD:
|
||||
return 3;
|
||||
case RSX_FP_OPCODE_DP3:
|
||||
case RSX_FP_OPCODE_DP4:
|
||||
return 2;
|
||||
case RSX_FP_OPCODE_DST:
|
||||
return 2;
|
||||
case RSX_FP_OPCODE_MIN:
|
||||
case RSX_FP_OPCODE_MAX:
|
||||
return 2;
|
||||
case RSX_FP_OPCODE_SLT:
|
||||
case RSX_FP_OPCODE_SGE:
|
||||
case RSX_FP_OPCODE_SLE:
|
||||
case RSX_FP_OPCODE_SGT:
|
||||
case RSX_FP_OPCODE_SNE:
|
||||
case RSX_FP_OPCODE_SEQ:
|
||||
return 2;
|
||||
case RSX_FP_OPCODE_FRC:
|
||||
case RSX_FP_OPCODE_FLR:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_KIL:
|
||||
return 0;
|
||||
case RSX_FP_OPCODE_PK4:
|
||||
case RSX_FP_OPCODE_UP4:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_DDX:
|
||||
case RSX_FP_OPCODE_DDY:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_TEX:
|
||||
case RSX_FP_OPCODE_TXD:
|
||||
case RSX_FP_OPCODE_TXP:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_RCP:
|
||||
case RSX_FP_OPCODE_RSQ:
|
||||
case RSX_FP_OPCODE_EX2:
|
||||
case RSX_FP_OPCODE_LG2:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_LIT:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_LRP:
|
||||
return 3;
|
||||
case RSX_FP_OPCODE_STR:
|
||||
case RSX_FP_OPCODE_SFL:
|
||||
return 0;
|
||||
case RSX_FP_OPCODE_COS:
|
||||
case RSX_FP_OPCODE_SIN:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_PK2:
|
||||
case RSX_FP_OPCODE_UP2:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_PKB:
|
||||
case RSX_FP_OPCODE_UPB:
|
||||
case RSX_FP_OPCODE_PK16:
|
||||
case RSX_FP_OPCODE_UP16:
|
||||
case RSX_FP_OPCODE_PKG:
|
||||
case RSX_FP_OPCODE_UPG:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_DP2A:
|
||||
return 3;
|
||||
case RSX_FP_OPCODE_TXL:
|
||||
case RSX_FP_OPCODE_TXB:
|
||||
return 2;
|
||||
case RSX_FP_OPCODE_DP2:
|
||||
return 2;
|
||||
case RSX_FP_OPCODE_NRM:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_DIV:
|
||||
case RSX_FP_OPCODE_DIVSQ:
|
||||
return 2;
|
||||
case RSX_FP_OPCODE_LIF:
|
||||
return 1;
|
||||
case RSX_FP_OPCODE_FENCT:
|
||||
case RSX_FP_OPCODE_FENCB:
|
||||
case RSX_FP_OPCODE_BRK:
|
||||
case RSX_FP_OPCODE_CAL:
|
||||
case RSX_FP_OPCODE_IFE:
|
||||
case RSX_FP_OPCODE_LOOP:
|
||||
case RSX_FP_OPCODE_REP:
|
||||
case RSX_FP_OPCODE_RET:
|
||||
// Flow control. Special registers are provided for these outside the common file
|
||||
return 0;
|
||||
|
||||
// The rest are unimplemented and not encountered in real software.
|
||||
// TODO: Probe these on real PS3 and figure out what they actually do.
|
||||
case RSX_FP_OPCODE_POW:
|
||||
fmt::throw_exception("Unimplemented POW instruction."); // Unused
|
||||
case RSX_FP_OPCODE_BEM:
|
||||
case RSX_FP_OPCODE_TEXBEM:
|
||||
case RSX_FP_OPCODE_TXPBEM:
|
||||
case RSX_FP_OPCODE_BEMLUM:
|
||||
fmt::throw_exception("Unimplemented BEM class instruction"); // Unused
|
||||
case RSX_FP_OPCODE_REFL:
|
||||
return 2;
|
||||
case RSX_FP_OPCODE_TIMESWTEX:
|
||||
fmt::throw_exception("Unimplemented TIMESWTEX instruction"); // Unused
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns a lane mask for the given operand.
|
||||
// The lane mask is the fixed function hardware lane so swizzles need to be applied on top to resolve the real data channel.
|
||||
u32 get_src_vector_lane_mask(const RSXFragmentProgram& prog, const Instruction* instruction, u32 operand)
|
||||
{
|
||||
constexpr u32 x = 0b0001;
|
||||
constexpr u32 y = 0b0010;
|
||||
constexpr u32 z = 0b0100;
|
||||
constexpr u32 w = 0b1000;
|
||||
constexpr u32 xy = 0b0011;
|
||||
constexpr u32 xyz = 0b0111;
|
||||
constexpr u32 xyzw = 0b1111;
|
||||
|
||||
const auto decode = [&](const rsx::simple_array<u32>& masks) -> u32
|
||||
{
|
||||
return operand < masks.size()
|
||||
? masks[operand]
|
||||
: 0u;
|
||||
};
|
||||
|
||||
auto opcode = static_cast<FP_opcode>(instruction->opcode);
|
||||
if (operand >= get_operand_count(opcode))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
OPDEST d0 { .HEX = instruction->bytecode[0] };
|
||||
const u32 dst_write_mask = d0.no_dest ? 0 : d0.write_mask;
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case RSX_FP_OPCODE_NOP:
|
||||
return 0;
|
||||
case RSX_FP_OPCODE_MOV:
|
||||
case RSX_FP_OPCODE_MUL:
|
||||
case RSX_FP_OPCODE_ADD:
|
||||
case RSX_FP_OPCODE_MAD:
|
||||
return xyzw & dst_write_mask;
|
||||
case RSX_FP_OPCODE_DP3:
|
||||
return xyz;
|
||||
case RSX_FP_OPCODE_DP4:
|
||||
return xyzw;
|
||||
case RSX_FP_OPCODE_DST:
|
||||
return decode({ y | z, y | w });
|
||||
case RSX_FP_OPCODE_MIN:
|
||||
case RSX_FP_OPCODE_MAX:
|
||||
return xyzw & dst_write_mask;
|
||||
case RSX_FP_OPCODE_SLT:
|
||||
case RSX_FP_OPCODE_SGE:
|
||||
case RSX_FP_OPCODE_SLE:
|
||||
case RSX_FP_OPCODE_SGT:
|
||||
case RSX_FP_OPCODE_SNE:
|
||||
case RSX_FP_OPCODE_SEQ:
|
||||
return xyzw & dst_write_mask;
|
||||
case RSX_FP_OPCODE_FRC:
|
||||
case RSX_FP_OPCODE_FLR:
|
||||
return xyzw & dst_write_mask;
|
||||
case RSX_FP_OPCODE_KIL:
|
||||
return 0;
|
||||
case RSX_FP_OPCODE_PK4:
|
||||
return xyzw;
|
||||
case RSX_FP_OPCODE_UP4:
|
||||
return x;
|
||||
case RSX_FP_OPCODE_DDX:
|
||||
case RSX_FP_OPCODE_DDY:
|
||||
return xyzw & dst_write_mask;
|
||||
case RSX_FP_OPCODE_TEX:
|
||||
case RSX_FP_OPCODE_TXD:
|
||||
switch (prog.get_texture_dimension(d0.tex_num))
|
||||
{
|
||||
case rsx::texture_dimension_extended::texture_dimension_1d:
|
||||
return x;
|
||||
case rsx::texture_dimension_extended::texture_dimension_2d:
|
||||
return xy;
|
||||
case rsx::texture_dimension_extended::texture_dimension_3d:
|
||||
case rsx::texture_dimension_extended::texture_dimension_cubemap:
|
||||
return xyz;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case RSX_FP_OPCODE_TXP:
|
||||
switch (prog.get_texture_dimension(d0.tex_num))
|
||||
{
|
||||
case rsx::texture_dimension_extended::texture_dimension_1d:
|
||||
return xy;
|
||||
case rsx::texture_dimension_extended::texture_dimension_2d:
|
||||
return xyz;
|
||||
case rsx::texture_dimension_extended::texture_dimension_3d:
|
||||
case rsx::texture_dimension_extended::texture_dimension_cubemap:
|
||||
return xyzw;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case RSX_FP_OPCODE_RCP:
|
||||
case RSX_FP_OPCODE_RSQ:
|
||||
case RSX_FP_OPCODE_EX2:
|
||||
case RSX_FP_OPCODE_LG2:
|
||||
return x;
|
||||
case RSX_FP_OPCODE_LIT:
|
||||
return xyzw;
|
||||
case RSX_FP_OPCODE_LRP:
|
||||
return xyzw & dst_write_mask;
|
||||
case RSX_FP_OPCODE_STR:
|
||||
case RSX_FP_OPCODE_SFL:
|
||||
return xyzw & dst_write_mask;
|
||||
case RSX_FP_OPCODE_COS:
|
||||
case RSX_FP_OPCODE_SIN:
|
||||
return x;
|
||||
case RSX_FP_OPCODE_PK2:
|
||||
return xy;
|
||||
case RSX_FP_OPCODE_UP2:
|
||||
return x;
|
||||
case RSX_FP_OPCODE_PKB:
|
||||
return xyzw;
|
||||
case RSX_FP_OPCODE_UPB:
|
||||
return x;
|
||||
case RSX_FP_OPCODE_PK16:
|
||||
return xy;
|
||||
case RSX_FP_OPCODE_UP16:
|
||||
return x;
|
||||
case RSX_FP_OPCODE_PKG:
|
||||
return xyzw;
|
||||
case RSX_FP_OPCODE_UPG:
|
||||
return x;
|
||||
case RSX_FP_OPCODE_DP2A:
|
||||
return decode({ xy, xy, x });
|
||||
case RSX_FP_OPCODE_TXL:
|
||||
case RSX_FP_OPCODE_TXB:
|
||||
return decode({ xy, x });
|
||||
case RSX_FP_OPCODE_REFL:
|
||||
return xyzw;
|
||||
case RSX_FP_OPCODE_DP2:
|
||||
return xy;
|
||||
case RSX_FP_OPCODE_NRM:
|
||||
return xyz;
|
||||
case RSX_FP_OPCODE_DIV:
|
||||
case RSX_FP_OPCODE_DIVSQ:
|
||||
return decode({ xyzw, x }) & dst_write_mask;
|
||||
case RSX_FP_OPCODE_LIF:
|
||||
return decode({ y | w });
|
||||
case RSX_FP_OPCODE_FENCT:
|
||||
case RSX_FP_OPCODE_FENCB:
|
||||
case RSX_FP_OPCODE_BRK:
|
||||
case RSX_FP_OPCODE_CAL:
|
||||
case RSX_FP_OPCODE_IFE:
|
||||
case RSX_FP_OPCODE_LOOP:
|
||||
case RSX_FP_OPCODE_REP:
|
||||
case RSX_FP_OPCODE_RET:
|
||||
// Flow control. Special registers are provided for these outside the common file
|
||||
return 0;
|
||||
|
||||
case RSX_FP_OPCODE_POW:
|
||||
fmt::throw_exception("Unimplemented POW instruction."); // Unused ??
|
||||
case RSX_FP_OPCODE_BEM:
|
||||
case RSX_FP_OPCODE_TEXBEM:
|
||||
case RSX_FP_OPCODE_TXPBEM:
|
||||
case RSX_FP_OPCODE_BEMLUM:
|
||||
fmt::throw_exception("Unimplemented BEM class instruction"); // Unused
|
||||
case RSX_FP_OPCODE_TIMESWTEX:
|
||||
fmt::throw_exception("Unimplemented TIMESWTEX instruction"); // Unused
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Resolved vector lane mask with swizzles applied.
|
||||
u32 get_src_vector_lane_mask_shuffled(const RSXFragmentProgram& prog, const Instruction* instruction, u32 operand)
|
||||
{
|
||||
// Brute-force this. There's only 16 permutations.
|
||||
constexpr u32 x = 0b0001;
|
||||
constexpr u32 y = 0b0010;
|
||||
constexpr u32 z = 0b0100;
|
||||
constexpr u32 w = 0b1000;
|
||||
|
||||
const u32 lane_mask = get_src_vector_lane_mask(prog, instruction, operand);
|
||||
if (!lane_mask)
|
||||
{
|
||||
return lane_mask;
|
||||
}
|
||||
|
||||
// Now we resolve matching lanes.
|
||||
// This sequence can be drastically sped up using lookup tables but that will come later.
|
||||
std::unordered_set<u32> inputs;
|
||||
SRC_Common src { .HEX = instruction->bytecode[operand + 1] };
|
||||
|
||||
if (src.reg_type != RSX_FP_REGISTER_TYPE_TEMP)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lane_mask & x) inputs.insert(src.swizzle_x);
|
||||
if (lane_mask & y) inputs.insert(src.swizzle_y);
|
||||
if (lane_mask & z) inputs.insert(src.swizzle_z);
|
||||
if (lane_mask & w) inputs.insert(src.swizzle_w);
|
||||
|
||||
u32 result = 0;
|
||||
if (inputs.contains(0)) result |= x;
|
||||
if (inputs.contains(1)) result |= y;
|
||||
if (inputs.contains(2)) result |= z;
|
||||
if (inputs.contains(3)) result |= w;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_delay_slot(const Instruction* instruction)
|
||||
{
|
||||
OPDEST dst { .HEX = instruction->bytecode[0] };
|
||||
SRC0 src0 { .HEX = instruction->bytecode[1] };
|
||||
SRC1 src1{ .HEX = instruction->bytecode[2] };
|
||||
|
||||
if (dst.opcode != RSX_FP_OPCODE_MOV || // These slots are always populated with MOV
|
||||
dst.no_dest || // Must have a sink
|
||||
src0.reg_type != RSX_FP_REGISTER_TYPE_TEMP || // Must read from reg
|
||||
dst.dest_reg != src0.tmp_reg_index || // Must be a write-to-self
|
||||
dst.fp16 || // Always full lane. We need to collect more data on this but it won't matter
|
||||
dst.saturate || // Precision modifier
|
||||
(dst.prec != RSX_FP_PRECISION_REAL &&
|
||||
dst.prec != RSX_FP_PRECISION_UNKNOWN)) // Cannot have precision modifiers
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we have precision modifiers on the source
|
||||
if (src0.abs || src0.neg || src1.scale)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dst.mask_x && src0.swizzle_x != 0) return false;
|
||||
if (dst.mask_y && src0.swizzle_y != 1) return false;
|
||||
if (dst.mask_z && src0.swizzle_z != 2) return false;
|
||||
if (dst.mask_w && src0.swizzle_w != 3) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RegisterRef get_src_register(const RSXFragmentProgram& prog, const Instruction* instruction, u32 operand)
|
||||
{
|
||||
SRC_Common src{ .HEX = instruction->bytecode[operand + 1] };
|
||||
if (src.reg_type != RSX_FP_REGISTER_TYPE_TEMP)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const u32 read_lanes = get_src_vector_lane_mask_shuffled(prog, instruction, operand);
|
||||
if (!read_lanes)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
RegisterRef ref{ .mask = read_lanes };
|
||||
Register& reg = ref.reg;
|
||||
|
||||
reg.f16 = !!src.fp16;
|
||||
reg.id = src.tmp_reg_index;
|
||||
return ref;
|
||||
}
|
||||
|
||||
RegisterRef get_dst_register(const Instruction* instruction)
|
||||
{
|
||||
OPDEST dst { .HEX = instruction->bytecode[0] };
|
||||
if (dst.no_dest)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
RegisterRef ref{ .mask = dst.write_mask };
|
||||
ref.reg.f16 = dst.fp16;
|
||||
ref.reg.id = dst.dest_reg;
|
||||
return ref;
|
||||
}
|
||||
|
||||
// Convert vector mask to file range
|
||||
rsx::simple_array<u32> get_register_file_range(const RegisterRef& reg)
|
||||
{
|
||||
if (!reg.mask)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const u32 lane_width = reg.reg.f16 ? 2 : 4;
|
||||
const u32 file_offset = reg.reg.id * lane_width * 4;
|
||||
|
||||
ensure(file_offset < constants::register_file_max_len, "Invalid register index");
|
||||
|
||||
rsx::simple_array<u32> result{};
|
||||
auto insert_lane = [&](u32 word_offset)
|
||||
{
|
||||
for (u32 i = 0; i < lane_width; ++i)
|
||||
{
|
||||
result.push_back(file_offset + (word_offset * lane_width) + i);
|
||||
}
|
||||
};
|
||||
|
||||
if (reg.x) insert_lane(0);
|
||||
if (reg.y) insert_lane(1);
|
||||
if (reg.z) insert_lane(2);
|
||||
if (reg.w) insert_lane(3);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
126
rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.h
Normal file
126
rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.h
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
#pragma once
|
||||
|
||||
#include "IR.h"
|
||||
#include "Emu/RSX/Common/simple_array.hpp"
|
||||
|
||||
struct RSXFragmentProgram;
|
||||
|
||||
namespace rsx::assembler
|
||||
{
|
||||
enum FP_opcode
|
||||
{
|
||||
RSX_FP_OPCODE_NOP = 0x00, // No-Operation
|
||||
RSX_FP_OPCODE_MOV = 0x01, // Move
|
||||
RSX_FP_OPCODE_MUL = 0x02, // Multiply
|
||||
RSX_FP_OPCODE_ADD = 0x03, // Add
|
||||
RSX_FP_OPCODE_MAD = 0x04, // Multiply-Add
|
||||
RSX_FP_OPCODE_DP3 = 0x05, // 3-component Dot Product
|
||||
RSX_FP_OPCODE_DP4 = 0x06, // 4-component Dot Product
|
||||
RSX_FP_OPCODE_DST = 0x07, // Distance
|
||||
RSX_FP_OPCODE_MIN = 0x08, // Minimum
|
||||
RSX_FP_OPCODE_MAX = 0x09, // Maximum
|
||||
RSX_FP_OPCODE_SLT = 0x0A, // Set-If-LessThan
|
||||
RSX_FP_OPCODE_SGE = 0x0B, // Set-If-GreaterEqual
|
||||
RSX_FP_OPCODE_SLE = 0x0C, // Set-If-LessEqual
|
||||
RSX_FP_OPCODE_SGT = 0x0D, // Set-If-GreaterThan
|
||||
RSX_FP_OPCODE_SNE = 0x0E, // Set-If-NotEqual
|
||||
RSX_FP_OPCODE_SEQ = 0x0F, // Set-If-Equal
|
||||
RSX_FP_OPCODE_FRC = 0x10, // Fraction (fract)
|
||||
RSX_FP_OPCODE_FLR = 0x11, // Floor
|
||||
RSX_FP_OPCODE_KIL = 0x12, // Kill fragment
|
||||
RSX_FP_OPCODE_PK4 = 0x13, // Pack four signed 8-bit values
|
||||
RSX_FP_OPCODE_UP4 = 0x14, // Unpack four signed 8-bit values
|
||||
RSX_FP_OPCODE_DDX = 0x15, // Partial-derivative in x (Screen space derivative w.r.t. x)
|
||||
RSX_FP_OPCODE_DDY = 0x16, // Partial-derivative in y (Screen space derivative w.r.t. y)
|
||||
RSX_FP_OPCODE_TEX = 0x17, // Texture lookup
|
||||
RSX_FP_OPCODE_TXP = 0x18, // Texture sample with projection (Projective texture lookup)
|
||||
RSX_FP_OPCODE_TXD = 0x19, // Texture sample with partial differentiation (Texture lookup with derivatives)
|
||||
RSX_FP_OPCODE_RCP = 0x1A, // Reciprocal
|
||||
RSX_FP_OPCODE_RSQ = 0x1B, // Reciprocal Square Root
|
||||
RSX_FP_OPCODE_EX2 = 0x1C, // Exponentiation base 2
|
||||
RSX_FP_OPCODE_LG2 = 0x1D, // Log base 2
|
||||
RSX_FP_OPCODE_LIT = 0x1E, // Lighting coefficients
|
||||
RSX_FP_OPCODE_LRP = 0x1F, // Linear Interpolation
|
||||
RSX_FP_OPCODE_STR = 0x20, // Set-If-True
|
||||
RSX_FP_OPCODE_SFL = 0x21, // Set-If-False
|
||||
RSX_FP_OPCODE_COS = 0x22, // Cosine
|
||||
RSX_FP_OPCODE_SIN = 0x23, // Sine
|
||||
RSX_FP_OPCODE_PK2 = 0x24, // Pack two 16-bit floats
|
||||
RSX_FP_OPCODE_UP2 = 0x25, // Unpack two 16-bit floats
|
||||
RSX_FP_OPCODE_POW = 0x26, // Power
|
||||
RSX_FP_OPCODE_PKB = 0x27, // Pack bytes
|
||||
RSX_FP_OPCODE_UPB = 0x28, // Unpack bytes
|
||||
RSX_FP_OPCODE_PK16 = 0x29, // Pack 16 bits
|
||||
RSX_FP_OPCODE_UP16 = 0x2A, // Unpack 16
|
||||
RSX_FP_OPCODE_BEM = 0x2B, // Bump-environment map (a.k.a. 2D coordinate transform)
|
||||
RSX_FP_OPCODE_PKG = 0x2C, // Pack with sRGB transformation
|
||||
RSX_FP_OPCODE_UPG = 0x2D, // Unpack gamma
|
||||
RSX_FP_OPCODE_DP2A = 0x2E, // 2-component dot product with scalar addition
|
||||
RSX_FP_OPCODE_TXL = 0x2F, // Texture sample with explicit LOD
|
||||
RSX_FP_OPCODE_TXB = 0x31, // Texture sample with bias
|
||||
RSX_FP_OPCODE_TEXBEM = 0x33,
|
||||
RSX_FP_OPCODE_TXPBEM = 0x34,
|
||||
RSX_FP_OPCODE_BEMLUM = 0x35,
|
||||
RSX_FP_OPCODE_REFL = 0x36, // Reflection vector
|
||||
RSX_FP_OPCODE_TIMESWTEX = 0x37,
|
||||
RSX_FP_OPCODE_DP2 = 0x38, // 2-component dot product
|
||||
RSX_FP_OPCODE_NRM = 0x39, // Normalize
|
||||
RSX_FP_OPCODE_DIV = 0x3A, // Division
|
||||
RSX_FP_OPCODE_DIVSQ = 0x3B, // Divide by Square Root
|
||||
RSX_FP_OPCODE_LIF = 0x3C, // Final part of LIT
|
||||
RSX_FP_OPCODE_FENCT = 0x3D, // Fence T?
|
||||
RSX_FP_OPCODE_FENCB = 0x3E, // Fence B?
|
||||
RSX_FP_OPCODE_BRK = 0x40, // Break
|
||||
RSX_FP_OPCODE_CAL = 0x41, // Subroutine call
|
||||
RSX_FP_OPCODE_IFE = 0x42, // If
|
||||
RSX_FP_OPCODE_LOOP = 0x43, // Loop
|
||||
RSX_FP_OPCODE_REP = 0x44, // Repeat
|
||||
RSX_FP_OPCODE_RET = 0x45, // Return
|
||||
|
||||
|
||||
// Custom opcodes for dependency injection
|
||||
RSX_FP_OPCODE_OR16_LO = 0x46, // Performs a 16-bit OR, taking one register channel as input and overwriting low 16 bits of the output
|
||||
RSX_FP_OPCODE_OR16_HI = 0x47, // Same as the lo variant but now overwrites the high 16-bit block
|
||||
};
|
||||
|
||||
namespace FP
|
||||
{
|
||||
namespace constants
|
||||
{
|
||||
// The ISA can encode for 48 registers of any width.
|
||||
// This allows to encode R0-R64 and H0-H95, though there aren't enough addressing bits for the latter.
|
||||
constexpr u32 register_file_max_len = 48 * 16;
|
||||
|
||||
// Enums for analysis passes.
|
||||
constexpr char content_unknown = 0;
|
||||
constexpr char content_float32 = 'R';
|
||||
constexpr char content_float16 = 'H';
|
||||
constexpr char content_dual = 'D';
|
||||
}
|
||||
|
||||
using register_file_t = std::array<char, constants::register_file_max_len>;
|
||||
|
||||
// Returns number of operands consumed by an instruction
|
||||
u8 get_operand_count(FP_opcode opcode);
|
||||
|
||||
// Returns a lane mask for the given operand.
|
||||
// The lane mask is the fixed function hardware lane so swizzles need to be applied on top to resolve the real data channel.
|
||||
u32 get_src_vector_lane_mask(const RSXFragmentProgram& prog, const Instruction* instruction, u32 operand);
|
||||
|
||||
// Resolved vector lane mask with swizzles applied.
|
||||
u32 get_src_vector_lane_mask_shuffled(const RSXFragmentProgram& prog, const Instruction* instruction, u32 operand);
|
||||
|
||||
// Returns true on delay slot instructions.
|
||||
bool is_delay_slot(const Instruction* instruction);
|
||||
|
||||
// Generate register references
|
||||
RegisterRef get_src_register(const RSXFragmentProgram& prog, const Instruction* instruction, u32 operand);
|
||||
RegisterRef get_dst_register(const Instruction* instruction);
|
||||
|
||||
// Convert vector mask to file ranges
|
||||
rsx::simple_array<u32> get_register_file_range(const RegisterRef& reg);
|
||||
|
||||
// Compile a register file annotated blob to register references
|
||||
std::vector<RegisterRef> compile_register_file(const std::array<char, 48 * 8>& file);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "CFG.h"
|
||||
|
||||
#include "Emu/RSX/Common/simple_array.hpp"
|
||||
|
|
@ -75,8 +74,19 @@ namespace rsx::assembler
|
|||
{
|
||||
if (auto found = find_block_for_pc(id))
|
||||
{
|
||||
parent->insert_succ(found, edge_type);
|
||||
found->insert_pred(parent, edge_type);
|
||||
auto succ = found;
|
||||
if (found->is_of_type(EdgeType::ELSE) &&
|
||||
(edge_type == EdgeType::ENDIF || edge_type == EdgeType::ENDLOOP))
|
||||
{
|
||||
// If we landed on an "ELSE" node, link to its "ENDIF" counterpart
|
||||
auto if_parent = found->pred.front().from;
|
||||
auto endif_edge = std::find_if(if_parent->succ.begin(), if_parent->succ.end(), FN(x.type == EdgeType::ENDIF));
|
||||
ensure(endif_edge != if_parent->succ.end(), "CFG: Invalid ELSE node");
|
||||
succ = endif_edge->to;
|
||||
}
|
||||
|
||||
parent->insert_succ(succ, edge_type);
|
||||
succ->insert_pred(parent, edge_type);
|
||||
return found;
|
||||
}
|
||||
|
||||
|
|
@ -101,6 +111,43 @@ namespace rsx::assembler
|
|||
|
||||
if (found)
|
||||
{
|
||||
auto front_edge = std::find_if(bb->pred.begin(), bb->pred.end(), FN(x.type != EdgeType::ENDIF && x.type != EdgeType::ENDLOOP));
|
||||
if (front_edge != bb->pred.end())
|
||||
{
|
||||
auto parent = ensure(front_edge->from);
|
||||
switch (front_edge->type)
|
||||
{
|
||||
case EdgeType::IF:
|
||||
case EdgeType::ELSE:
|
||||
{
|
||||
// Find the merge node from the parent.
|
||||
auto succ = std::find_if(parent->succ.begin(), parent->succ.end(), FN(x.type == EdgeType::ENDIF));
|
||||
ensure(succ != parent->succ.end(), "CFG: Broken IF linkage. Please report to developers.");
|
||||
bb->insert_succ(succ->to, EdgeType::ENDIF);
|
||||
succ->to->insert_pred(bb, EdgeType::ENDIF);
|
||||
break;
|
||||
}
|
||||
case EdgeType::LOOP:
|
||||
{
|
||||
// Find the merge node from the parent
|
||||
auto succ = std::find_if(parent->succ.begin(), parent->succ.end(), FN(x.type == EdgeType::ENDLOOP));
|
||||
ensure(succ != parent->succ.end(), "CFG: Broken LOOP linkage. Please report to developers.");
|
||||
bb->insert_succ(succ->to, EdgeType::ENDLOOP);
|
||||
succ->to->insert_pred(bb, EdgeType::ENDLOOP);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Missing an edge type?
|
||||
rsx_log.error("CFG: Unexpected block exit. Report to developers.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (bb->pred.empty())
|
||||
{
|
||||
// Impossible situation.
|
||||
rsx_log.error("CFG: Child block has no parent but has successor! Report to developers.");
|
||||
}
|
||||
|
||||
bb = *found;
|
||||
}
|
||||
|
||||
|
|
@ -113,7 +160,7 @@ namespace rsx::assembler
|
|||
src2.HEX = decoded._u32[3];
|
||||
|
||||
end = !!dst.end;
|
||||
const u32 opcode = dst.opcode | (src1.opcode_is_branch << 6);
|
||||
const u32 opcode = dst.opcode | (src1.opcode_hi << 6);
|
||||
|
||||
if (opcode == RSX_FP_OPCODE_NOP)
|
||||
{
|
||||
|
|
@ -126,6 +173,7 @@ namespace rsx::assembler
|
|||
std::memcpy(ir_inst.bytecode, &decoded._u32[0], 16);
|
||||
ir_inst.length = 4;
|
||||
ir_inst.addr = pc * 16;
|
||||
ir_inst.opcode = opcode;
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
|
|
@ -146,22 +194,45 @@ namespace rsx::assembler
|
|||
case RSX_FP_OPCODE_IFE:
|
||||
{
|
||||
// Inserts if and else and end blocks
|
||||
auto parent = bb;
|
||||
bb = safe_insert_block(parent, pc + 1, EdgeType::IF);
|
||||
if (src2.end_offset != src1.else_offset)
|
||||
const u32 end_addr = src2.end_offset >> 2u;
|
||||
const u32 else_addr = src1.else_offset >> 2u;
|
||||
if (end_addr == pc + 1u)
|
||||
{
|
||||
else_blocks.push_back(safe_insert_block(parent, src1.else_offset >> 2, EdgeType::ELSE));
|
||||
// NOP. Empty IF block
|
||||
bb->instructions.pop_back();
|
||||
break;
|
||||
}
|
||||
end_blocks.push_back(safe_insert_block(parent, src2.end_offset >> 2, EdgeType::ENDIF));
|
||||
|
||||
if (else_addr > end_addr)
|
||||
{
|
||||
// Our systems support this, but it is not verified on real hardware.
|
||||
rsx_log.error("CFG: Non-contiguous branch detected. Report to developers.");
|
||||
}
|
||||
|
||||
auto parent = bb;
|
||||
bb = safe_insert_block(parent, pc + 1u, EdgeType::IF);
|
||||
if (end_addr != else_addr)
|
||||
{
|
||||
else_blocks.push_back(safe_insert_block(parent, else_addr, EdgeType::ELSE));
|
||||
}
|
||||
end_blocks.push_back(safe_insert_block(parent, end_addr, EdgeType::ENDIF));
|
||||
break;
|
||||
}
|
||||
case RSX_FP_OPCODE_LOOP:
|
||||
case RSX_FP_OPCODE_REP:
|
||||
{
|
||||
// Inserts for and end blocks
|
||||
const u32 end_addr = src2.end_offset >> 2u;
|
||||
if (end_addr == pc + 1u)
|
||||
{
|
||||
// NOP. Empty LOOP block
|
||||
bb->instructions.pop_back();
|
||||
break;
|
||||
}
|
||||
|
||||
auto parent = bb;
|
||||
bb = safe_insert_block(parent, pc + 1, EdgeType::LOOP);
|
||||
end_blocks.push_back(safe_insert_block(parent, src2.end_offset >> 2, EdgeType::ENDLOOP));
|
||||
bb = safe_insert_block(parent, pc + 1u, EdgeType::LOOP);
|
||||
end_blocks.push_back(safe_insert_block(parent, end_addr, EdgeType::ENDLOOP));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
@ -174,6 +245,7 @@ namespace rsx::assembler
|
|||
ir_inst.length += 4;
|
||||
pc++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
pc++;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,16 @@ namespace rsx::assembler
|
|||
{
|
||||
int id = 0;
|
||||
bool f16 = false;
|
||||
|
||||
bool operator == (const Register& other) const
|
||||
{
|
||||
return id == other.id && f16 == other.f16;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return std::string(f16 ? "H" : "R") + std::to_string(id);
|
||||
}
|
||||
};
|
||||
|
||||
struct RegisterRef
|
||||
|
|
@ -19,7 +29,7 @@ namespace rsx::assembler
|
|||
// Vector information
|
||||
union
|
||||
{
|
||||
u32 mask;
|
||||
u32 mask = 0;
|
||||
|
||||
struct
|
||||
{
|
||||
|
|
@ -29,6 +39,16 @@ namespace rsx::assembler
|
|||
bool w : 1;
|
||||
};
|
||||
};
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return !!mask;
|
||||
}
|
||||
|
||||
bool operator == (const RegisterRef& other) const
|
||||
{
|
||||
return reg == other.reg && mask == other.mask;
|
||||
}
|
||||
};
|
||||
|
||||
struct Instruction
|
||||
|
|
@ -71,6 +91,7 @@ namespace rsx::assembler
|
|||
struct BasicBlock
|
||||
{
|
||||
u32 id = 0;
|
||||
|
||||
std::vector<Instruction> instructions; // Program instructions for the RSX processor
|
||||
std::vector<FlowEdge> succ; // Forward edges. Sorted closest first.
|
||||
std::vector<FlowEdge> pred; // Back edges. Sorted closest first.
|
||||
|
|
@ -78,6 +99,9 @@ namespace rsx::assembler
|
|||
std::vector<Instruction> prologue; // Prologue, created by passes
|
||||
std::vector<Instruction> epilogue; // Epilogue, created by passes
|
||||
|
||||
std::vector<RegisterRef> input_list; // Register inputs.
|
||||
std::vector<RegisterRef> clobber_list; // Clobbered outputs
|
||||
|
||||
FlowEdge* insert_succ(BasicBlock* b, EdgeType type = EdgeType::NONE)
|
||||
{
|
||||
FlowEdge e{ .type = type, .from = this, .to = b };
|
||||
|
|
@ -91,5 +115,25 @@ namespace rsx::assembler
|
|||
pred.push_back(e);
|
||||
return &pred.back();
|
||||
}
|
||||
|
||||
bool is_of_type(EdgeType type) const
|
||||
{
|
||||
return pred.size() == 1 &&
|
||||
pred.front().type == type;
|
||||
}
|
||||
|
||||
bool has_sibling_of_type(EdgeType type) const
|
||||
{
|
||||
if (pred.size() != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto source_node = pred.front().from;
|
||||
return std::find_if(
|
||||
source_node->succ.begin(),
|
||||
source_node->succ.end(),
|
||||
FN(x.type == type)) != source_node->succ.end();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,226 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "RegisterAnnotationPass.h"
|
||||
#include "Emu/RSX/Program/Assembler/FPOpcodes.h"
|
||||
#include "Emu/RSX/Program/RSXFragmentProgram.h"
|
||||
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace rsx::assembler::FP
|
||||
{
|
||||
using namespace constants;
|
||||
|
||||
bool is_delay_slot(const Instruction& instruction)
|
||||
{
|
||||
const OPDEST dst{ .HEX = instruction.bytecode[0] };
|
||||
const SRC0 src0{ .HEX = instruction.bytecode[1] };
|
||||
const SRC1 src1{ .HEX = instruction.bytecode[2] };
|
||||
|
||||
if (dst.opcode != RSX_FP_OPCODE_MOV || // These slots are always populated with MOV
|
||||
dst.no_dest || // Must have a sink
|
||||
src0.reg_type != RSX_FP_REGISTER_TYPE_TEMP || // Must read from reg
|
||||
dst.dest_reg != src0.tmp_reg_index || // Must be a write-to-self
|
||||
dst.fp16 != src0.fp16 || // Must really be the same register
|
||||
src0.abs || src0.neg ||
|
||||
dst.saturate) // Precision modifier
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (dst.prec)
|
||||
{
|
||||
case RSX_FP_PRECISION_REAL:
|
||||
case RSX_FP_PRECISION_UNKNOWN:
|
||||
break;
|
||||
case RSX_FP_PRECISION_HALF:
|
||||
if (!src0.fp16) return false;
|
||||
break;
|
||||
case RSX_FP_PRECISION_FIXED12:
|
||||
case RSX_FP_PRECISION_FIXED9:
|
||||
case RSX_FP_PRECISION_SATURATE:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we have precision modifiers on the source
|
||||
if (src0.abs || src0.neg || src1.scale)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dst.mask_x && src0.swizzle_x != 0) return false;
|
||||
if (dst.mask_y && src0.swizzle_y != 1) return false;
|
||||
if (dst.mask_z && src0.swizzle_z != 2) return false;
|
||||
if (dst.mask_w && src0.swizzle_w != 3) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<RegisterRef> compile_register_file(const register_file_t& file)
|
||||
{
|
||||
std::vector<RegisterRef> results;
|
||||
|
||||
// F16 register processing
|
||||
for (int reg16 = 0; reg16 < 48; ++reg16)
|
||||
{
|
||||
const u32 offset = reg16 * 8;
|
||||
auto word = *reinterpret_cast<const u64*>(&file[offset]);
|
||||
|
||||
if (!word) [[ likely ]]
|
||||
{
|
||||
// Trivial rejection, very commonly hit.
|
||||
continue;
|
||||
}
|
||||
|
||||
RegisterRef ref{ .reg {.id = reg16, .f16 = true } };
|
||||
ref.x = (file[offset] == content_dual || file[offset] == content_float16);
|
||||
ref.y = (file[offset + 2] == content_dual || file[offset + 2] == content_float16);
|
||||
ref.z = (file[offset + 4] == content_dual || file[offset + 4] == content_float16);
|
||||
ref.w = (file[offset + 6] == content_dual || file[offset + 6] == content_float16);
|
||||
|
||||
if (ref)
|
||||
{
|
||||
results.push_back(std::move(ref));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to check a span for 32-bit access
|
||||
auto match_any_32 = [](const std::span<const char> lanes)
|
||||
{
|
||||
return std::any_of(lanes.begin(), lanes.end(), FN(x == content_dual || x == content_float32));
|
||||
};
|
||||
|
||||
// F32 register processing
|
||||
for (int reg32 = 0; reg32 < 24; ++reg32)
|
||||
{
|
||||
const u32 offset = reg32 * 16;
|
||||
auto word0 = *reinterpret_cast<const u64*>(&file[offset]);
|
||||
auto word1 = *reinterpret_cast<const u64*>(&file[offset + 8]);
|
||||
|
||||
if (!word0 && !word1) [[ likely ]]
|
||||
{
|
||||
// Trivial rejection, very commonly hit.
|
||||
continue;
|
||||
}
|
||||
|
||||
RegisterRef ref{ .reg {.id = reg32, .f16 = false } };
|
||||
if (word0)
|
||||
{
|
||||
ref.x = match_any_32({ &file[offset], 4 });
|
||||
ref.y = match_any_32({ &file[offset + 4], 4 });
|
||||
}
|
||||
|
||||
if (word1)
|
||||
{
|
||||
ref.z = match_any_32({ &file[offset + 8], 4 });
|
||||
ref.w = match_any_32({ &file[offset + 12], 4 });
|
||||
}
|
||||
|
||||
if (ref)
|
||||
{
|
||||
results.push_back(std::move(ref));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Decay instructions into register references
|
||||
void annotate_instructions(BasicBlock* block, const RSXFragmentProgram& prog, bool skip_delay_slots)
|
||||
{
|
||||
for (auto& instruction : block->instructions)
|
||||
{
|
||||
if (skip_delay_slots && is_delay_slot(instruction))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 operand_count = get_operand_count(static_cast<FP_opcode>(instruction.opcode));
|
||||
for (u32 i = 0; i < operand_count; i++)
|
||||
{
|
||||
RegisterRef reg = get_src_register(prog, &instruction, i);
|
||||
if (!reg.mask)
|
||||
{
|
||||
// Likely a literal constant
|
||||
continue;
|
||||
}
|
||||
|
||||
instruction.srcs.push_back(std::move(reg));
|
||||
}
|
||||
|
||||
RegisterRef dst = get_dst_register(&instruction);
|
||||
if (dst)
|
||||
{
|
||||
instruction.dsts.push_back(std::move(dst));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Annotate each block with input and output lanes (read and clobber list)
|
||||
void annotate_block_io(BasicBlock* block)
|
||||
{
|
||||
alignas(16) register_file_t output_register_file;
|
||||
alignas(16) register_file_t input_register_file; // We'll eventually replace with a bitfield mask, but for ease of debugging, we use char for now
|
||||
|
||||
std::memset(output_register_file.data(), content_unknown, register_file_max_len);
|
||||
std::memset(input_register_file.data(), content_unknown, register_file_max_len);
|
||||
|
||||
for (const auto& instruction : block->instructions)
|
||||
{
|
||||
for (const auto& src : instruction.srcs)
|
||||
{
|
||||
const auto read_bytes = get_register_file_range(src);
|
||||
const char expected_type = src.reg.f16 ? content_float16 : content_float32;
|
||||
for (const auto& index : read_bytes)
|
||||
{
|
||||
if (output_register_file[index] != content_unknown)
|
||||
{
|
||||
// Something already wrote to this lane
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input_register_file[index] == expected_type)
|
||||
{
|
||||
// We already know about this input
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input_register_file[index] == 0)
|
||||
{
|
||||
// Not known, tag as input
|
||||
input_register_file[index] = expected_type;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collision on the lane
|
||||
input_register_file[index] = content_dual;
|
||||
}
|
||||
}
|
||||
|
||||
if (!instruction.dsts.empty())
|
||||
{
|
||||
const auto& dst = instruction.dsts.front();
|
||||
const auto write_bytes = get_register_file_range(dst);
|
||||
const char expected_type = dst.reg.f16 ? content_float16 : content_float32;
|
||||
|
||||
for (const auto& index : write_bytes)
|
||||
{
|
||||
output_register_file[index] = expected_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compile the input and output refs into register references
|
||||
block->clobber_list = compile_register_file(output_register_file);
|
||||
block->input_list = compile_register_file(input_register_file);
|
||||
}
|
||||
|
||||
void RegisterAnnotationPass::run(FlowGraph& graph)
|
||||
{
|
||||
for (auto& block : graph.blocks)
|
||||
{
|
||||
annotate_instructions(&block, m_prog, m_config.skip_delay_slots);
|
||||
annotate_block_io(&block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../CFG.h"
|
||||
|
||||
struct RSXFragmentProgram;
|
||||
|
||||
namespace rsx::assembler::FP
|
||||
{
|
||||
struct RegisterAnnotationPassOptions
|
||||
{
|
||||
bool skip_delay_slots = false; // When enabled, detect delay slots and ignore annotating them.
|
||||
};
|
||||
|
||||
// The annotation pass annotates each basic block with 2 pieces of information:
|
||||
// 1. The "input" register list for a block.
|
||||
// 2. The "output" register list for a block (clobber list).
|
||||
// The information can be used by other passes to set up prologue/epilogue on each block.
|
||||
// The pass also populates register reference members of each instruction, such as the input and output lanes.
|
||||
class RegisterAnnotationPass : public CFGPass
|
||||
{
|
||||
public:
|
||||
RegisterAnnotationPass(
|
||||
const RSXFragmentProgram& prog,
|
||||
const RegisterAnnotationPassOptions& options = {})
|
||||
: m_prog(prog), m_config(options)
|
||||
{}
|
||||
|
||||
void run(FlowGraph& graph) override;
|
||||
|
||||
private:
|
||||
const RSXFragmentProgram& m_prog;
|
||||
RegisterAnnotationPassOptions m_config;
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,484 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "RegisterDependencyPass.h"
|
||||
#include "Emu/RSX/Program/Assembler/FPOpcodes.h"
|
||||
#include "Emu/RSX/Program/RSXFragmentProgram.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace rsx::assembler::FP
|
||||
{
|
||||
using namespace constants;
|
||||
|
||||
struct DependencyPassContext
|
||||
{
|
||||
std::unordered_map<BasicBlock*, register_file_t> exec_register_map;
|
||||
std::unordered_map<BasicBlock*, register_file_t> sync_register_map;
|
||||
};
|
||||
|
||||
enum Register32BarrierFlags
|
||||
{
|
||||
NONE = 0,
|
||||
OR_WORD0 = 1,
|
||||
OR_WORD1 = 2,
|
||||
DEFAULT = OR_WORD0 | OR_WORD1
|
||||
};
|
||||
|
||||
struct RegisterBarrier32
|
||||
{
|
||||
RegisterRef ref;
|
||||
u32 flags[4];
|
||||
};
|
||||
|
||||
std::vector<RegisterRef> decode_lanes16(const std::unordered_set<u32>& lanes)
|
||||
{
|
||||
std::vector<RegisterRef> result;
|
||||
|
||||
for (u32 index = 0, file_offset = 0; index < 48; ++index, file_offset += 8)
|
||||
{
|
||||
// Each register has 4 16-bit lanes
|
||||
u32 mask = 0;
|
||||
if (lanes.contains(file_offset + 0)) mask |= (1u << 0);
|
||||
if (lanes.contains(file_offset + 2)) mask |= (1u << 1);
|
||||
if (lanes.contains(file_offset + 4)) mask |= (1u << 2);
|
||||
if (lanes.contains(file_offset + 6)) mask |= (1u << 3);
|
||||
|
||||
if (mask == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RegisterRef ref{ .reg{.id = static_cast<int>(index), .f16 = true } };
|
||||
ref.mask = mask;
|
||||
result.push_back(std::move(ref));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<RegisterBarrier32> decode_lanes32(const std::unordered_set<u32>& lanes)
|
||||
{
|
||||
std::vector<RegisterBarrier32> result;
|
||||
|
||||
for (u32 index = 0, file_offset = 0; index < 48; ++index, file_offset += 16)
|
||||
{
|
||||
// Each register has 8 16-bit lanes
|
||||
RegisterBarrier32 barrier{};
|
||||
auto& ref = barrier.ref;
|
||||
|
||||
for (u32 lane = 0; lane < 16; lane += 2)
|
||||
{
|
||||
if (!lanes.contains(file_offset + lane))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 ch = (lane / 4);
|
||||
const u32 flags = (lane & 3)
|
||||
? Register32BarrierFlags::OR_WORD1
|
||||
: Register32BarrierFlags::OR_WORD0;
|
||||
|
||||
ref.mask |= (1u << ch);
|
||||
barrier.flags[ch] |= flags;
|
||||
}
|
||||
|
||||
if (ref.mask == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ref.reg = {.id = static_cast<int>(index), .f16 = false };
|
||||
result.push_back(std::move(barrier));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Instruction> build_barrier32(const RegisterBarrier32& barrier)
|
||||
{
|
||||
// Upto 4 instructions are needed per 32-bit register
|
||||
// R0.x = packHalf2x16(H0.xy)
|
||||
// R0.y = packHalf2x16(H0.zw);
|
||||
// R0.z = packHalf2x16(H1.xy);
|
||||
// R0.w = packHalf2x16(H1.zw);
|
||||
|
||||
std::vector<Instruction> result;
|
||||
|
||||
for (u32 mask = barrier.ref.mask, ch = 0; mask > 0; mask >>= 1, ++ch)
|
||||
{
|
||||
if (!(mask & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& reg = barrier.ref.reg;
|
||||
const auto reg_id = reg.id;
|
||||
|
||||
Instruction instruction{};
|
||||
OPDEST dst{};
|
||||
dst.prec = RSX_FP_PRECISION_REAL;
|
||||
dst.fp16 = 0;
|
||||
dst.dest_reg = reg_id;
|
||||
dst.write_mask = (1u << ch);
|
||||
|
||||
const u32 src_reg_id = (ch / 2) + (reg_id * 2);
|
||||
const bool is_word0 = !(ch & 1); // Only even
|
||||
|
||||
SRC0 src0{};
|
||||
if (is_word0)
|
||||
{
|
||||
src0.swizzle_x = 0;
|
||||
src0.swizzle_y = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
src0.swizzle_x = 2;
|
||||
src0.swizzle_y = 3;
|
||||
}
|
||||
|
||||
src0.swizzle_z = 2;
|
||||
src0.swizzle_w = 3;
|
||||
src0.reg_type = RSX_FP_REGISTER_TYPE_TEMP;
|
||||
src0.tmp_reg_index = src_reg_id;
|
||||
src0.fp16 = 1;
|
||||
|
||||
// Prepare source 1 to match the output in case we need to encode an OR
|
||||
SRC1 src1{};
|
||||
src1.reg_type = RSX_FP_REGISTER_TYPE_TEMP;
|
||||
src1.tmp_reg_index = reg_id;
|
||||
src1.swizzle_x = ch;
|
||||
src1.swizzle_y = ch;
|
||||
src1.swizzle_z = ch;
|
||||
src1.swizzle_w = ch;
|
||||
|
||||
u32 opcode = 0;
|
||||
switch (barrier.flags[ch])
|
||||
{
|
||||
case Register32BarrierFlags::DEFAULT:
|
||||
opcode = RSX_FP_OPCODE_PK2;
|
||||
break;
|
||||
case Register32BarrierFlags::OR_WORD0:
|
||||
opcode = RSX_FP_OPCODE_OR16_LO;
|
||||
// Swap inputs
|
||||
std::swap(src0.HEX, src1.HEX);
|
||||
break;
|
||||
case Register32BarrierFlags::OR_WORD1:
|
||||
opcode = RSX_FP_OPCODE_OR16_HI;
|
||||
src0.swizzle_x = src0.swizzle_y;
|
||||
std::swap(src0.HEX, src1.HEX);
|
||||
break;
|
||||
case Register32BarrierFlags::NONE:
|
||||
default:
|
||||
fmt::throw_exception("Unexpected lane barrier with no mask.");
|
||||
}
|
||||
|
||||
dst.opcode = opcode & 0x3F;
|
||||
src1.opcode_hi = (opcode > 0x3F) ? 1 : 0;
|
||||
src0.exec_if_eq = src0.exec_if_gr = src0.exec_if_lt = 1;
|
||||
|
||||
instruction.opcode = opcode;
|
||||
instruction.bytecode[0] = dst.HEX;
|
||||
instruction.bytecode[1] = src0.HEX;
|
||||
instruction.bytecode[2] = src1.HEX;
|
||||
|
||||
Register src_reg{ .id = static_cast<int>(src_reg_id), .f16 = true };
|
||||
instruction.srcs.push_back({ .reg = src_reg, .mask = 0xF });
|
||||
instruction.dsts.push_back({ .reg{ .id = reg_id, .f16 = false }, .mask = (1u << ch) });
|
||||
result.push_back(std::move(instruction));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Instruction> build_barrier16(const RegisterRef& reg)
|
||||
{
|
||||
// H0.xy = unpackHalf2x16(R0.x)
|
||||
// H0.zw = unpackHalf2x16(R0.y)
|
||||
// H1.xy = unpackHalf2x16(R0.z)
|
||||
// H1.zw = unpackHalf2x16(R0.w)
|
||||
|
||||
std::vector<Instruction> result;
|
||||
|
||||
for (u32 mask = reg.mask, ch = 0; mask > 0; mask >>= 1, ++ch)
|
||||
{
|
||||
if (!(mask & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Instruction instruction{};
|
||||
OPDEST dst{};
|
||||
dst.opcode = RSX_FP_OPCODE_UP2;
|
||||
dst.prec = RSX_FP_PRECISION_HALF;
|
||||
dst.fp16 = 1;
|
||||
dst.dest_reg = reg.reg.id;
|
||||
dst.write_mask = 1u << ch;
|
||||
|
||||
const u32 src_reg_id = reg.reg.id / 2;
|
||||
const bool is_odd_reg = !!(reg.reg.id & 1);
|
||||
const bool is_odd_ch = !!(ch & 1);
|
||||
const bool is_word0 = ch < 2;
|
||||
|
||||
// If we're an even channel, we should also write the next channel (y/w)
|
||||
if (!is_odd_ch && (mask & 2))
|
||||
{
|
||||
mask >>= 1;
|
||||
++ch;
|
||||
dst.write_mask |= (1u << ch);
|
||||
}
|
||||
|
||||
SRC0 src0{};
|
||||
src0.exec_if_eq = src0.exec_if_gr = src0.exec_if_lt = 1;
|
||||
|
||||
if (is_word0)
|
||||
{
|
||||
src0.swizzle_x = is_odd_reg ? 2 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
src0.swizzle_x = is_odd_reg ? 3 : 1;
|
||||
}
|
||||
|
||||
src0.swizzle_y = 1;
|
||||
src0.swizzle_z = 2;
|
||||
src0.swizzle_w = 3;
|
||||
src0.reg_type = RSX_FP_REGISTER_TYPE_TEMP;
|
||||
src0.tmp_reg_index = src_reg_id;
|
||||
|
||||
instruction.opcode = dst.opcode;
|
||||
instruction.bytecode[0] = dst.HEX;
|
||||
instruction.bytecode[1] = src0.HEX;
|
||||
|
||||
Register src_reg{ .id = static_cast<int>(src_reg_id), .f16 = true };
|
||||
instruction.srcs.push_back({ .reg = src_reg, .mask = 0xF });
|
||||
instruction.dsts.push_back({ .reg{.id = reg.reg.id, .f16 = false }, .mask = dst.write_mask });
|
||||
result.push_back(std::move(instruction));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Instruction> resolve_dependencies(const std::unordered_set<u32>& lanes, bool f16)
|
||||
{
|
||||
std::vector<Instruction> result;
|
||||
|
||||
if (f16)
|
||||
{
|
||||
const auto regs = decode_lanes16(lanes);
|
||||
for (const auto& ref : regs)
|
||||
{
|
||||
auto instructions = build_barrier16(ref);
|
||||
result.insert(result.end(), instructions.begin(), instructions.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto barriers = decode_lanes32(lanes);
|
||||
for (const auto& barrier : barriers)
|
||||
{
|
||||
auto instructions = build_barrier32(barrier);
|
||||
result.insert(result.end(), std::make_move_iterator(instructions.begin()), std::make_move_iterator(instructions.end()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void insert_dependency_barriers(DependencyPassContext& ctx, BasicBlock* block)
|
||||
{
|
||||
register_file_t& register_file = ctx.exec_register_map[block];
|
||||
std::memset(register_file.data(), content_unknown, register_file_max_len);
|
||||
|
||||
std::unordered_set<u32> barrier16;
|
||||
std::unordered_set<u32> barrier32;
|
||||
|
||||
// This subpass does not care about the prologue and epilogue and assumes each block is unique.
|
||||
for (auto it = block->instructions.begin(); it != block->instructions.end(); ++it)
|
||||
{
|
||||
auto& inst = *it;
|
||||
|
||||
barrier16.clear();
|
||||
barrier32.clear();
|
||||
|
||||
for (const auto& src : inst.srcs)
|
||||
{
|
||||
const auto read_bytes = get_register_file_range(src);
|
||||
const char expected_type = src.reg.f16 ? content_float16 : content_float32;
|
||||
for (const auto& index : read_bytes)
|
||||
{
|
||||
if (register_file[index] == content_unknown)
|
||||
{
|
||||
// Skip input
|
||||
continue;
|
||||
}
|
||||
|
||||
if (register_file[index] == expected_type || register_file[index] == content_dual)
|
||||
{
|
||||
// Match - nothing to do
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collision on the lane
|
||||
register_file[index] = content_dual;
|
||||
(src.reg.f16 ? barrier16 : barrier32).insert(index);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& dst : inst.dsts)
|
||||
{
|
||||
const auto write_bytes = get_register_file_range(dst);
|
||||
const char expected_type = dst.reg.f16 ? content_float16 : content_float32;
|
||||
|
||||
for (const auto& index : write_bytes)
|
||||
{
|
||||
register_file[index] = expected_type;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to inject some barrier instructions
|
||||
if (!barrier16.empty())
|
||||
{
|
||||
auto barrier16_in = decode_lanes16(barrier16);
|
||||
std::vector<Instruction> instructions;
|
||||
instructions.reserve(barrier16_in.size());
|
||||
|
||||
for (const auto& reg : barrier16_in)
|
||||
{
|
||||
auto barrier = build_barrier16(reg);
|
||||
instructions.insert(instructions.end(), std::make_move_iterator(barrier.begin()), std::make_move_iterator(barrier.end()));
|
||||
}
|
||||
|
||||
it = block->instructions.insert(it, std::make_move_iterator(instructions.begin()), std::make_move_iterator(instructions.end()));
|
||||
std::advance(it, instructions.size());
|
||||
}
|
||||
|
||||
if (!barrier32.empty())
|
||||
{
|
||||
auto barrier32_in = decode_lanes32(barrier32);
|
||||
std::vector<Instruction> instructions;
|
||||
instructions.reserve(barrier32_in.size());
|
||||
|
||||
for (const auto& reg : barrier32_in)
|
||||
{
|
||||
auto barrier = build_barrier32(reg);
|
||||
instructions.insert(instructions.end(), std::make_move_iterator(barrier.begin()), std::make_move_iterator(barrier.end()));
|
||||
}
|
||||
|
||||
it = block->instructions.insert(it, std::make_move_iterator(instructions.begin()), std::make_move_iterator(instructions.end()));
|
||||
std::advance(it, instructions.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void insert_block_register_dependency(DependencyPassContext& ctx, BasicBlock* block, const std::unordered_set<u32>& lanes, bool f16)
|
||||
{
|
||||
std::unordered_set<u32> clobbered_lanes;
|
||||
std::unordered_set<u32> lanes_to_search;
|
||||
|
||||
for (auto& back_edge : block->pred)
|
||||
{
|
||||
auto target = back_edge.from;
|
||||
|
||||
// Quick check - if we've reached an IF-ELSE anchor, don't traverse upwards.
|
||||
// The IF and ELSE edges are already a complete set and will bre processed before this node.
|
||||
if (back_edge.type == EdgeType::ENDIF &&
|
||||
&back_edge == &block->pred.back() &&
|
||||
target->succ.size() == 3 &&
|
||||
target->succ[1].type == EdgeType::ELSE &&
|
||||
target->succ[2].type == EdgeType::ENDIF &&
|
||||
target->succ[2].to == block)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Did this target even clobber our register?
|
||||
ensure(ctx.exec_register_map.find(target) != ctx.exec_register_map.end(), "Block has not been pre-processed");
|
||||
|
||||
if (ctx.sync_register_map.find(target) == ctx.sync_register_map.end())
|
||||
{
|
||||
auto& blob = ctx.sync_register_map[target];
|
||||
std::memset(blob.data(), content_unknown, register_file_max_len);
|
||||
}
|
||||
|
||||
auto& sync_register_file = ctx.sync_register_map[target];
|
||||
const auto& exec_register_file = ctx.exec_register_map[target];
|
||||
const auto clobber_type = f16 ? content_float32 : content_float16;
|
||||
|
||||
lanes_to_search.clear();
|
||||
clobbered_lanes.clear();
|
||||
|
||||
for (auto& lane : lanes)
|
||||
{
|
||||
if (exec_register_file[lane] == clobber_type &&
|
||||
sync_register_file[lane] == content_unknown)
|
||||
{
|
||||
clobbered_lanes.insert(lane);
|
||||
sync_register_file[lane] = content_dual;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exec_register_file[lane] == content_unknown)
|
||||
{
|
||||
lanes_to_search.insert(lane);
|
||||
}
|
||||
}
|
||||
|
||||
if (!clobbered_lanes.empty())
|
||||
{
|
||||
auto instructions = resolve_dependencies(clobbered_lanes, f16);
|
||||
target->epilogue.insert(target->epilogue.end(), std::make_move_iterator(instructions.begin()), std::make_move_iterator(instructions.end()));
|
||||
}
|
||||
|
||||
if (lanes_to_search.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have some missing lanes. Search upwards
|
||||
if (!target->pred.empty())
|
||||
{
|
||||
// We only need to search the last predecessor which is the true "root" of the branch
|
||||
insert_block_register_dependency(ctx, target, lanes_to_search, f16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void insert_block_dependencies(DependencyPassContext& ctx, BasicBlock* block)
|
||||
{
|
||||
auto range_from_ref = [](const RegisterRef& ref)
|
||||
{
|
||||
const auto range = get_register_file_range(ref);
|
||||
|
||||
std::unordered_set<u32> result;
|
||||
for (const auto& value : range)
|
||||
{
|
||||
result.insert(value);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
for (auto& ref : block->input_list)
|
||||
{
|
||||
const auto range = range_from_ref(ref);
|
||||
insert_block_register_dependency(ctx, block, range, ref.reg.f16);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterDependencyPass::run(FlowGraph& graph)
|
||||
{
|
||||
DependencyPassContext ctx{};
|
||||
|
||||
// First, run intra-block dependency
|
||||
for (auto& block : graph.blocks)
|
||||
{
|
||||
insert_dependency_barriers(ctx, &block);
|
||||
}
|
||||
|
||||
// Then, create prologue/epilogue instructions
|
||||
// Traverse the list in reverse order to bubble up dependencies correctly.
|
||||
for (auto it = graph.blocks.rbegin(); it != graph.blocks.rend(); ++it)
|
||||
{
|
||||
insert_block_dependencies(ctx, &(*it));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../CFG.h"
|
||||
|
||||
namespace rsx::assembler::FP
|
||||
{
|
||||
// The register dependency pass identifies data hazards for each basic block and injects barrier instructions.
|
||||
// Real PS3 does not have explicit barriers, but does instead often use delay slots or fence instructions to stall until a specific hardware unit clears the fence to advance.
|
||||
// For decompiled shaders, we have the problem that aliasing is not real and is instead simulated. We do not have access to unions on the GPU without really nasty tricks.
|
||||
class RegisterDependencyPass : public CFGPass
|
||||
{
|
||||
public:
|
||||
void run(FlowGraph& graph) override;
|
||||
};
|
||||
}
|
||||
|
|
@ -273,7 +273,7 @@ void CgBinaryDisasm::TaskFP()
|
|||
src2.HEX = GetData(data[3]);
|
||||
|
||||
m_step = 4 * sizeof(u32);
|
||||
m_opcode = dst.opcode | (src1.opcode_is_branch << 6);
|
||||
m_opcode = dst.opcode | (src1.opcode_hi << 6);
|
||||
|
||||
auto SCT = [&]()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,12 +3,19 @@
|
|||
#include "FragmentProgramDecompiler.h"
|
||||
#include "ProgramStateCache.h"
|
||||
|
||||
#include "Assembler/Passes/FP/RegisterAnnotationPass.h"
|
||||
#include "Assembler/Passes/FP/RegisterDependencyPass.h"
|
||||
|
||||
#include "Emu/system_config.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
namespace fragment_program
|
||||
{
|
||||
using namespace rsx::assembler;
|
||||
|
||||
static const std::string reg_table[] =
|
||||
{
|
||||
"wpos",
|
||||
|
|
@ -17,10 +24,33 @@ namespace rsx
|
|||
"tc0", "tc1", "tc2", "tc3", "tc4", "tc5", "tc6", "tc7", "tc8", "tc9",
|
||||
"ssa"
|
||||
};
|
||||
|
||||
static const std::vector<RegisterRef> s_fp32_output_set =
|
||||
{
|
||||
{.reg {.id = 0, .f16 = false }, .mask = 0xf },
|
||||
{.reg {.id = 2, .f16 = false }, .mask = 0xf },
|
||||
{.reg {.id = 3, .f16 = false }, .mask = 0xf },
|
||||
{.reg {.id = 4, .f16 = false }, .mask = 0xf },
|
||||
};
|
||||
|
||||
static const std::vector<RegisterRef> s_fp16_output_set =
|
||||
{
|
||||
{.reg {.id = 0, .f16 = true }, .mask = 0xf },
|
||||
{.reg {.id = 4, .f16 = true }, .mask = 0xf },
|
||||
{.reg {.id = 6, .f16 = true }, .mask = 0xf },
|
||||
{.reg {.id = 8, .f16 = true }, .mask = 0xf },
|
||||
};
|
||||
|
||||
static const RegisterRef s_z_export_reg =
|
||||
{
|
||||
.reg {.id = 1, .f16 = false },
|
||||
.mask = (1u << 2)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
using namespace rsx::fragment_program;
|
||||
using namespace rsx::assembler;
|
||||
|
||||
// SIMD vector lanes
|
||||
enum VectorLane : u8
|
||||
|
|
@ -31,6 +61,26 @@ enum VectorLane : u8
|
|||
W = 3,
|
||||
};
|
||||
|
||||
std::vector<RegisterRef> get_fragment_program_output_set(u32 ctrl, u32 mrt_count)
|
||||
{
|
||||
std::vector<RegisterRef> result;
|
||||
if (mrt_count > 0)
|
||||
{
|
||||
result = (ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS)
|
||||
? s_fp32_output_set
|
||||
: s_fp16_output_set;
|
||||
|
||||
result.resize(mrt_count);
|
||||
}
|
||||
|
||||
if (ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT)
|
||||
{
|
||||
result.push_back(s_z_export_reg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FragmentProgramDecompiler::FragmentProgramDecompiler(const RSXFragmentProgram &prog, u32& size)
|
||||
: m_size(size)
|
||||
, m_prog(prog)
|
||||
|
|
@ -151,8 +201,6 @@ void FragmentProgramDecompiler::SetDst(std::string code, u32 flags)
|
|||
}
|
||||
|
||||
const u32 reg_index = dst.fp16 ? (dst.dest_reg >> 1) : dst.dest_reg;
|
||||
ensure(reg_index < temp_registers.size());
|
||||
|
||||
if (dst.opcode == RSX_FP_OPCODE_MOV &&
|
||||
src0.reg_type == RSX_FP_REGISTER_TYPE_TEMP &&
|
||||
src0.tmp_reg_index == reg_index)
|
||||
|
|
@ -165,8 +213,6 @@ void FragmentProgramDecompiler::SetDst(std::string code, u32 flags)
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
temp_registers[reg_index].tag(dst.dest_reg, !!dst.fp16, dst.mask_x, dst.mask_y, dst.mask_z, dst.mask_w);
|
||||
}
|
||||
|
||||
void FragmentProgramDecompiler::AddFlowOp(const std::string& code)
|
||||
|
|
@ -522,26 +568,7 @@ template<typename T> std::string FragmentProgramDecompiler::GetSRC(T src)
|
|||
switch (src.reg_type)
|
||||
{
|
||||
case RSX_FP_REGISTER_TYPE_TEMP:
|
||||
|
||||
if (!src.fp16)
|
||||
{
|
||||
if (dst.opcode == RSX_FP_OPCODE_UP16 ||
|
||||
dst.opcode == RSX_FP_OPCODE_UP2 ||
|
||||
dst.opcode == RSX_FP_OPCODE_UP4 ||
|
||||
dst.opcode == RSX_FP_OPCODE_UPB ||
|
||||
dst.opcode == RSX_FP_OPCODE_UPG)
|
||||
{
|
||||
auto ® = temp_registers[src.tmp_reg_index];
|
||||
if (reg.requires_gather(src.swizzle_x))
|
||||
{
|
||||
properties.has_gather_op = true;
|
||||
AddReg(src.tmp_reg_index, src.fp16);
|
||||
ret = getFloatTypeName(4) + reg.gather_r();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (precision_modifier == RSX_FP_PRECISION_HALF)
|
||||
if (src.fp16 && precision_modifier == RSX_FP_PRECISION_HALF)
|
||||
{
|
||||
// clamp16() is not a cheap operation when emulated; avoid at all costs
|
||||
precision_modifier = RSX_FP_PRECISION_REAL;
|
||||
|
|
@ -762,7 +789,6 @@ std::string FragmentProgramDecompiler::BuildCode()
|
|||
const std::string float4_type = (fp16_out && device_props.has_native_half_support)? getHalfTypeName(4) : getFloatTypeName(4);
|
||||
const std::string init_value = float4_type + "(0.)";
|
||||
std::array<std::string, 4> output_register_names;
|
||||
std::array<u32, 4> ouput_register_indices = { 0, 2, 3, 4 };
|
||||
|
||||
// Holder for any "cleanup" before exiting main
|
||||
std::stringstream main_epilogue;
|
||||
|
|
@ -772,17 +798,6 @@ std::string FragmentProgramDecompiler::BuildCode()
|
|||
{
|
||||
// Hw tests show that the depth export register is default-initialized to 0 and not wpos.z!!
|
||||
m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "r1", init_value);
|
||||
|
||||
auto& r1 = temp_registers[1];
|
||||
if (r1.requires_gather(VectorLane::Z))
|
||||
{
|
||||
// r1.zw was not written to
|
||||
properties.has_gather_op = true;
|
||||
main_epilogue << " r1.z = " << float4_type << r1.gather_r() << ".z;\n";
|
||||
|
||||
// Emit debug warning. Useful to diagnose regressions, but should be removed in future.
|
||||
rsx_log.warning("ROP reads from shader depth without writing to it. Final value will be gathered.");
|
||||
}
|
||||
}
|
||||
|
||||
// Add the color output registers. They are statically written to and have guaranteed initialization (except r1.z which == wpos.z)
|
||||
|
|
@ -810,33 +825,6 @@ std::string FragmentProgramDecompiler::BuildCode()
|
|||
continue;
|
||||
}
|
||||
|
||||
const auto block_index = ouput_register_indices[n];
|
||||
auto& r = temp_registers[block_index];
|
||||
|
||||
if (fp16_out)
|
||||
{
|
||||
// Check if we need a split/extract op
|
||||
if (r.requires_split(0))
|
||||
{
|
||||
main_epilogue << " " << reg_name << " = " << float4_type << r.split_h0() << ";\n";
|
||||
|
||||
// Emit debug warning. Useful to diagnose regressions, but should be removed in future.
|
||||
rsx_log.warning("ROP reads from %s without writing to it. Final value will be extracted from the 32-bit register.", reg_name);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!r.requires_gather128())
|
||||
{
|
||||
// Nothing to do
|
||||
continue;
|
||||
}
|
||||
|
||||
// We need to gather the data from existing registers
|
||||
main_epilogue << " " << reg_name << " = " << float4_type << r.gather_r() << ";\n";
|
||||
properties.has_gather_op = true;
|
||||
|
||||
// Emit debug warning. Useful to diagnose regressions, but should be removed in future.
|
||||
rsx_log.warning("ROP reads from %s without writing to it. Final value will be gathered.", reg_name);
|
||||
}
|
||||
|
|
@ -1024,28 +1012,6 @@ std::string FragmentProgramDecompiler::BuildCode()
|
|||
OS << Format(divsq_func);
|
||||
}
|
||||
|
||||
// Declare register gather/merge if needed
|
||||
if (properties.has_gather_op)
|
||||
{
|
||||
std::string float2 = getFloatTypeName(2);
|
||||
|
||||
OS << float4 << " gather(" << float4 << " _h0, " << float4 << " _h1)\n";
|
||||
OS << "{\n";
|
||||
OS << " float x = uintBitsToFloat(packHalf2x16(_h0.xy));\n";
|
||||
OS << " float y = uintBitsToFloat(packHalf2x16(_h0.zw));\n";
|
||||
OS << " float z = uintBitsToFloat(packHalf2x16(_h1.xy));\n";
|
||||
OS << " float w = uintBitsToFloat(packHalf2x16(_h1.zw));\n";
|
||||
OS << " return " << float4 << "(x, y, z, w);\n";
|
||||
OS << "}\n\n";
|
||||
|
||||
OS << float2 << " gather(" << float4 << " _h)\n";
|
||||
OS << "{\n";
|
||||
OS << " float x = uintBitsToFloat(packHalf2x16(_h.xy));\n";
|
||||
OS << " float y = uintBitsToFloat(packHalf2x16(_h.zw));\n";
|
||||
OS << " return " << float2 << "(x, y);\n";
|
||||
OS << "}\n\n";
|
||||
}
|
||||
|
||||
if (properties.has_dynamic_register_load)
|
||||
{
|
||||
OS <<
|
||||
|
|
@ -1149,6 +1115,14 @@ bool FragmentProgramDecompiler::handle_sct_scb(u32 opcode)
|
|||
return true;
|
||||
case RSX_FP_OPCODE_PKB: SetDst(getFloatTypeName(4) + "(uintBitsToFloat(packUnorm4x8($0)))"); return true;
|
||||
case RSX_FP_OPCODE_SIN: SetDst("sin($0.xxxx)"); return true;
|
||||
|
||||
// Custom ISA extensions for 16-bit OR
|
||||
case RSX_FP_OPCODE_OR16_HI:
|
||||
SetDst("$float4(uintBitsToFloat((floatBitsToUint($0.x) & 0x0000ffff) | (packHalf2x16($1.xx) & 0xffff0000)))");
|
||||
return true;
|
||||
case RSX_FP_OPCODE_OR16_LO:
|
||||
SetDst("$float4(uintBitsToFloat((floatBitsToUint($0.x) & 0xffff0000) | (packHalf2x16($1.xx) & 0x0000ffff)))");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1295,7 +1269,37 @@ bool FragmentProgramDecompiler::handle_tex_srb(u32 opcode)
|
|||
|
||||
std::string FragmentProgramDecompiler::Decompile()
|
||||
{
|
||||
const auto graph = rsx::assembler::deconstruct_fragment_program(m_prog);
|
||||
auto graph = deconstruct_fragment_program(m_prog);
|
||||
|
||||
if (!graph.blocks.empty())
|
||||
{
|
||||
// The RSX CFG is missing the output block. We inject a fake tail block that ingests the ROP outputs.
|
||||
BasicBlock* rop_block = nullptr;
|
||||
BasicBlock* tail_block = &graph.blocks.back();
|
||||
if (tail_block->instructions.empty())
|
||||
{
|
||||
// Merge block. Use this directly
|
||||
rop_block = tail_block;
|
||||
}
|
||||
else
|
||||
{
|
||||
graph.blocks.push_back({});
|
||||
rop_block = &graph.blocks.back();
|
||||
|
||||
tail_block->insert_succ(rop_block);
|
||||
rop_block->insert_pred(tail_block);
|
||||
}
|
||||
|
||||
const auto rop_inputs = get_fragment_program_output_set(m_prog.ctrl, m_prog.mrt_buffers_count);
|
||||
rop_block->input_list.insert(rop_block->input_list.end(), rop_inputs.begin(), rop_inputs.end());
|
||||
|
||||
FP::RegisterAnnotationPass annotation_pass{ m_prog, { .skip_delay_slots = true } };
|
||||
FP::RegisterDependencyPass dependency_pass{};
|
||||
|
||||
annotation_pass.run(graph);
|
||||
dependency_pass.run(graph);
|
||||
}
|
||||
|
||||
m_size = 0;
|
||||
m_location = 0;
|
||||
m_loop_count = 0;
|
||||
|
|
@ -1303,57 +1307,105 @@ std::string FragmentProgramDecompiler::Decompile()
|
|||
m_is_valid_ucode = true;
|
||||
m_constant_offsets.clear();
|
||||
|
||||
enum
|
||||
// For GLSL scope wind/unwind. We store the min scope depth and loop count for each block and "unwind" to it.
|
||||
// This should recover information lost when multiple nodes converge on a single merge node or even skip a merge node as is the case with "ELSE" nodes.
|
||||
std::unordered_map<const BasicBlock*, std::pair<int, u32>> block_data;
|
||||
|
||||
auto push_block_info = [&](const BasicBlock* block)
|
||||
{
|
||||
FORCE_NONE,
|
||||
FORCE_SCT,
|
||||
FORCE_SCB,
|
||||
u32 loop = m_loop_count;
|
||||
int level = m_code_level;
|
||||
|
||||
auto found = block_data.find(block);
|
||||
if (found != block_data.end())
|
||||
{
|
||||
level = std::min(level, found->second.first);
|
||||
loop = std::min(loop, found->second.second);
|
||||
}
|
||||
|
||||
block_data[block] = { level, loop };
|
||||
};
|
||||
|
||||
int forced_unit = FORCE_NONE;
|
||||
auto emit_block = [&](const std::vector<Instruction>& instructions)
|
||||
{
|
||||
for (auto& inst : instructions)
|
||||
{
|
||||
m_instruction = &inst;
|
||||
dst.HEX = inst.bytecode[0];
|
||||
src0.HEX = inst.bytecode[1];
|
||||
src1.HEX = inst.bytecode[2];
|
||||
src2.HEX = inst.bytecode[3];
|
||||
|
||||
ensure(handle_tex_srb(inst.opcode) || handle_sct_scb(inst.opcode), "Unsupported operation");
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto &block : graph.blocks)
|
||||
{
|
||||
// TODO: Handle block prologue if any
|
||||
auto found = block_data.find(&block);
|
||||
if (found != block_data.end())
|
||||
{
|
||||
const auto [level, loop] = found->second;
|
||||
for (int i = m_code_level; i > level; i--)
|
||||
{
|
||||
m_code_level--;
|
||||
AddCode("}");
|
||||
}
|
||||
|
||||
m_loop_count = loop;
|
||||
}
|
||||
|
||||
if (!block.pred.empty())
|
||||
{
|
||||
// CFG guarantees predecessors are sorted, closest one first
|
||||
for (const auto& pred : block.pred)
|
||||
// Predecessors are always sorted closest last.
|
||||
// This gives some adjacency info and tells us how the previous block connects to this one.
|
||||
const auto& pred = block.pred.back();
|
||||
switch (pred.type)
|
||||
{
|
||||
switch (pred.type)
|
||||
{
|
||||
case rsx::assembler::EdgeType::ENDLOOP:
|
||||
m_loop_count--;
|
||||
[[ fallthrough ]];
|
||||
case rsx::assembler::EdgeType::ENDIF:
|
||||
m_code_level--;
|
||||
AddCode("}");
|
||||
break;
|
||||
case rsx::assembler::EdgeType::LOOP:
|
||||
m_loop_count++;
|
||||
[[ fallthrough ]];
|
||||
case rsx::assembler::EdgeType::IF:
|
||||
// Instruction will be inserted by the SIP decoder
|
||||
AddCode("{");
|
||||
m_code_level++;
|
||||
break;
|
||||
case rsx::assembler::EdgeType::ELSE:
|
||||
// This one needs more testing
|
||||
m_code_level--;
|
||||
AddCode("}");
|
||||
AddCode("else");
|
||||
AddCode("{");
|
||||
m_code_level++;
|
||||
break;
|
||||
default:
|
||||
// Start a new block anyway
|
||||
fmt::throw_exception("Unexpected block found");
|
||||
}
|
||||
case EdgeType::LOOP:
|
||||
m_loop_count++;
|
||||
[[ fallthrough ]];
|
||||
case EdgeType::IF:
|
||||
AddCode("{");
|
||||
m_code_level++;
|
||||
break;
|
||||
case EdgeType::ELSE:
|
||||
AddCode("else");
|
||||
AddCode("{");
|
||||
m_code_level++;
|
||||
break;
|
||||
case EdgeType::ENDIF:
|
||||
case EdgeType::ENDLOOP:
|
||||
// Pure merge block?
|
||||
break;
|
||||
case EdgeType::NONE:
|
||||
ensure(block.instructions.empty());
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Unhandled edge type %d", static_cast<int>(pred.type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!block.prologue.empty())
|
||||
{
|
||||
AddCode("// Prologue");
|
||||
emit_block(block.prologue);
|
||||
}
|
||||
|
||||
const bool early_epilogue =
|
||||
!block.epilogue.empty() &&
|
||||
!block.succ.empty() &&
|
||||
(block.succ.front().type == EdgeType::IF || block.succ.front().type == EdgeType::LOOP);
|
||||
|
||||
for (const auto& inst : block.instructions)
|
||||
{
|
||||
if (early_epilogue && &inst == &block.instructions.back())
|
||||
{
|
||||
AddCode("// Epilogue");
|
||||
emit_block(block.epilogue);
|
||||
}
|
||||
|
||||
m_instruction = &inst;
|
||||
|
||||
dst.HEX = inst.bytecode[0];
|
||||
|
|
@ -1363,11 +1415,9 @@ std::string FragmentProgramDecompiler::Decompile()
|
|||
|
||||
opflags = 0;
|
||||
|
||||
const u32 opcode = dst.opcode | (src1.opcode_is_branch << 6);
|
||||
|
||||
auto SIP = [&]()
|
||||
{
|
||||
switch (opcode)
|
||||
switch (m_instruction->opcode)
|
||||
{
|
||||
case RSX_FP_OPCODE_BRK:
|
||||
if (m_loop_count) AddFlowOp("break");
|
||||
|
|
@ -1377,12 +1427,10 @@ std::string FragmentProgramDecompiler::Decompile()
|
|||
rsx_log.error("Unimplemented SIP instruction: CAL");
|
||||
break;
|
||||
case RSX_FP_OPCODE_FENCT:
|
||||
AddCode("//FENCT");
|
||||
forced_unit = FORCE_SCT;
|
||||
AddCode("// FENCT");
|
||||
break;
|
||||
case RSX_FP_OPCODE_FENCB:
|
||||
AddCode("//FENCB");
|
||||
forced_unit = FORCE_SCB;
|
||||
AddCode("// FENCB");
|
||||
break;
|
||||
case RSX_FP_OPCODE_IFE:
|
||||
AddCode("if($cond)");
|
||||
|
|
@ -1406,7 +1454,7 @@ std::string FragmentProgramDecompiler::Decompile()
|
|||
return true;
|
||||
};
|
||||
|
||||
switch (opcode)
|
||||
switch (m_instruction->opcode)
|
||||
{
|
||||
case RSX_FP_OPCODE_NOP:
|
||||
break;
|
||||
|
|
@ -1415,19 +1463,10 @@ std::string FragmentProgramDecompiler::Decompile()
|
|||
AddFlowOp("_kill()");
|
||||
break;
|
||||
default:
|
||||
int prev_force_unit = forced_unit;
|
||||
|
||||
// Some instructions do not respect forced unit
|
||||
// Tested with Tales of Vesperia
|
||||
if (SIP()) break;
|
||||
if (handle_tex_srb(opcode)) break;
|
||||
|
||||
// FENCT/FENCB do not actually reject instructions if they dont match the forced unit
|
||||
// Looks like they are optimization hints and not hard-coded forced paths
|
||||
if (handle_sct_scb(opcode)) break;
|
||||
forced_unit = FORCE_NONE;
|
||||
|
||||
rsx_log.error("Unknown/illegal instruction: 0x%x (forced unit %d)", opcode, prev_force_unit);
|
||||
if (handle_tex_srb(m_instruction->opcode)) break;
|
||||
if (handle_sct_scb(m_instruction->opcode)) break;
|
||||
rsx_log.error("Unknown/illegal instruction: 0x%x", m_instruction->opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1435,16 +1474,28 @@ std::string FragmentProgramDecompiler::Decompile()
|
|||
if (dst.end) break;
|
||||
}
|
||||
|
||||
// TODO: Handle block epilogue if needed
|
||||
if (!early_epilogue && !block.epilogue.empty())
|
||||
{
|
||||
AddCode("// Epilogue");
|
||||
emit_block(block.epilogue);
|
||||
}
|
||||
|
||||
for (auto& succ : block.succ)
|
||||
{
|
||||
switch (succ.type)
|
||||
{
|
||||
case EdgeType::ENDIF:
|
||||
case EdgeType::ENDLOOP:
|
||||
case EdgeType::ELSE:
|
||||
push_block_info(succ.to);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (m_code_level > 1)
|
||||
{
|
||||
rsx_log.error("Hanging block found at end of shader. Malformed shader?");
|
||||
|
||||
m_code_level--;
|
||||
AddCode("}");
|
||||
}
|
||||
ensure(m_code_level == 1);
|
||||
|
||||
// flush m_code_level
|
||||
m_code_level = 1;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
#include "ShaderParam.h"
|
||||
#include "FragmentProgramRegister.h"
|
||||
#include "RSXFragmentProgram.h"
|
||||
|
||||
#include "Assembler/CFG.h"
|
||||
|
|
@ -53,8 +52,6 @@ class FragmentProgramDecompiler
|
|||
int m_code_level;
|
||||
std::unordered_map<u32, u32> m_constant_offsets;
|
||||
|
||||
std::array<rsx::MixedPrecisionRegister, 64> temp_registers;
|
||||
|
||||
std::string GetMask() const;
|
||||
|
||||
void SetDst(std::string code, u32 flags = 0);
|
||||
|
|
@ -175,7 +172,6 @@ public:
|
|||
|
||||
// Decoded properties (out)
|
||||
bool has_lit_op = false;
|
||||
bool has_gather_op = false;
|
||||
bool has_no_output = false;
|
||||
bool has_discard_op = false;
|
||||
bool has_tex_op = false;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue