Compare commits
272 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 46a54f5474 | |||
| e31173e33e | |||
| 5b0ef401f7 | |||
| 1650dd1c7d | |||
| 84f123041a | |||
| f258ae795c | |||
| 33e80a733d | |||
| 20331a77ce | |||
| 4e6b37ca04 | |||
| 646ad3eed4 | |||
| edcc2a9e0d | |||
| 0e20acdf55 | |||
| f262e77fbd | |||
| 244aa6879a | |||
| e9fe90244f | |||
| a90376a7c4 | |||
| aff7d8a994 | |||
| 7f4b6b1ae7 | |||
| 78bcb0fd53 | |||
| c733e794de | |||
| 99fc90648b | |||
| 2587545eed | |||
| ac300c7356 | |||
| 2af2db2cbf | |||
| d5ca9dacb6 | |||
| 5e436984a2 | |||
| be972f04ac | |||
| 00c87a8fc9 | |||
| e51656e070 | |||
| 9dc6d920ce | |||
| 354db953e6 | |||
| e67cf68321 | |||
| 60d35e17ac | |||
| d161d8c545 | |||
| f66b29f043 | |||
| 77f6db2543 | |||
| 5eabe9aa33 | |||
| 76c1681173 | |||
| 67ba381dfe | |||
| 9a6285942b | |||
| d93f16fe87 | |||
| 214c1ccc7e | |||
| d1ac92fd99 | |||
| 263b7854c1 | |||
| 843aed228e | |||
| bf71b1fcf7 | |||
| 81c216f330 | |||
| e4b242955c | |||
| 3fac832567 | |||
| 08011e9b78 | |||
| ee7ed1fdc3 | |||
| d63a712cc5 | |||
| c549d7154b | |||
| 590483b81f | |||
| 9ba9d9424e | |||
| 962e140cea | |||
| 9afa960979 | |||
| 8b4f3fa775 | |||
| 5fb0b2c190 | |||
| 266ab28145 | |||
| 2acac67570 | |||
| 361ef33856 | |||
| c0c52c33b9 | |||
| 4ed92f4155 | |||
| cb953a418f | |||
| 9c1f258be3 | |||
| c6641748ce | |||
| 12efd29121 | |||
| a4bd1755cf | |||
| 683c061e60 | |||
| 0e625e193e | |||
| 43302f937c | |||
| 792ac5ed64 | |||
| 90548468ee | |||
| 1584aba387 | |||
| caea340985 | |||
| ab6c74b3c1 | |||
| f06c8b22e8 | |||
| 68fdc49528 | |||
| cd73d738e5 | |||
| ab50e5483e | |||
| 8a72bdb422 | |||
| df39bd39b0 | |||
| d58df667b9 | |||
| 7a24642c56 | |||
| 1a90adfb5e | |||
| 3832d4fa1c | |||
| 8f328b2624 | |||
| 80ef348600 | |||
| e2a9c0a8f3 | |||
| 780a234467 | |||
| ea9dc9317d | |||
| 15af1bca69 | |||
| 479150b214 | |||
| e1d1d16227 | |||
| 877dd1f98d | |||
| 7df7ac57cc | |||
| e5793acd78 | |||
| 24e76f2960 | |||
| 75e66ab5e0 | |||
| b3725baf5a | |||
| f1d9a014c0 | |||
| 8e3112734e | |||
| eb850681ee | |||
| 70308d05f3 | |||
| c7e5a62909 | |||
| f72d148d37 | |||
| d8435163bb | |||
| af11546b1e | |||
| 63f16d7a46 | |||
| c765de81d4 | |||
| 02e1c49818 | |||
| 71b15c1fa6 | |||
| 65e9388f00 | |||
| 056d43eca7 | |||
| 55ec4808f1 | |||
| 2cfa5406e7 | |||
| 86716dc37b | |||
| f9ab077908 | |||
| 8b3a3e4ac8 | |||
| 45f5819ef3 | |||
| 3f55067414 | |||
| 797b69fe8f | |||
| 019fa390f2 | |||
| 42de2a3e0b | |||
| f90bf2dd40 | |||
| dc8fc9fc79 | |||
| 3d49976b3c | |||
| eed38e1bbc | |||
| 9595297a3a | |||
| 7c5b5d25e3 | |||
| 2e22a0d9bb | |||
| ba2a8ebf2e | |||
| 381c7544fa | |||
| 4752c4014b | |||
| 24642a4c18 | |||
| 62979c7bd9 | |||
| 7b9fb7ad9c | |||
| 7f830d555d | |||
| 9aafd8c09f | |||
| 6781eb7c76 | |||
| 3e09b97f58 | |||
| e7b9513d4a | |||
| 34c36c73ee | |||
| 2821eb7397 | |||
| 65c9cd99cd | |||
| b217e8384c | |||
| df96be199b | |||
| ecc55048c5 | |||
| e58911cc95 | |||
| 269c4604aa | |||
| 8f1dc7a2d4 | |||
| da91a841b7 | |||
| 4323ca4a01 | |||
| fff0c96bf3 | |||
| 7167fbf545 | |||
| e4aff539b0 | |||
| d0e31f4ec3 | |||
| 3e06446297 | |||
| 4237039d53 | |||
| 5ae7dbf77c | |||
| f1037f75d9 | |||
| a7cb513a89 | |||
| e3ec71c683 | |||
| f7870d4968 | |||
| 80ef0ce9b5 | |||
| 696a3c4a42 | |||
| 81a01134bb | |||
| 70c291753c | |||
| 3c7ada8e83 | |||
| 46b3027981 | |||
| 334999f639 | |||
| dabfce5c82 | |||
| 0a8d9a12ab | |||
| 19b2da2590 | |||
| 17a074e1ac | |||
| 38d5f688b3 | |||
| e307a1b4bd | |||
| a50e22a11f | |||
| c995505023 | |||
| 1f8f428a59 | |||
| e10c6cbaf7 | |||
| 5870da0b55 | |||
| 5f729d4dbf | |||
| ebde86b967 | |||
| 14a425e487 | |||
| 1f1d1da87b | |||
| 129ade2f73 | |||
| c37c6fb6de | |||
| 5736fa3351 | |||
| 3e84a2acc0 | |||
| 77c252a4c0 | |||
| e4f92ddf77 | |||
| 1f3b1e1c99 | |||
| 73bdf7481e | |||
| c2ab3c664c | |||
| ddec5d6908 | |||
| 975aae1d13 | |||
| 9e62ca562b | |||
| d28b0ba2fa | |||
| aba332d4c4 | |||
| 543fb7a9cb | |||
| 67b3fc70f8 | |||
| 2268aa9093 | |||
| 4b8ee85995 | |||
| 86ead1b93b | |||
| 7ec7f261b1 | |||
| 1685769bd9 | |||
| 2acb6ed60d | |||
| 144244e902 | |||
| 7cf9d1380b | |||
| f188019244 | |||
| 38dfc88e8d | |||
| 95c36221fa | |||
| b30ac48513 | |||
| 4fcc1c0f31 | |||
| 1a2fad4eac | |||
| da4f4dafbd | |||
| e5876b3a1b | |||
| 9c2d94ca1a | |||
| ae4f837f57 | |||
| c6c4aa4634 | |||
| bcda172ae7 | |||
| d511e76a63 | |||
| 56e982375e | |||
| e3eaf5f29e | |||
| e56609d836 | |||
| 460fb66949 | |||
| b955469721 | |||
| 6378e16fdf | |||
| 8b48fd95fe | |||
| 5600430a05 | |||
| 50ad7ba1f6 | |||
| fe6cb4774e | |||
| 91fefd4671 | |||
| 677d5f09ce | |||
| c7f5475b56 | |||
| 42e3add6a1 | |||
| 8dc98bbc1b | |||
| f98595bee5 | |||
| fafefb2cf5 | |||
| 1cbcf7e1ad | |||
| df080fbc53 | |||
| bd66dfedc9 | |||
| 69faf14a79 | |||
| 1a443cf6b5 | |||
| 2fb68bfc03 | |||
| 4d70c483b2 | |||
| 29256df5b1 | |||
| a74156fe5b | |||
| 57473ff98d | |||
| 00618995d9 | |||
| 6a340afb8a | |||
| dfbc49513d | |||
| 7bb830a228 | |||
| b3f002f68f | |||
| ea949a5f28 | |||
| 53457262d4 | |||
| 472efc08eb | |||
| 0525070898 | |||
| cbc1c472d5 | |||
| aca9884e76 | |||
| 17fb601c02 | |||
| 76ac8ffbd6 | |||
| fe0c164de2 | |||
| ea2c5294df | |||
| 3c0681ad6d | |||
| 0debcfed0a | |||
| 06f733a7f2 | |||
| ad6a72e369 | |||
| b40ed5bdb7 | |||
| ee6e4c493d |
@@ -12,7 +12,7 @@ pkg info # debug
|
||||
pkg install llvm12
|
||||
|
||||
# Mandatory dependencies (qt5-dbus and qt5-gui are pulled via qt5-widgets)
|
||||
pkg install git ccache cmake ninja qt5-qmake qt5-buildtools qt5-widgets qt5-concurrent glew openal-soft ffmpeg
|
||||
pkg install git ccache cmake ninja qt5-qmake qt5-buildtools qt5-widgets qt5-concurrent qt5-multimedia qt5-svg glew openal-soft ffmpeg
|
||||
|
||||
# Optional dependencies (libevdev is pulled by qt5-gui)
|
||||
pkg install pkgconf alsa-lib pulseaudio sdl2 evdev-proto vulkan-headers vulkan-loader
|
||||
|
||||
@@ -7,8 +7,8 @@ PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID"
|
||||
|
||||
# Resource/dependency URLs
|
||||
# Qt mirrors can be volatile and slow, so we list 2
|
||||
QT_HOST="http://mirrors.ocf.berkeley.edu/qt/"
|
||||
#QT_HOST="http://qt.mirror.constant.com/"
|
||||
#QT_HOST="http://mirrors.ocf.berkeley.edu/qt/"
|
||||
QT_HOST="http://qt.mirror.constant.com/"
|
||||
QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g")
|
||||
QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]')
|
||||
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}.win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}"
|
||||
@@ -17,6 +17,8 @@ QT_BASE_URL="${QT_HOST}${QT_PREFIX}qtbase${QT_SUFFIX}"
|
||||
QT_WINE_URL="${QT_HOST}${QT_PREFIX}qtwinextras${QT_SUFFIX}"
|
||||
QT_DECL_URL="${QT_HOST}${QT_PREFIX}qtdeclarative${QT_SUFFIX}"
|
||||
QT_TOOL_URL="${QT_HOST}${QT_PREFIX}qttools${QT_SUFFIX}"
|
||||
QT_MM_URL="${QT_HOST}${QT_PREFIX}qtmultimedia${QT_SUFFIX}"
|
||||
QT_SVG_URL="${QT_HOST}${QT_PREFIX}qtsvg${QT_SUFFIX}"
|
||||
LLVMLIBS_URL='https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win/llvmlibs_mt.7z'
|
||||
GLSLANG_URL='https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z'
|
||||
VULKAN_SDK_URL="https://www.dropbox.com/s/fjlh97hrz0crqxv/VulkanSDK-1.2.182.0-Installer.exe"
|
||||
@@ -26,6 +28,8 @@ DEP_URLS=" \
|
||||
$QT_WINE_URL \
|
||||
$QT_DECL_URL \
|
||||
$QT_TOOL_URL \
|
||||
$QT_MM_URL \
|
||||
$QT_SVG_URL \
|
||||
$LLVMLIBS_URL \
|
||||
$GLSLANG_URL \
|
||||
$VULKAN_SDK_URL"
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ windows_task:
|
||||
|
||||
linux_task:
|
||||
container:
|
||||
image: rpcs3/rpcs3-ci-bionic:1.1
|
||||
image: rpcs3/rpcs3-ci-bionic:1.3
|
||||
cpu: 4
|
||||
memory: 16G
|
||||
env:
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
Standard: c++20
|
||||
UseTab: ForIndentation
|
||||
UseTab: AlignWithSpaces
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
AccessModifierOffset: -4
|
||||
@@ -16,6 +16,7 @@ AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: Inline
|
||||
Cpp11BracedListStyle: true
|
||||
IndentCaseLabels: false
|
||||
SortIncludes: false
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@
|
||||
url = ../../curl/curl.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/wolfssl"]
|
||||
path = 3rdparty/wolfssl
|
||||
path = 3rdparty/wolfssl/wolfssl
|
||||
url = ../../wolfSSL/wolfssl.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/flatbuffers"]
|
||||
|
||||
Vendored
+7
@@ -45,7 +45,14 @@ if(WIN32)
|
||||
src/LzmaLib.c
|
||||
src/7zCrc.c
|
||||
src/MtDec.c)
|
||||
target_include_directories(3rdparty_7z INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
|
||||
$<INSTALL_INTERFACE:/src>)
|
||||
|
||||
target_include_directories(3rdparty_7z INTERFACE 7z)
|
||||
|
||||
set_property(TARGET 3rdparty_7z PROPERTY FOLDER "3rdparty/")
|
||||
|
||||
else()
|
||||
add_library(3rdparty_7z INTERFACE)
|
||||
endif()
|
||||
|
||||
Vendored
+4
-46
@@ -239,6 +239,9 @@ if(USE_FAUDIO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${FAUDIO_TARGET} PROPERTY FOLDER "3rdparty/")
|
||||
|
||||
|
||||
# FFMPEG
|
||||
add_library(3rdparty_ffmpeg INTERFACE)
|
||||
|
||||
@@ -309,52 +312,7 @@ endif()
|
||||
include(llvm.cmake)
|
||||
|
||||
# WOLFSSL
|
||||
if(USE_SYSTEM_WOLFSSL)
|
||||
message("-- RPCS3: using shared wolfssl")
|
||||
pkg_check_modules(WolfSSL REQUIRED IMPORTED_TARGET wolfssl>=4.7.0)
|
||||
add_library(wolfssl INTERFACE)
|
||||
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
|
||||
else()
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# OPENSSL_EXTRA, WOLFSSL_DES_ECB and HAVE_SNI are unconfigurable from CMake cache.
|
||||
# but they do have it in a TODO list (wolfssl/CMakeList, 1021)
|
||||
add_compile_definitions(OPENSSL_EXTRA WOLFSSL_DES_ECB HAVE_SNI)
|
||||
|
||||
set(WOLFSSL_TLS13 "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHA3 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHAKE256 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_BASE64_ENCODE "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_DES3 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_POLY1305 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CHACHA "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FILESYSTEM "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_PWDBASED "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FAST_MATH "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_EXAMPLES "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CRYPT_TESTS "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_ASYNC_THREADS "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CONFIG_H "no" CACHE INTERNAL "")
|
||||
|
||||
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
|
||||
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# This only works in single-config generator
|
||||
# For a multi-config generator, we need to provide different wolfssl binaries for different config
|
||||
if (GENERATOR_IS_MULTI_CONFIG)
|
||||
message( FATAL_ERROR "RPCS3 can only be configured using single-config generator." )
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(WolfSSL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/wolfssl.lib")
|
||||
else()
|
||||
set(WolfSSL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/libwolfssl.a")
|
||||
endif()
|
||||
|
||||
# "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/" provides openssl headers
|
||||
# So that curl can be built on an environment where openssl headers are not provided
|
||||
set(WolfSSL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/" "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/" "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/")
|
||||
endif()
|
||||
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
|
||||
|
||||
# CURL
|
||||
add_subdirectory(curl EXCLUDE_FROM_ALL)
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/FAudio updated: 1f6daa2382...28528bc885
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
# OpenAL
|
||||
if (MSVC)
|
||||
find_path(OPENAL_INCLUDE_DIR al.h PATHS OpenAL/include)
|
||||
find_library(OPENAL_LIBRARY OpenAL32 PATHS OpenAL/libs/Win64/)
|
||||
find_path(OPENAL_INCLUDE_DIR al.h PATHS include/)
|
||||
find_library(OPENAL_LIBRARY OpenAL32 PATHS libs/Win64/)
|
||||
else()
|
||||
find_package(OpenAL REQUIRED)
|
||||
endif()
|
||||
|
||||
Vendored
+4
-2
@@ -38,8 +38,10 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeGenerator>"Visual Studio $(VisualStudioVersion.Substring(0,2))"</CmakeGenerator>
|
||||
<CmakeReleaseCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) SPIRV-Tools -B build</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) -S SPIRV-Tools -B build</CmakeDebugCLI>
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) SPIRV-Tools -B build</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) -S SPIRV-Tools -B build</CmakeDebugCLI>
|
||||
<PropsAbsPath>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\buildfiles\msvc\common_default.props'))</PropsAbsPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
|
||||
BIN
Binary file not shown.
Vendored
+1
@@ -14,4 +14,5 @@ target_link_libraries(asmjit PRIVATE ${ASMJIT_DEPS})
|
||||
# ASMJIT should have a option for disabling installing and this wouldnt
|
||||
# be required to avoid installing ASMJIT...
|
||||
|
||||
set_property(TARGET asmjit PROPERTY FOLDER "3rdparty/")
|
||||
add_library(3rdparty::asmjit ALIAS asmjit)
|
||||
|
||||
Vendored
+6
@@ -12,5 +12,11 @@ else()
|
||||
option(HTTP_ONLY "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)" ON)
|
||||
option(CURL_ZLIB "Set to ON to enable building curl with zlib support." OFF)
|
||||
set(CURL_CA_PATH "none" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
|
||||
if(USE_MSVC_STATIC_CRT)
|
||||
set(CURL_STATIC_CRT ON CACHE BOOL "Use static crt to build curl" FORCE)
|
||||
endif()
|
||||
add_subdirectory(curl EXCLUDE_FROM_ALL)
|
||||
|
||||
set_property(TARGET libcurl PROPERTY FOLDER "3rdparty/")
|
||||
|
||||
endif()
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/curl/curl updated: 6b951a6928...c7aef0a945
Vendored
+1
-1
@@ -43,7 +43,7 @@
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>curl\include;curl\lib;extra;$(SolutionDir)3rdparty\wolfssl;$(SolutionDir)3rdparty\wolfssl\wolfssl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>curl\include;curl\lib;extra;$(SolutionDir)3rdparty\wolfssl\wolfssl\wolfssl;$(SolutionDir)3rdparty\wolfssl\wolfssl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>HAVE_SNI;NDEBUG;BUILDING_LIBCURL;CURL_STATICLIB;USE_WOLFSSL;USE_IPV6;SIZEOF_LONG=4;SIZEOF_LONG_LONG=8;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/flatbuffers updated: 9e7e8cbe9f...a9a295fecf
Vendored
+1
-1
Submodule 3rdparty/glslang/glslang updated: ae2a562936...2fb89a0072
Vendored
+4
-2
@@ -38,8 +38,10 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeGenerator>"Visual Studio $(VisualStudioVersion.Substring(0,2))"</CmakeGenerator>
|
||||
<CmakeReleaseCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeDebugCLI>
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeDebugCLI>
|
||||
<PropsAbsPath>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\buildfiles\msvc\common_default.props'))</PropsAbsPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
|
||||
Vendored
+4
-4
@@ -2,17 +2,17 @@ add_library(3rdparty_qt5 INTERFACE)
|
||||
|
||||
set(QT_MIN_VER 5.15.2)
|
||||
|
||||
find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent)
|
||||
find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia MultimediaWidgets Svg)
|
||||
if(WIN32)
|
||||
find_package(Qt5 ${QT_MIN_VER} COMPONENTS WinExtras REQUIRED)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg)
|
||||
else()
|
||||
find_package(Qt5 ${QT_MIN_VER} COMPONENTS DBus Gui)
|
||||
if(Qt5DBus_FOUND)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg)
|
||||
target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS)
|
||||
else()
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg)
|
||||
endif()
|
||||
target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
+81
-21
@@ -1,4 +1,4 @@
|
||||
// stb_truetype.h - v1.25 - public domain
|
||||
// stb_truetype.h - v1.26 - public domain
|
||||
// authored from 2009-2021 by Sean Barrett / RAD Game Tools
|
||||
//
|
||||
// =======================================================================
|
||||
@@ -58,6 +58,7 @@
|
||||
//
|
||||
// VERSION HISTORY
|
||||
//
|
||||
// 1.26 (2021-08-28) fix broken rasterizer
|
||||
// 1.25 (2021-07-11) many fixes
|
||||
// 1.24 (2020-02-05) fix warning
|
||||
// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
|
||||
@@ -271,8 +272,8 @@
|
||||
//// SAMPLE PROGRAMS
|
||||
////
|
||||
//
|
||||
// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
|
||||
//
|
||||
// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.
|
||||
// See "tests/truetype_demo_win32.c" for a complete version.
|
||||
#if 0
|
||||
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
|
||||
#include "stb_truetype.h"
|
||||
@@ -307,10 +308,10 @@ void my_stbtt_print(float x, float y, char *text)
|
||||
if (*text >= 32 && *text < 128) {
|
||||
stbtt_aligned_quad q;
|
||||
stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
|
||||
glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
|
||||
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
|
||||
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
|
||||
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
|
||||
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);
|
||||
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);
|
||||
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);
|
||||
glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);
|
||||
}
|
||||
++text;
|
||||
}
|
||||
@@ -3061,6 +3062,23 @@ static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edg
|
||||
}
|
||||
}
|
||||
|
||||
static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)
|
||||
{
|
||||
STBTT_assert(top_width >= 0);
|
||||
STBTT_assert(bottom_width >= 0);
|
||||
return (top_width + bottom_width) / 2.0f * height;
|
||||
}
|
||||
|
||||
static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)
|
||||
{
|
||||
return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);
|
||||
}
|
||||
|
||||
static float stbtt__sized_triangle_area(float height, float width)
|
||||
{
|
||||
return height * width / 2;
|
||||
}
|
||||
|
||||
static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
|
||||
{
|
||||
float y_bottom = y_top+1;
|
||||
@@ -3115,10 +3133,10 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
|
||||
float height;
|
||||
// simple case, only spans one pixel
|
||||
int x = (int) x_top;
|
||||
height = sy1 - sy0;
|
||||
height = (sy1 - sy0) * e->direction;
|
||||
STBTT_assert(x >= 0 && x < len);
|
||||
scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
|
||||
scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
|
||||
scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
|
||||
scanline_fill[x] += height; // everything right of this pixel is filled
|
||||
} else {
|
||||
int x,x1,x2;
|
||||
float y_crossing, y_final, step, sign, area;
|
||||
@@ -3134,40 +3152,79 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
|
||||
dy = -dy;
|
||||
t = x0, x0 = xb, xb = t;
|
||||
}
|
||||
assert(dy >= 0);
|
||||
assert(dx >= 0);
|
||||
STBTT_assert(dy >= 0);
|
||||
STBTT_assert(dx >= 0);
|
||||
|
||||
x1 = (int) x_top;
|
||||
x2 = (int) x_bottom;
|
||||
// compute intersection with y axis at x1+1
|
||||
y_crossing = (x1+1 - x0) * dy + y_top;
|
||||
y_crossing = y_top + dy * (x1+1 - x0);
|
||||
|
||||
// compute intersection with y axis at x2
|
||||
y_final = y_top + dy * (x2 - x0);
|
||||
|
||||
// x1 x_top x2 x_bottom
|
||||
// y_top +------|-----+------------+------------+--------|---+------------+
|
||||
// | | | | | |
|
||||
// | | | | | |
|
||||
// sy0 | Txxxxx|............|............|............|............|
|
||||
// y_crossing | *xxxxx.......|............|............|............|
|
||||
// | | xxxxx..|............|............|............|
|
||||
// | | /- xx*xxxx........|............|............|
|
||||
// | | dy < | xxxxxx..|............|............|
|
||||
// y_final | | \- | xx*xxx.........|............|
|
||||
// sy1 | | | | xxxxxB...|............|
|
||||
// | | | | | |
|
||||
// | | | | | |
|
||||
// y_bottom +------------+------------+------------+------------+------------+
|
||||
//
|
||||
// goal is to measure the area covered by '.' in each pixel
|
||||
|
||||
// if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
|
||||
// @TODO: maybe test against sy1 rather than y_bottom?
|
||||
if (y_crossing > y_bottom)
|
||||
y_crossing = y_bottom;
|
||||
|
||||
sign = e->direction;
|
||||
// area of the rectangle covered from y0..y_crossing
|
||||
|
||||
// area of the rectangle covered from sy0..y_crossing
|
||||
area = sign * (y_crossing-sy0);
|
||||
// area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
|
||||
scanline[x1] += area * (x1+1 - x_top)/2;
|
||||
|
||||
// area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
|
||||
scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);
|
||||
|
||||
// check if final y_crossing is blown up; no test case for this
|
||||
y_final = y_crossing + dy * (x2 - (x1+1)); // advance y by number of steps taken below
|
||||
if (y_final > y_bottom) {
|
||||
y_final = y_bottom;
|
||||
dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom
|
||||
}
|
||||
|
||||
step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, which is also how much pixel area changes for each step in x
|
||||
// in second pixel, area covered by line segment found in first pixel
|
||||
// is always a rectangle 1 wide * the height of that line segment; this
|
||||
// is exactly what the variable 'area' stores. it also gets a contribution
|
||||
// from the line segment within it. the THIRD pixel will get the first
|
||||
// pixel's rectangle contribution, the second pixel's rectangle contribution,
|
||||
// and its own contribution. the 'own contribution' is the same in every pixel except
|
||||
// the leftmost and rightmost, a trapezoid that slides down in each pixel.
|
||||
// the second pixel's contribution to the third pixel will be the
|
||||
// rectangle 1 wide times the height change in the second pixel, which is dy.
|
||||
|
||||
step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,
|
||||
// which multiplied by 1-pixel-width is how much pixel area changes for each step in x
|
||||
// so the area advances by 'step' every time
|
||||
|
||||
for (x = x1+1; x < x2; ++x) {
|
||||
scanline[x] += area + step/2; // area of parallelogram is step/2
|
||||
scanline[x] += area + step/2; // area of trapezoid is 1*step/2
|
||||
area += step;
|
||||
}
|
||||
STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
|
||||
STBTT_assert(sy1 > y_final-0.01f);
|
||||
|
||||
// area of the triangle (x2,y_crossing), (x_bottom,y1), (x2,y1)
|
||||
scanline[x2] += area + sign * (x_bottom - x2)/2 * (sy1-y_crossing);
|
||||
// area covered in the last pixel is the rectangle from all the pixels to the left,
|
||||
// plus the trapezoid filled by the line segment in this pixel all the way to the right edge
|
||||
scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);
|
||||
|
||||
// the rest of the line is filled based on the total height of the line segment in this pixel
|
||||
scanline_fill[x2] += sign * (sy1-sy0);
|
||||
}
|
||||
} else {
|
||||
@@ -3175,6 +3232,9 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
|
||||
// clipping logic. since this does not match the intended use
|
||||
// of this library, we use a different, very slow brute
|
||||
// force implementation
|
||||
// note though that this does happen some of the time because
|
||||
// x_top and x_bottom can be extrapolated at the top & bottom of
|
||||
// the shape and actually lie outside the bounding box
|
||||
int x;
|
||||
for (x=0; x < len; ++x) {
|
||||
// cases:
|
||||
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
if(USE_SYSTEM_WOLFSSL)
|
||||
message("-- RPCS3: using shared wolfssl")
|
||||
pkg_check_modules(WolfSSL REQUIRED IMPORTED_TARGET wolfssl>=4.7.0)
|
||||
add_library(wolfssl INTERFACE)
|
||||
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
|
||||
else()
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# OPENSSL_EXTRA, WOLFSSL_DES_ECB and HAVE_SNI are unconfigurable from CMake cache.
|
||||
# but they do have it in a TODO list (wolfssl/CMakeList, 1021)
|
||||
add_compile_definitions(OPENSSL_EXTRA WOLFSSL_DES_ECB HAVE_SNI HAVE_WRITE_DUP)
|
||||
|
||||
set(WOLFSSL_TLS13 "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHA3 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHAKE256 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_BASE64_ENCODE "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_DES3 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_POLY1305 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CHACHA "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FILESYSTEM "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_PWDBASED "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FAST_MATH "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_EXAMPLES "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CRYPT_TESTS "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_ASYNC_THREADS "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CONFIG_H "no" CACHE INTERNAL "")
|
||||
|
||||
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
|
||||
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# This only works in single-config generator
|
||||
# For a multi-config generator, we need to provide different wolfssl binaries for different config
|
||||
if (GENERATOR_IS_MULTI_CONFIG)
|
||||
message( FATAL_ERROR "RPCS3 can only be configured using single-config generator." )
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(WolfSSL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/${CMAKE_BUILD_TYPE}/wolfssl.lib" CACHE INTERNAL "")
|
||||
else()
|
||||
set(WolfSSL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/libwolfssl.a" CACHE INTERNAL "")
|
||||
endif()
|
||||
|
||||
# "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/" provides openssl headers
|
||||
# So that curl can be built on an environment where openssl headers are not provided
|
||||
set(WolfSSL_INCLUDE_DIR
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/wolfssl/"
|
||||
CACHE INTERNAL "")
|
||||
endif()
|
||||
@@ -48,7 +48,7 @@
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>./wolfssl;./wolfssl/IDE/WIN;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;ECC_TIMING_RESISTANT;WC_RSA_BLINDING;HAVE_AESGCM;WOLFSSL_SHA512;WOLFSSL_SHA384;NO_DSA;HAVE_ECC;TFM_ECC256;ECC_SHAMIR;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_TLS_EXTENSIONS;HAVE_SNI;HAVE_SUPPORTED_CURVES;HAVE_EXTENDED_MASTER;NO_RC4;HAVE_ENCRYPT_THEN_MAC;NO_PSK;NO_MD4;WC_NO_ASYNC_THREADING;OPENSSL_EXTRA;WOLFSSL_USER_SETTINGS;CYASSL_USER_SETTINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;CYASSL_USER_SETTINGS;WC_NO_HARDEN;HAVE_WRITE_DUP;WC_RSA_BLINDING;NO_MULTIBYTE_PRINT;OPENSSL_EXTRA;WOLFSSL_RIPEMD;NO_PSK;HAVE_EXTENDED_MASTER;WOLFSSL_SNIFFER;HAVE_AESGCM;WOLFSSL_SHA384;WOLFSSL_SHA512;HAVE_SUPPORTED_CURVES;HAVE_TLS_EXTENSIONS;HAVE_ECC;ECC_SHAMIR;ECC_TIMING_RESISTANT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
@@ -62,7 +62,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>./wolfssl;./wolfssl/IDE/WIN;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;WOLFSSL_USER_SETTINGS;CYASSL_USER_SETTINGS;WC_NO_HARDEN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;CYASSL_USER_SETTINGS;WC_NO_HARDEN;HAVE_WRITE_DUP;WC_RSA_BLINDING;NO_MULTIBYTE_PRINT;OPENSSL_EXTRA;WOLFSSL_RIPEMD;NO_PSK;HAVE_EXTENDED_MASTER;WOLFSSL_SNIFFER;HAVE_AESGCM;WOLFSSL_SHA384;WOLFSSL_SHA512;HAVE_SUPPORTED_CURVES;HAVE_TLS_EXTENSIONS;HAVE_ECC;ECC_SHAMIR;ECC_TIMING_RESISTANT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
Vendored
+1
-1
Submodule 3rdparty/yaml-cpp/yaml-cpp updated: 6a211f0bc7...0b67821f30
Vendored
+1
@@ -52,6 +52,7 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\convert.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\depthguard.cpp" />
|
||||
<ClCompile Include="yaml-cpp\src\directives.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emit.cpp">
|
||||
|
||||
+3
@@ -91,5 +91,8 @@
|
||||
<ClCompile Include="yaml-cpp\src\exceptions.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\depthguard.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+50
-3
@@ -28,17 +28,64 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/buildfiles/cmake")
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# Currently cmake config scripts are single-config generator only.
|
||||
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(IS_MULTI_CONFIG)
|
||||
message(FATAL_ERROR "Multi-config generators are not supported.")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "No build type selected, default to Release")
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
add_definitions(-DNDEBUG)
|
||||
elseif(NOT MSVC)
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Debug" AND NOT MSVC)
|
||||
add_definitions(-D_DEBUG)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
option(USE_MSVC_STATIC_CRT "Use static MSVC C runtime" OFF)
|
||||
|
||||
# TODO(cjj19970505@live.cno)
|
||||
# DiscordRPC binary in 3rdparty is compiled /MT
|
||||
# So theoretically we should enable DiscordRPC in Release and static CRT build
|
||||
# since we might encounter some rumtime issues when more than one CRT version are presented.
|
||||
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
|
||||
# Add other DiscordRPC binaries(compiled with /MTd, /MD, /MDd) or compile it from source may address this issue.
|
||||
if(NOT(CMAKE_BUILD_TYPE MATCHES "Release" AND USE_MSVC_STATIC_CRT))
|
||||
set(USE_DISCORD_RPC OFF CACHE BOOL "Discord RPC is only available in Release and static CRT build." FORCE)
|
||||
endif()
|
||||
|
||||
if(USE_MSVC_STATIC_CRT)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
else()
|
||||
# though doc ( https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html )
|
||||
# says if that property is not set then CMake uses the default value MultiThreaded$<$<CONFIG:Debug>:Debug>DLL
|
||||
# to select a MSVC runtime library.
|
||||
# But yaml-cpp set /MT(d) if CMAKE_MSVC_RUNTIME_LIBRARY is undefined
|
||||
# So we have to define it explicitly
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
endif()
|
||||
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# offical QT uses dynamic CRT.
|
||||
# When building our lib with static CRT and debug build type
|
||||
# and linking with Qt with dynmaic CRT and debug build,
|
||||
# error is encountered in runtime (which is expected).
|
||||
# But building our lib with static CRT and release build type,
|
||||
# and linking with Qt with dynamic CRT and release build seems to be working,
|
||||
# which is the same config with VS solution.
|
||||
# (though technically it might still have some hidden errors).
|
||||
# So we allow static CRT in both relase and debug build, but prompt warning in debug build.
|
||||
# For more info:
|
||||
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
|
||||
# https://wiki.qt.io/Technical_FAQ#Why_does_a_statically_built_Qt_use_the_dynamic_Visual_Studio_runtime_libraries_.3F_Do_I_need_to_deploy_those_with_my_application_.3F
|
||||
if(USE_MSVC_STATIC_CRT AND CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
message(AUTHOR_WARNING "Debug build currently can not work with static CRT.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
|
||||
endif()
|
||||
|
||||
@@ -387,7 +387,7 @@ bool cfg::node::from_string(const std::string& value, bool dynamic)
|
||||
return true;
|
||||
}
|
||||
|
||||
cfg_log.fatal("Failed to load node: %s", error);
|
||||
cfg_log.error("Failed to load node: %s", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -429,6 +429,14 @@ void cfg::map_entry::set_map(std::map<std::string, std::string>&& map)
|
||||
m_map = std::move(map);
|
||||
}
|
||||
|
||||
void cfg::map_entry::erase(const std::string& key)
|
||||
{
|
||||
if (m_map.contains(key))
|
||||
{
|
||||
m_map.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
void cfg::map_entry::from_default()
|
||||
{
|
||||
set_map({});
|
||||
|
||||
@@ -494,6 +494,8 @@ namespace cfg
|
||||
void set_value(const std::string& key, const std::string& value);
|
||||
void set_map(std::map<std::string, std::string>&& map);
|
||||
|
||||
void erase(const std::string& key);
|
||||
|
||||
void from_default() override;
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -383,12 +383,12 @@ shared_ptr<fs::device_base> fs::set_virtual_device(const std::string& name, shar
|
||||
return get_device_manager().set_device(name, std::move(device));
|
||||
}
|
||||
|
||||
std::string fs::get_parent_dir(const std::string& path)
|
||||
std::string fs::get_parent_dir(const std::string& path, u32 levels)
|
||||
{
|
||||
std::string_view result = path;
|
||||
|
||||
// Number of path components to remove
|
||||
usz to_remove = 1;
|
||||
usz to_remove = levels;
|
||||
|
||||
while (to_remove--)
|
||||
{
|
||||
@@ -683,9 +683,9 @@ bool fs::create_path(const std::string& path)
|
||||
|
||||
#ifdef _WIN32
|
||||
// Workaround: don't call is_dir with naked drive letter
|
||||
if (!parent.empty() && parent.back() != ':' && !is_dir(parent) && !create_path(parent))
|
||||
if (parent.size() < path.size() && parent.back() != ':' && !is_dir(parent) && !create_path(parent))
|
||||
#else
|
||||
if (!parent.empty() && !is_dir(parent) && !create_path(parent))
|
||||
if (parent.size() < path.size() && !is_dir(parent) && !create_path(parent))
|
||||
#endif
|
||||
{
|
||||
return false;
|
||||
|
||||
+3
-2
@@ -45,7 +45,8 @@ namespace fs
|
||||
constexpr auto lock = +open_mode::lock; // Prevent opening the file more than once
|
||||
constexpr auto unread = +open_mode::unread; // Aggressively prevent reading the opened file (do not use)
|
||||
|
||||
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
|
||||
constexpr auto write_new = write + create + excl;
|
||||
constexpr auto rewrite = write + create + trunc;
|
||||
|
||||
// File seek mode
|
||||
enum class seek_mode : u32
|
||||
@@ -156,7 +157,7 @@ namespace fs
|
||||
shared_ptr<device_base> set_virtual_device(const std::string& name, shared_ptr<device_base> device);
|
||||
|
||||
// Try to get parent directory (returns empty string on failure)
|
||||
std::string get_parent_dir(const std::string& path);
|
||||
std::string get_parent_dir(const std::string& path, u32 levels = 1);
|
||||
|
||||
// Get file information
|
||||
bool stat(const std::string& path, stat_t& info);
|
||||
|
||||
+6
-15
@@ -1231,20 +1231,16 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
|
||||
|
||||
bool handled = rsx::g_access_violation_handler(addr, is_writing);
|
||||
|
||||
if (cpu && (cpu->state += cpu_flag::temp, cpu->test_stopped()))
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
if (handled)
|
||||
{
|
||||
g_tls_fault_rsx++;
|
||||
if (cpu && cpu->test_stopped())
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cpu && cpu->test_stopped())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
const u8* const code = reinterpret_cast<u8*>(RIP(context));
|
||||
@@ -1389,11 +1385,6 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
|
||||
|
||||
if (vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable))
|
||||
{
|
||||
if (cpu && cpu->test_stopped())
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1580,7 +1571,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
|
||||
}
|
||||
}
|
||||
|
||||
Emu.Pause();
|
||||
Emu.Pause(true);
|
||||
|
||||
if (!g_tls_access_violation_recovered)
|
||||
{
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ private:
|
||||
static void set_name(std::string);
|
||||
|
||||
// Make entry point
|
||||
static native_entry make_trampoline(u64(*)(thread_base*));
|
||||
static native_entry make_trampoline(u64(*entry)(thread_base* _base));
|
||||
|
||||
friend class thread_ctrl;
|
||||
|
||||
|
||||
+260
-61
@@ -9,6 +9,8 @@
|
||||
#include "util/endian.hpp"
|
||||
#include "util/asm.hpp"
|
||||
|
||||
#include <charconv>
|
||||
|
||||
LOG_CHANNEL(patch_log, "PAT");
|
||||
|
||||
template <>
|
||||
@@ -38,6 +40,10 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
||||
{
|
||||
case patch_type::invalid: return "invalid";
|
||||
case patch_type::alloc: return "alloc";
|
||||
case patch_type::code_alloc: return "calloc";
|
||||
case patch_type::jump: return "jump";
|
||||
case patch_type::jump_link: return "jumpl";
|
||||
case patch_type::jump_func: return "jumpf";
|
||||
case patch_type::load: return "load";
|
||||
case patch_type::byte: return "byte";
|
||||
case patch_type::le16: return "le16";
|
||||
@@ -61,12 +67,6 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
||||
|
||||
patch_engine::patch_engine()
|
||||
{
|
||||
const std::string patches_path = get_patches_path();
|
||||
|
||||
if (!fs::create_path(patches_path))
|
||||
{
|
||||
patch_log.fatal("Failed to create path: %s (%s)", patches_path, fs::g_tls_error);
|
||||
}
|
||||
}
|
||||
|
||||
std::string patch_engine::get_patch_config_path()
|
||||
@@ -174,6 +174,14 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
continue;
|
||||
}
|
||||
|
||||
if (main_key.empty())
|
||||
{
|
||||
append_log_message(log_messages, "Error: Skipping empty key");
|
||||
patch_log.error("Skipping empty key (file: %s)", path);
|
||||
is_valid = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip Anchors
|
||||
if (main_key == patch_key::anchors)
|
||||
{
|
||||
@@ -182,8 +190,8 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
|
||||
// Find or create an entry matching the key/hash in our map
|
||||
auto& container = patches_map[main_key];
|
||||
container.hash = main_key;
|
||||
container.version = version;
|
||||
container.hash = main_key;
|
||||
container.version = version;
|
||||
|
||||
// Go through each patch
|
||||
for (auto patches_entry : pair.second)
|
||||
@@ -221,10 +229,18 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
{
|
||||
const std::string& title = game_node.first.Scalar();
|
||||
|
||||
if (title.empty())
|
||||
{
|
||||
append_log_message(log_messages, fmt::format("Error: Empty game title (key: %s, file: %s)", main_key, path));
|
||||
patch_log.error("Empty game title (key: %s, file: %s)", main_key, path);
|
||||
is_valid = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto yml_type = game_node.second.Type(); yml_type != YAML::NodeType::Map)
|
||||
{
|
||||
append_log_message(log_messages, fmt::format("Error: Skipping %s: expected Map, found %s (patch: %s, key: %s)", title, yml_type, description, main_key));
|
||||
patch_log.error("Skipping %s: expected Map, found %s (patch: %s, key: %s, file: %s)", title, yml_type, description, main_key, path);
|
||||
append_log_message(log_messages, fmt::format("Error: Skipping game %s: expected Map, found %s (patch: %s, key: %s)", title, yml_type, description, main_key));
|
||||
patch_log.error("Skipping game %s: expected Map, found %s (patch: %s, key: %s, file: %s)", title, yml_type, description, main_key, path);
|
||||
is_valid = false;
|
||||
continue;
|
||||
}
|
||||
@@ -235,7 +251,14 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
{
|
||||
const std::string& serial = serial_node.first.Scalar();
|
||||
|
||||
if (serial == patch_key::all)
|
||||
if (serial.empty())
|
||||
{
|
||||
append_log_message(log_messages, fmt::format("Error: Using empty serial (title: %s, patch: %s, key: %s)", title, description, main_key));
|
||||
patch_log.error("Using empty serial (title: %s, patch: %s, key: %s, file: %s)", title, description, main_key, path);
|
||||
is_valid = false;
|
||||
continue;
|
||||
}
|
||||
else if (serial == patch_key::all)
|
||||
{
|
||||
if (!title_is_all_key)
|
||||
{
|
||||
@@ -361,11 +384,11 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
patch_type patch_engine::get_patch_type(YAML::Node node)
|
||||
patch_type patch_engine::get_patch_type(const std::string& text)
|
||||
{
|
||||
u64 type_val = 0;
|
||||
|
||||
if (!node || !node.IsScalar() || !cfg::try_to_enum_value(&type_val, &fmt_class_string<patch_type>::format, node.Scalar()))
|
||||
if (!cfg::try_to_enum_value(&type_val, &fmt_class_string<patch_type>::format, text))
|
||||
{
|
||||
return patch_type::invalid;
|
||||
}
|
||||
@@ -373,6 +396,16 @@ patch_type patch_engine::get_patch_type(YAML::Node node)
|
||||
return static_cast<patch_type>(type_val);
|
||||
}
|
||||
|
||||
patch_type patch_engine::get_patch_type(YAML::Node node)
|
||||
{
|
||||
if (!node || !node.IsScalar())
|
||||
{
|
||||
return patch_type::invalid;
|
||||
}
|
||||
|
||||
return get_patch_type(node.Scalar());
|
||||
}
|
||||
|
||||
bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifier, const YAML::Node& root, std::stringstream* log_messages)
|
||||
{
|
||||
if (!node || !node.IsSequence())
|
||||
@@ -434,6 +467,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
|
||||
switch (p_data.type)
|
||||
{
|
||||
case patch_type::utf8:
|
||||
case patch_type::jump_func:
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -516,10 +550,21 @@ void patch_engine::append_title_patches(const std::string& title_id)
|
||||
}
|
||||
|
||||
void ppu_register_range(u32 addr, u32 size);
|
||||
bool ppu_form_branch_to_code(u32 entry, u32 target, bool link = false, bool with_toc = false, std::string module_name = {});
|
||||
u32 ppu_generate_id(std::string_view name);
|
||||
|
||||
static std::basic_string<u32> apply_modification(const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
|
||||
void unmap_vm_area(std::shared_ptr<vm::block_t>& ptr)
|
||||
{
|
||||
std::basic_string<u32> applied;
|
||||
if (ptr && ptr->flags & (1ull << 62))
|
||||
{
|
||||
vm::unmap(0, true, &ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns old 'applied' size
|
||||
static usz apply_modification(std::basic_string<u32>& applied, const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
|
||||
{
|
||||
const usz old_applied_size = applied.size();
|
||||
|
||||
for (const auto& p : patch.data_list)
|
||||
{
|
||||
@@ -531,26 +576,24 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
|
||||
const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value) + alloc_at % 4096, 4096);
|
||||
|
||||
// Allocate map if needed, if allocated flags will indicate that bit 62 is set (unique identifier)
|
||||
auto alloc_map = vm::reserve_map(vm::any, alloc_at & -0x10000, utils::align(alloc_size, 0x10000), vm::page_size_64k | vm::preallocated | vm::bf0_0x2 | (1ull << 62));
|
||||
auto alloc_map = vm::reserve_map(vm::any, alloc_at & -0x10000, utils::align(alloc_size, 0x10000), vm::page_size_64k | vm::preallocated | (1ull << 62));
|
||||
|
||||
u64 flags = 0;
|
||||
u64 flags = vm::alloc_unwritable;
|
||||
|
||||
switch (p.offset % patch_engine::mem_protection::mask)
|
||||
{
|
||||
case patch_engine::mem_protection::wx: flags |= vm::page_writable + vm::page_readable + vm::page_executable; break;
|
||||
case patch_engine::mem_protection::ro: flags |= vm::page_readable; break;
|
||||
case patch_engine::mem_protection::rx: flags |= vm::page_writable + vm::page_executable; break;
|
||||
case patch_engine::mem_protection::rw: flags |= vm::page_writable + vm::page_readable; break;
|
||||
case patch_engine::mem_protection::wx: flags = vm::alloc_executable; break;
|
||||
case patch_engine::mem_protection::ro: break;
|
||||
case patch_engine::mem_protection::rx: flags |= vm::alloc_executable; break;
|
||||
case patch_engine::mem_protection::rw: flags &= ~vm::alloc_unwritable; break;
|
||||
default: ensure(false);
|
||||
}
|
||||
|
||||
if (alloc_map)
|
||||
{
|
||||
if (alloc_map->falloc(alloc_at, alloc_size))
|
||||
if ((p.alloc_addr = alloc_map->falloc(alloc_at, alloc_size, nullptr, flags)))
|
||||
{
|
||||
vm::page_protect(alloc_at, alloc_size, 0, flags, flags ^ (vm::page_writable + vm::page_readable + vm::page_executable));
|
||||
|
||||
if (flags & vm::page_executable)
|
||||
if (flags & vm::alloc_executable)
|
||||
{
|
||||
ppu_register_range(alloc_at, alloc_size);
|
||||
}
|
||||
@@ -560,45 +603,41 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
|
||||
}
|
||||
|
||||
// Revert if allocated map before failure
|
||||
if (alloc_map->flags & (1ull << 62))
|
||||
{
|
||||
vm::unmap(vm::any, alloc_map->addr);
|
||||
}
|
||||
unmap_vm_area(alloc_map);
|
||||
}
|
||||
}
|
||||
|
||||
// Revert in case of failure
|
||||
for (u32 index : applied)
|
||||
std::for_each(applied.begin() + old_applied_size, applied.end(), [&](u32 index)
|
||||
{
|
||||
const u32 addr = patch.data_list[index].offset & -4096;
|
||||
const u32 addr = std::exchange(patch.data_list[index].alloc_addr, 0);
|
||||
|
||||
// Try different alignments until works
|
||||
if (!vm::dealloc(addr))
|
||||
{
|
||||
if (!vm::dealloc(addr & -0x10000))
|
||||
{
|
||||
vm::dealloc(addr & -0x100000);
|
||||
}
|
||||
}
|
||||
vm::dealloc(addr);
|
||||
|
||||
if (auto alloc_map = vm::get(vm::any, addr); alloc_map->flags & (1ull << 62))
|
||||
{
|
||||
vm::unmap(vm::any, alloc_map->addr);
|
||||
}
|
||||
}
|
||||
auto alloc_map = vm::get(vm::any, addr);
|
||||
unmap_vm_area(alloc_map);
|
||||
});
|
||||
|
||||
applied.clear();
|
||||
return applied;
|
||||
applied.resize(old_applied_size);
|
||||
return old_applied_size;
|
||||
}
|
||||
|
||||
// Fixup values from before
|
||||
std::fill(applied.begin(), applied.end(), u32{umax});
|
||||
std::fill(applied.begin() + old_applied_size, applied.end(), u32{umax});
|
||||
|
||||
u32 relocate_instructions_at = 0;
|
||||
|
||||
for (const auto& p : patch.data_list)
|
||||
{
|
||||
u32 offset = p.offset;
|
||||
|
||||
if (offset < min_addr || offset - min_addr >= filesz)
|
||||
if (relocate_instructions_at && vm::read32(relocate_instructions_at) != 0x6000'0000u)
|
||||
{
|
||||
// No longer points a NOP to be filled, meaning we ran out of instructions
|
||||
relocate_instructions_at = 0;
|
||||
}
|
||||
|
||||
if (!relocate_instructions_at && (offset < min_addr || offset - min_addr >= filesz))
|
||||
{
|
||||
// This patch is out of range for this segment
|
||||
continue;
|
||||
@@ -608,6 +647,13 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
|
||||
|
||||
auto ptr = dst + offset;
|
||||
|
||||
if (relocate_instructions_at)
|
||||
{
|
||||
offset = relocate_instructions_at;
|
||||
ptr = vm::get_super_ptr<u8>(relocate_instructions_at);
|
||||
relocate_instructions_at += 4; // Advance to the next instruction on dynamic memory
|
||||
}
|
||||
|
||||
u32 resval = umax;
|
||||
|
||||
switch (p.type)
|
||||
@@ -623,6 +669,135 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
|
||||
// Applied before
|
||||
continue;
|
||||
}
|
||||
case patch_type::code_alloc:
|
||||
{
|
||||
relocate_instructions_at = 0;
|
||||
|
||||
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
|
||||
|
||||
// Allow only if points to a PPU executable instruction
|
||||
if (out_branch < 0x10000 || out_branch >= 0x4000'0000 || !vm::check_addr<4>(out_branch, vm::page_executable))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value + 1) * 4, 0x10000);
|
||||
|
||||
// Always executable
|
||||
u64 flags = vm::alloc_executable | vm::alloc_unwritable;
|
||||
|
||||
switch (p.offset % patch_engine::mem_protection::mask)
|
||||
{
|
||||
case patch_engine::mem_protection::rw:
|
||||
case patch_engine::mem_protection::wx:
|
||||
{
|
||||
flags &= ~vm::alloc_unwritable;
|
||||
break;
|
||||
}
|
||||
case patch_engine::mem_protection::ro:
|
||||
case patch_engine::mem_protection::rx:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default: ensure(false);
|
||||
}
|
||||
|
||||
const auto alloc_map = ensure(vm::get(vm::any, out_branch));
|
||||
|
||||
// Range allowed for absolute branches to operate at
|
||||
// It takes into account that we need to put a branch for return at the end of memory space
|
||||
const u32 addr = p.alloc_addr = alloc_map->alloc(alloc_size, nullptr, 0x10000, flags);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
patch_log.error("Failed to allocate 0x%x bytes for code (entry=0x%x)", alloc_size, addr, out_branch);
|
||||
continue;
|
||||
}
|
||||
|
||||
patch_log.success("Allocated 0x%x for code at 0x%x (entry=0x%x)", alloc_size, addr, out_branch);
|
||||
|
||||
// NOP filled
|
||||
std::fill_n(vm::get_super_ptr<u32>(addr), p.value.long_value, 0x60000000);
|
||||
|
||||
// Register code
|
||||
ppu_register_range(addr, alloc_size);
|
||||
|
||||
// Write branch to code
|
||||
ppu_form_branch_to_code(out_branch, addr);
|
||||
resval = out_branch & -4;
|
||||
|
||||
// Write address of the allocated memory to the code entry
|
||||
*vm::get_super_ptr<u32>(resval) = addr;
|
||||
|
||||
// Write branch to return to code
|
||||
ppu_form_branch_to_code(addr + static_cast<u32>(p.value.long_value) * 4, resval + 4);
|
||||
relocate_instructions_at = addr;
|
||||
break;
|
||||
}
|
||||
case patch_type::jump:
|
||||
case patch_type::jump_link:
|
||||
{
|
||||
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
|
||||
const u32 dest = static_cast<u32>(p.value.long_value);
|
||||
|
||||
// Allow only if points to a PPU executable instruction
|
||||
if (!ppu_form_branch_to_code(out_branch, dest, p.type == patch_type::jump_link))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
resval = out_branch & -4;
|
||||
break;
|
||||
}
|
||||
case patch_type::jump_func:
|
||||
{
|
||||
const std::string& str = p.original_value;
|
||||
|
||||
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
|
||||
const usz sep_pos = str.find_first_of(':');
|
||||
|
||||
// Must contain only a single ':' or none
|
||||
// If ':' is found: Left string is the module name, right string is the function name
|
||||
// If ':' is not found: The entire string is a direct address of the function's descriptor in hexadecimal
|
||||
if (str.size() <= 2 || !sep_pos || sep_pos == str.size() - 1 || sep_pos != str.find_last_of(":"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string_view func_name{std::string_view(str).substr(sep_pos + 1)};
|
||||
u32 id = 0;
|
||||
|
||||
if (func_name.starts_with("0x"sv))
|
||||
{
|
||||
// Raw hexadeciaml-formatted FNID (real function name cannot contain a digit at the start, derived from C/CPP which were used in PS3 development)
|
||||
const auto result = std::from_chars(func_name.data() + 2, func_name.data() + func_name.size() - 2, id, 16);
|
||||
|
||||
if (result.ec != std::errc() || str.data() + sep_pos != result.ptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sep_pos == umax)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate FNID using function name
|
||||
id = ppu_generate_id(func_name);
|
||||
}
|
||||
|
||||
// Allow only if points to a PPU executable instruction
|
||||
// FNID/OPD-address is placed at target
|
||||
if (!ppu_form_branch_to_code(out_branch, id, true, true, std::string{str.data(), sep_pos != umax ? sep_pos : 0}))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
resval = out_branch & -4;
|
||||
break;
|
||||
}
|
||||
case patch_type::byte:
|
||||
{
|
||||
*ptr = static_cast<u8>(p.value.long_value);
|
||||
@@ -721,7 +896,7 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
|
||||
applied.push_back(resval);
|
||||
}
|
||||
|
||||
return applied;
|
||||
return old_applied_size;
|
||||
}
|
||||
|
||||
std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32 filesz, u32 min_addr)
|
||||
@@ -812,11 +987,12 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
|
||||
// Apply modifications sequentially
|
||||
auto apply_func = [&](const patch_info& patch)
|
||||
{
|
||||
auto applied = apply_modification(patch, dst, filesz, min_addr);
|
||||
const usz old_size = apply_modification(applied_total, patch, dst, filesz, min_addr);
|
||||
|
||||
applied_total += applied;
|
||||
|
||||
patch_log.success("Applied patch (hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s') (<- %u)", patch.hash, patch.description, patch.author, patch.patch_version, patch.version, applied.size());
|
||||
if (applied_total.size() != old_size)
|
||||
{
|
||||
patch_log.success("Applied patch (hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s') (<- %u)", patch.hash, patch.description, patch.author, patch.patch_version, patch.version, applied_total.size() - old_size);
|
||||
}
|
||||
};
|
||||
|
||||
// Sort specific patches after global patches
|
||||
@@ -858,18 +1034,36 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
|
||||
return applied_total;
|
||||
}
|
||||
|
||||
void patch_engine::unload(const std::string& name)
|
||||
{
|
||||
if (m_map.find(name) == m_map.cend())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& container = m_map.at(name);
|
||||
|
||||
for (const auto& [description, patch] : container.patch_info_map)
|
||||
{
|
||||
for (auto& entry : patch.data_list)
|
||||
{
|
||||
// Deallocate used memory
|
||||
if (u32 addr = std::exchange(entry.alloc_addr, 0))
|
||||
{
|
||||
vm::dealloc(addr);
|
||||
|
||||
auto alloc_map = vm::get(vm::any, addr);
|
||||
unmap_vm_area(alloc_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void patch_engine::save_config(const patch_map& patches_map)
|
||||
{
|
||||
const std::string path = get_patch_config_path();
|
||||
patch_log.notice("Saving patch config file %s", path);
|
||||
|
||||
fs::file file(path, fs::rewrite);
|
||||
if (!file)
|
||||
{
|
||||
patch_log.fatal("Failed to open patch config file %s (%s)", path, fs::g_tls_error);
|
||||
return;
|
||||
}
|
||||
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginMap;
|
||||
|
||||
@@ -933,7 +1127,12 @@ void patch_engine::save_config(const patch_map& patches_map)
|
||||
|
||||
out << YAML::EndMap;
|
||||
|
||||
file.write(out.c_str(), out.size());
|
||||
fs::pending_file file(path);
|
||||
|
||||
if (!file.file || (file.file.write(out.c_str(), out.size()), !file.commit()))
|
||||
{
|
||||
patch_log.error("Failed to create patch config file %s (%s)", path, fs::g_tls_error);
|
||||
}
|
||||
}
|
||||
|
||||
static void append_patches(patch_engine::patch_map& existing_patches, const patch_engine::patch_map& new_patches, usz& count, usz& total, std::stringstream* log_messages)
|
||||
|
||||
@@ -27,6 +27,10 @@ enum class patch_type
|
||||
invalid,
|
||||
load,
|
||||
alloc, // Allocate memory at address (zeroized executable memory)
|
||||
code_alloc,// Allocate memory somewhere, saves branch to memory at specfied address (filled with PPU NOP and branch for returning)
|
||||
jump, // Install special 32-bit jump instruction (PPU only atm)
|
||||
jump_link, // jump + set link (PPU only atm)
|
||||
jump_func, // jump to exported function (PPU only, forever)
|
||||
byte,
|
||||
le16,
|
||||
le32,
|
||||
@@ -56,6 +60,7 @@ public:
|
||||
u64 long_value;
|
||||
f64 double_value;
|
||||
} value{0};
|
||||
mutable u32 alloc_addr = 0; // Used to save optional allocation address (if occured)
|
||||
};
|
||||
|
||||
using patch_app_versions = std::unordered_map<std::string /*app_version*/, bool /*enabled*/>;
|
||||
@@ -118,6 +123,9 @@ public:
|
||||
// Read and add a patch node to the patch info
|
||||
static bool read_patch_node(patch_info& info, YAML::Node node, const YAML::Node& root, std::stringstream* log_messages = nullptr);
|
||||
|
||||
// Get the patch type from a string
|
||||
static patch_type get_patch_type(const std::string& text);
|
||||
|
||||
// Get the patch type of a patch node
|
||||
static patch_type get_patch_type(YAML::Node node);
|
||||
|
||||
@@ -148,6 +156,9 @@ public:
|
||||
// Apply patch (returns the number of entries applied)
|
||||
std::basic_string<u32> apply(const std::string& name, u8* dst, u32 filesz = -1, u32 min_addr = 0);
|
||||
|
||||
// Deallocate memory used by patches
|
||||
void unload(const std::string& name);
|
||||
|
||||
private:
|
||||
// Database
|
||||
patch_map m_map{};
|
||||
|
||||
+2
-2
@@ -35,13 +35,13 @@ jobs:
|
||||
displayName: ccache
|
||||
|
||||
- bash: |
|
||||
docker pull --quiet rpcs3/rpcs3-ci-bionic:1.1
|
||||
docker pull --quiet rpcs3/rpcs3-ci-bionic:1.3
|
||||
docker run \
|
||||
-v $(pwd):/rpcs3 \
|
||||
--env-file .ci/docker.env \
|
||||
-v $CCACHE_DIR:/root/.ccache \
|
||||
-v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \
|
||||
rpcs3/rpcs3-ci-bionic:1.1 \
|
||||
rpcs3/rpcs3-ci-bionic:1.3 \
|
||||
/rpcs3/.ci/build-linux.sh
|
||||
displayName: Docker setup and build
|
||||
|
||||
|
||||
Binary file not shown.
@@ -2,8 +2,7 @@
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:throwingNew- /constexpr:steps16777216 /D _CRT_SECURE_NO_DEPRECATE=1 /D _CRT_NON_CONFORMING_SWPRINTFS=1 /D _SCL_SECURE_NO_WARNINGS=1")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D NOMINMAX /D _ENABLE_EXTENDED_ALIGNED_STORAGE=1 /D _HAS_EXCEPTIONS=0 /MT")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D NOMINMAX /D _ENABLE_EXTENDED_ALIGNED_STORAGE=1 /D _HAS_EXCEPTIONS=0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /DYNAMICBASE:NO /BASE:0x10000 /FIXED")
|
||||
|
||||
#TODO: Some of these could be cleaned up
|
||||
|
||||
@@ -66,7 +66,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (static)", "3rdp
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "7zlib", "3rdparty\7z\7zlib.vcxproj", "{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wolfssl", "3rdparty\wolfssl.vcxproj", "{73973223-5EE8-41CA-8E88-1D60E89A237B}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wolfssl", "3rdparty\wolfssl\wolfssl.vcxproj", "{73973223-5EE8-41CA-8E88-1D60E89A237B}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcurl", "3rdparty\curl\libcurl.vcxproj", "{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
|
||||
+41
-2
@@ -151,9 +151,48 @@ elseif(UNIX)
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/bin/git $<TARGET_FILE_DIR:rpcs3>/git)
|
||||
elseif(WIN32)
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# this is a single-config generator workaround
|
||||
# multi-config generator will fail.
|
||||
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# offical Qt binaries are built with -MD(d) only as stated in offical wiki
|
||||
# https://wiki.qt.io/Technical_FAQ#Why_does_a_statically_built_Qt_use_the_dynamic_Visual_Studio_runtime_libraries_.3F_Do_I_need_to_deploy_those_with_my_application_.3F
|
||||
# If we build our libs with /MT(d), we might encounter some issues.
|
||||
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
|
||||
|
||||
# Qt installed from Qt installer has following hierarchy:
|
||||
# bin/ for release and debug dlls and windeployqt tools
|
||||
# lib/cmake/Qt5/ for Qt5_Dir
|
||||
# Qt installed from vcpkg has following hierarchy:
|
||||
# bin/ for release dlls
|
||||
# debug/bin/ for debug dlls
|
||||
# tools/qt5/bin/ for tools including windeployqt
|
||||
# tools/qt5/debug/bin/ for tools with debug build including windeployqt
|
||||
# share/cmake/Qt5/ for Qt5_Dir
|
||||
|
||||
# If Qt5 is installed from official Qt installer
|
||||
list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../bin/")
|
||||
|
||||
# If Qt5 is installed from vcpkg
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../tools/qt5/debug/bin/")
|
||||
else()
|
||||
list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../tools/qt5/bin/")
|
||||
endif()
|
||||
|
||||
find_program(_WINDEPLOYQT
|
||||
NAMES windeployqt
|
||||
PATHS ${_QT5_TOOLS_PATHS}
|
||||
REQUIRED)
|
||||
message("Use windeployqt in ${_WINDEPLOYQT}")
|
||||
|
||||
add_custom_command(TARGET rpcs3 POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$<TARGET_FILE_DIR:rpcs3>"
|
||||
COMMAND "${Qt5_DIR}/../../../bin/windeployqt" --no-angle --no-compiler-runtime --no-opengl-sw --no-patchqt --no-svg --no-translations --no-quick --plugindir "$<TARGET_FILE_DIR:rpcs3>/qt/plugins" --release "$<TARGET_FILE:rpcs3>")
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$<TARGET_FILE_DIR:rpcs3>"
|
||||
# If Qt5 is installed from vcpkg, add binary path to PATH
|
||||
# otherwise windeployqt tool won't be able to locate necessary dlls
|
||||
COMMAND set PATH=${Qt5_DIR}/../../../$<$<CONFIG:Debug>:debug/>bin/$<SEMICOLON>%PATH%
|
||||
COMMAND "${_WINDEPLOYQT}" --no-angle --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --plugindir "$<TARGET_FILE_DIR:rpcs3>/qt/plugins" $<$<CONFIG:Debug>:--debug> $<$<CONFIG:Release>:--release> "$<TARGET_FILE:rpcs3>")
|
||||
endif()
|
||||
|
||||
# Unix installation
|
||||
|
||||
@@ -33,6 +33,37 @@ struct SELF_KEY
|
||||
SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::string& e, const std::string& r, const std::string& pb, const std::string& pr, u32 ct);
|
||||
};
|
||||
|
||||
constexpr u32 PASSPHRASE_KEY_LEN = 16;
|
||||
constexpr u32 PASSPHRASE_OUT_LEN = 4096;
|
||||
|
||||
constexpr u8 SC_ISO_SERIES_KEY_1[PASSPHRASE_KEY_LEN] = {
|
||||
0xD4, 0x13, 0xB8, 0x96, 0x63, 0xE1, 0xFE, 0x9F, 0x75, 0x14, 0x3D, 0x3B, 0xB4, 0x56, 0x52, 0x74 // D413B89663E1FE9F75143D3BB4565274
|
||||
};
|
||||
|
||||
constexpr u8 SC_ISO_SERIES_KEY_2[PASSPHRASE_KEY_LEN] = {
|
||||
0xFA, 0x72, 0xCE, 0xEF, 0x59, 0xB4, 0xD2, 0x98, 0x9F, 0x11, 0x19, 0x13, 0x28, 0x7F, 0x51, 0xC7 // FA72CEEF59B4D2989F111913287F51C7
|
||||
};
|
||||
|
||||
constexpr u8 SC_KEY_FOR_MASTER_1[PASSPHRASE_KEY_LEN] = {
|
||||
0xDA, 0xA4, 0xB9, 0xF2, 0xBC, 0x70, 0xB2, 0x80, 0xA7, 0xB3, 0x40, 0xFA, 0x0D, 0x04, 0xBA, 0x14 // DAA4B9F2BC70B280A7B340FA0D04BA14
|
||||
};
|
||||
|
||||
constexpr u8 SC_KEY_FOR_MASTER_2[PASSPHRASE_KEY_LEN] = {
|
||||
0x29, 0xC1, 0x94, 0xFF, 0xEC, 0x1F, 0xD1, 0x4D, 0x4A, 0xAE, 0x00, 0x6C, 0x32, 0xB3, 0x59, 0x90 // 29C194FFEC1FD14D4AAE006C32B35990
|
||||
};
|
||||
|
||||
constexpr u8 SC_ISO_SERIES_INTERNAL_KEY_1[PASSPHRASE_KEY_LEN] = {
|
||||
0x73, 0x63, 0x6B, 0x65, 0x79, 0x5F, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x6B, 0x65, 0x79, 0x00 // 73636B65795F7365726965736B657900
|
||||
};
|
||||
|
||||
constexpr u8 SC_ISO_SERIES_INTERNAL_KEY_2[PASSPHRASE_KEY_LEN] = {
|
||||
0x73, 0x63, 0x6B, 0x65, 0x79, 0x5F, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x6B, 0x65, 0x79, 0x32 // 73636B65795F7365726965736B657932
|
||||
};
|
||||
|
||||
constexpr u8 SC_ISO_SERIES_INTERNAL_KEY_3[PASSPHRASE_KEY_LEN] = {
|
||||
0x73, 0x63, 0x6B, 0x65, 0x79, 0x5F, 0x66, 0x6F, 0x72, 0x5F, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72 // 73636B65795F666F725F6D6173746572
|
||||
};
|
||||
|
||||
constexpr u8 PKG_AES_KEY_IDU[0x10] = {
|
||||
0x5d, 0xb9, 0x11, 0xe6, 0xb7, 0xe5, 0x0a, 0x7d, 0x32, 0x15, 0x38, 0xfd, 0x7c, 0x66, 0xf1, 0x7b
|
||||
};
|
||||
@@ -180,6 +211,104 @@ constexpr u8 PUP_KEY[0x40] = {
|
||||
0x30, 0xCE, 0x83, 0x66
|
||||
};
|
||||
|
||||
// name; location; notes
|
||||
constexpr s64 PAID_01 = 0x0003CD28CB47D3C1L; // spu_token_processor.self; CoreOS; Only for 2E - 083.007
|
||||
constexpr s64 PAID_02 = 0x1010000001000001L; // vsh / games / utilities; /dev_flash/, cell_root/target/images; only for 2E - 080.006
|
||||
constexpr s64 PAID_03 = 0x1010000001000003L; // retail games and their updates
|
||||
constexpr s64 PAID_04 = 0x1010000002000003L;
|
||||
constexpr s64 PAID_05 = 0x1020000401000001L; // ps2emu; /dev_flash/ps2emu; CEX DEX DECR ?
|
||||
constexpr s64 PAID_06 = 0x1050000003000001L; // lv2_kernel.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_07 = 0x1070000001000002L; // onicore_child.self; /dev_flash/vsh/module; same for CEX DEX DECR
|
||||
constexpr s64 PAID_08 = 0x1070000002000002L; // mcore.self; /dev_flash/vsh/module; same for CEX DEX DECR
|
||||
constexpr s64 PAID_09 = 0x1070000003000002L; // mgvideo.self; /dev_flash/vsh/module; same for CEX DEX DECR
|
||||
constexpr s64 PAID_10 = 0x1070000004000002L; // swagner / swreset; /dev_flash/vsh/module; DTCP-IP DRM modules
|
||||
constexpr s64 PAID_11 = 0x107000000E000001L; // vtrm_server.fself; lv1
|
||||
constexpr s64 PAID_12 = 0x107000000F000001L; // update_manager_server.fself; lv1
|
||||
constexpr s64 PAID_13 = 0x1070000010000001L; // sc_manager_server.fself; lv1
|
||||
constexpr s64 PAID_14 = 0x1070000011000001L; // secure_rtc_server.fself; lv1
|
||||
constexpr s64 PAID_15 = 0x1070000012000001L; // spm_server.fself; lv1
|
||||
constexpr s64 PAID_16 = 0x1070000013000001L; // sb_manager_server.fself; lv1
|
||||
constexpr s64 PAID_17 = 0x1070000014000001L; // framework.fself; lv1
|
||||
constexpr s64 PAID_18 = 0x1070000015000001L; // lv2_loader.fself; lv1
|
||||
constexpr s64 PAID_19 = 0x1070000016000001L; // profile_loader.fself; lv1
|
||||
constexpr s64 PAID_20 = 0x1070000017000001L; // ss_init.fself; lv1
|
||||
constexpr s64 PAID_21 = 0x1070000018000001L; // individual_info_mgr_server.fself; lv1
|
||||
constexpr s64 PAID_22 = 0x1070000019000001L; // app_info_manager_server.fself; lv1
|
||||
constexpr s64 PAID_23 = 0x107000001A000001L; // ss_sc_init_pu.fself; JIG lv1 proc
|
||||
constexpr s64 PAID_24 = 0x107000001C000001L; // updater_frontend.fself; lv1
|
||||
constexpr s64 PAID_25 = 0x107000001D000001L; // sysmgr_ss.fself; lv1
|
||||
constexpr s64 PAID_26 = 0x107000001F000001L; // sb_iso_spu_module.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_27 = 0x1070000020000001L; // sc_iso.self / sc_iso_factory.self; CoreOS / [2.43 JIG PUP]; same for CEX DEX DECR
|
||||
constexpr s64 PAID_28 = 0x1070000021000001L; // spp_verifier.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_29 = 0x1070000022000001L; // spu_pkg_rvk_verifier.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_30 = 0x1070000023000001L; // spu_token_processor.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_31 = 0x1070000024000001L; // sv_iso_spu_module.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_32 = 0x1070000025000001L; // aim_spu_module.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_33 = 0x1070000026000001L; // ss_sc_init.self; [2.43 JIG PUP]
|
||||
constexpr s64 PAID_34 = 0x1070000027000001L; // dispatcher.fself; lv1;
|
||||
constexpr s64 PAID_35 = 0x1070000028000001L; // factory_data_mngr_server.fself; JIG lv1 proc
|
||||
constexpr s64 PAID_36 = 0x1070000029000001L; // fdm_spu_module.self; [2.43 JIG PUP]
|
||||
constexpr s64 PAID_37 = 0x107000002A000001L;
|
||||
constexpr s64 PAID_38 = 0x1070000031000001L;
|
||||
constexpr s64 PAID_39 = 0x1070000032000001L; // ss_server1.fself; lv1
|
||||
constexpr s64 PAID_40 = 0x1070000033000001L; // ss_server2.fself; lv1
|
||||
constexpr s64 PAID_41 = 0x1070000034000001L; // ss_server3.fself; lv1
|
||||
constexpr s64 PAID_42 = 0x1070000037000001L; // mc_iso_spu_module.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_43 = 0x1070000039000001L; // bdp_bdmv.self; /dev_flash/bdplayer
|
||||
constexpr s64 PAID_44 = 0x107000003A000001L; // bdj.self; /dev_flash/bdplayer
|
||||
constexpr s64 PAID_45 = 0x1070000040000001L; // sys/external modules; /dev_flash/sys/external; same for CEX DEX DECR (incl. liblv2dbg_for_cex.sprx + liblv2dbg_for_dex.sprx)
|
||||
constexpr s64 PAID_46 = 0x1070000041000001L; // ps1emu; /dev_flash/ps1emu; CEX DEX DECR ?
|
||||
constexpr s64 PAID_47 = 0x1070000043000001L; // me_iso_spu_module.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_48 = 0x1070000044000001L; // (related to usb dongle)
|
||||
constexpr s64 PAID_49 = 0x1070000045000001L; // USB Dongle Authenticator; ss_server1.fself; same for CEX DEX DECR
|
||||
constexpr s64 PAID_50 = 0x1070000046000001L; // spu_mode_auth.self; [2.43 JIG PUP]
|
||||
constexpr s64 PAID_51 = 0x1070000047000001L; // otheros.self; otheros.self
|
||||
constexpr s64 PAID_52 = 0x1070000048000001L; // ftpd; cell_root/target/images; DECR
|
||||
constexpr s64 PAID_53 = 0x107000004C000001L; // spu_utoken_processor.self; CoreOS (since FW 2.40)
|
||||
constexpr s64 PAID_54 = 0x107000004E000001L; // (syscall 878)
|
||||
constexpr s64 PAID_55 = 0x107000004F000001L;
|
||||
constexpr s64 PAID_56 = 0x1070000050000001L;
|
||||
constexpr s64 PAID_57 = 0x1070000051000001L;
|
||||
constexpr s64 PAID_58 = 0x1070000052000001L; // sys/internal CEX + vsh/module modules CEX; /dev_flash/sys/internal + /dev_flash/vsh/module; Differs between CEX (this authid) and DECR
|
||||
constexpr s64 PAID_59 = 0x1070000054000001L; // (syscall 21)
|
||||
constexpr s64 PAID_60 = 0x1070000055000001L; // manu_info_spu_module.self; CoreOS (since FW 3.50)
|
||||
constexpr s64 PAID_61 = 0x1070000058000001L; // me_iso_for_ps2emu.self; CoreOS (since FW 3.70)
|
||||
constexpr s64 PAID_62 = 0x1070000059000001L; // sv_iso_for_ps2emu.self; CoreOS (since FW 3.70)
|
||||
constexpr s64 PAID_63 = 0x1070000300000001L; // Lv2diag.self; BD-remarry toolkit
|
||||
constexpr s64 PAID_64 = 0x10700003FC000001L; // emer_init.self; CoreOS (since FW 2.00)
|
||||
constexpr s64 PAID_65 = 0x10700003FD000001L; // ps3swu; PUP root; same for CEX DEX DECR
|
||||
constexpr s64 PAID_66 = 0x10700003FD000001L; // PS3ToolUpdater; cell_root/target/images; Only DECR
|
||||
constexpr s64 PAID_67 = 0x10700003FD000001L; // manufacturing_updater_for_reset.self; BD-remarry toolkit
|
||||
constexpr s64 PAID_68 = 0x10700003FE000001L; // sys_agent.self DECR; /dev_flash/sys/internal; DECR
|
||||
constexpr s64 PAID_69 = 0x10700003FF000001L; // db_backup, mkfs, mkfs_085, mount_hdd, registry_backup, set_monitor, most sys/internal modules DECR + most vsh/module modules DECR; /dev_flash/sys/internal + /dev_flash/vsh/module + cell_root/target/images; Differs between DECR (this authid) and CEX
|
||||
constexpr s64 PAID_70 = 0x1070000400000001L; // vsh / games / utilities; /dev_flash/, cell_root/target/images; only for 081.003 - 083.007
|
||||
constexpr s64 PAID_71 = 0x1070000409000001L; // psp_emulator.self; /dev_flash/pspemu/psp_emulator.self; CEX DEX DECR ?
|
||||
constexpr s64 PAID_72 = 0x107000040A000001L; // psp_translator.self; /dev_flash/pspemu/psp_translator.self; CEX DEX DECR ?
|
||||
constexpr s64 PAID_73 = 0x107000040B000001L; // emulator_api.sprx and other .sprx; /dev_flash/pspemu/release/; CEX DEX DECR ?
|
||||
constexpr s64 PAID_74 = 0x107000040C000001L; // emulator_drm.sprx; /dev_flash/pspemu/release/emulator_drm.sprx; CEX DEX DECR ?
|
||||
constexpr s64 PAID_75 = 0x107000040C000001L; // libchnnlsv.sprx; /dev_flash/sys/internal/; CEX DEX DECR ?
|
||||
constexpr s64 PAID_76 = 0x107000040D000001L; // ?psp related?; ?/dev_flash/pspemu/release/?; CEX DEX DECR ?
|
||||
constexpr s64 PAID_77 = 0x1070000500000001L; // cellftp; cell_root/target/images/; DECR
|
||||
constexpr s64 PAID_78 = 0x1070000501000001L; // hdd_copy.self; CoreOS (since FW 3.10)
|
||||
constexpr s64 PAID_79 = 0x10700005FC000001L; // sys_audio; /dev_flash/sys/internal; CEX
|
||||
constexpr s64 PAID_80 = 0x10700005FD000001L; // sys_init_osd; /dev_flash/sys/internal; CEX
|
||||
constexpr s64 PAID_81 = 0x10700005FF000001L; // vsh.self; /dev_flash/vsh/; CEX
|
||||
constexpr s64 PAID_82 = 0x1070001002000001L; // PvrRecSvr.sprx; BCJB95006\USRDIR\v320; CEX
|
||||
constexpr s64 PAID_83 = 0x1070200056000001L; // cachemgr.self; WebMAF apps/USRDIR
|
||||
constexpr s64 PAID_84 = 0x1070200057000001L; // EBOOT.BIN.self + .sprx files; WebMAF apps/USRDIR/prx/ps3; Demen_prx.ppu.sprx + WebMAF sprx files
|
||||
constexpr s64 PAID_85 = 0x1FF0000001000001L; // lv0; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_86 = 0x1FF0000002000001L; // lv1.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_87 = 0x1FF0000008000001L; // lv1ldr; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_88 = 0x1FF0000009000001L; // lv2ldr; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_89 = 0x1FF000000A000001L; // isoldr; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_90 = 0x1FF000000B000001L; // rvkldr; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_91 = 0x1FF000000C000001L; // appldr; CoreOS; same for CEX DEX DECR
|
||||
|
||||
constexpr s64 LAID_1 = 0x1070000001000001L; // (= HV processes / SCE_CELLOS_PME); flash and vflash
|
||||
constexpr s64 LAID_2 = 0x1070000002000001L; // (= GameOS / PS3_LPAR); flash and vflash
|
||||
constexpr s64 LAID_3 = 0x1020000003000001L; // (= PS2_LPAR / PS2_GX_LPAR / PS2_SW_LPAR / PS2_NE_LPAR); (used in ps3vflashc region inside vflash in NOR consoles, and ps3db region)... dev_flash and dev_hdd0 regions
|
||||
constexpr s64 LAID_4 = 0x1080000004000001L; // (= LINUX_LPAR); (used in ps3vflashf region inside vflash in NOR consoles)... otheros bootloader region
|
||||
|
||||
class KeyVault
|
||||
{
|
||||
std::vector<SELF_KEY> sk_LV0_arr{};
|
||||
|
||||
+27
-33
@@ -6,6 +6,7 @@
|
||||
#include "ec.h"
|
||||
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include <cmath>
|
||||
|
||||
#include "util/asm.hpp"
|
||||
@@ -649,7 +650,7 @@ void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& E
|
||||
EDAT.file_size = swap64(*reinterpret_cast<u64*>(&edat_header[8]));
|
||||
}
|
||||
|
||||
bool extract_all_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, unsigned char* rifkey, bool verbose)
|
||||
bool extract_all_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, bool verbose)
|
||||
{
|
||||
// Setup NPD and EDAT/SDAT structs.
|
||||
NPD_HEADER NPD;
|
||||
@@ -658,8 +659,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
// Read in the NPD and EDAT/SDAT headers.
|
||||
read_npd_edat_header(input, NPD, EDAT);
|
||||
|
||||
unsigned char npd_magic[4] = {0x4E, 0x50, 0x44, 0x00}; //NPD0
|
||||
if (memcmp(&NPD.magic, npd_magic, 4))
|
||||
if (NPD.magic != "NPD\0"_u32)
|
||||
{
|
||||
edat_log.error("%s has invalid NPD header or already decrypted.", input_file_name);
|
||||
return true;
|
||||
@@ -671,6 +671,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
edat_log.notice("NPD version: %d", NPD.version);
|
||||
edat_log.notice("NPD license: %d", NPD.license);
|
||||
edat_log.notice("NPD type: %d", NPD.type);
|
||||
edat_log.notice("NPD content_id: %s", NPD.content_id);
|
||||
}
|
||||
|
||||
// Set decryption key.
|
||||
@@ -715,27 +716,28 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
|
||||
// Select EDAT key.
|
||||
if ((NPD.license & 0x3) == 0x3) // Type 3: Use supplied devklic.
|
||||
memcpy(&key, devklic, 0x10);
|
||||
else if ((NPD.license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key).
|
||||
{
|
||||
memcpy(&key, rifkey, 0x10);
|
||||
std::memcpy(&key, devklic, 0x10);
|
||||
}
|
||||
else // Type 2: Use key from RAP file (RIF key). (also used for type 1 at the moment)
|
||||
{
|
||||
const std::string rap_path = rpcs3::utils::get_rap_file_path(NPD.content_id);
|
||||
|
||||
if (fs::file rap{rap_path}; rap && rap.size() >= sizeof(key))
|
||||
{
|
||||
key = GetEdatRifKeyFromRapFile(rap);
|
||||
}
|
||||
|
||||
// Make sure we don't have an empty RIF key.
|
||||
if (!key)
|
||||
{
|
||||
edat_log.error("A valid RAP file is needed for this EDAT file! (local activation)");
|
||||
edat_log.error("A valid RAP file is needed for this EDAT file! (license=%d)", NPD.license);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ((NPD.license & 0x1) == 0x1) // Type 1: Use network activation.
|
||||
{
|
||||
memcpy(&key, rifkey, 0x10);
|
||||
|
||||
// Make sure we don't have an empty RIF key.
|
||||
if (!key)
|
||||
if (verbose)
|
||||
{
|
||||
edat_log.error("A valid RAP file is needed for this EDAT file! (network activation)");
|
||||
return true;
|
||||
edat_log.notice("RIFKEY: %s", std::bit_cast<be_t<u128>>(key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,8 +747,6 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
|
||||
std::memcpy(&data, devklic, sizeof(data));
|
||||
edat_log.notice("DEVKLIC: %s", data);
|
||||
std::memcpy(&data, rifkey, sizeof(data));
|
||||
edat_log.notice("RIF KEY: %s", data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -793,8 +793,7 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& inpu
|
||||
// Read in the NPD and EDAT/SDAT headers.
|
||||
read_npd_edat_header(&input, NPD, EDAT);
|
||||
|
||||
unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0
|
||||
if (memcmp(&NPD.magic, npd_magic, 4))
|
||||
if (NPD.magic != "NPD\0"_u32)
|
||||
{
|
||||
edat_log.error("%s has invalid NPD header or already decrypted.", input_file_name);
|
||||
return false;
|
||||
@@ -824,13 +823,17 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& inpu
|
||||
}
|
||||
|
||||
// Decrypts full file
|
||||
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose)
|
||||
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose)
|
||||
{
|
||||
if (!input)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Prepare the files.
|
||||
input.seek(0);
|
||||
|
||||
// Set keys (RIF and DEVKLIC).
|
||||
u128 rifKey{};
|
||||
// Set DEVKLIC
|
||||
u128 devklic{};
|
||||
|
||||
// Select the EDAT key mode.
|
||||
@@ -875,17 +878,9 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
|
||||
return fs::file{};
|
||||
}
|
||||
|
||||
// Read the RAP file, if provided.
|
||||
if (!rap_file_name.empty())
|
||||
{
|
||||
const fs::file rap(rap_file_name);
|
||||
|
||||
rifKey = GetEdatRifKeyFromRapFile(rap);
|
||||
}
|
||||
|
||||
// Delete the bad output file if any errors arise.
|
||||
fs::file output = fs::make_stream<std::vector<u8>>();
|
||||
if (extract_all_data(&input, &output, input_file_name.c_str(), reinterpret_cast<uchar*>(&devklic), reinterpret_cast<uchar*>(&rifKey), verbose))
|
||||
if (extract_all_data(&input, &output, input_file_name.c_str(), reinterpret_cast<uchar*>(&devklic), verbose))
|
||||
{
|
||||
output.release();
|
||||
return fs::file{};
|
||||
@@ -901,8 +896,7 @@ bool EDATADecrypter::ReadHeader()
|
||||
// Read in the NPD and EDAT/SDAT headers.
|
||||
read_npd_edat_header(&edata_file, npdHeader, edatHeader);
|
||||
|
||||
unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0
|
||||
if (memcmp(&npdHeader.magic, npd_magic, 4))
|
||||
if (npdHeader.magic != "NPD\0"_u32)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ struct NPD_HEADER
|
||||
s32 version;
|
||||
s32 license;
|
||||
s32 type;
|
||||
u8 content_id[0x30];
|
||||
char content_id[0x30];
|
||||
u8 digest[0x10];
|
||||
u8 title_hash[0x10];
|
||||
u8 dev_hash[0x10];
|
||||
@@ -43,7 +43,7 @@ struct EDAT_HEADER
|
||||
};
|
||||
|
||||
// Decrypts full file, or null/empty file
|
||||
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose);
|
||||
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose);
|
||||
|
||||
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, std::string* contentID);
|
||||
|
||||
@@ -86,13 +86,9 @@ public:
|
||||
|
||||
fs::stat_t stat() override
|
||||
{
|
||||
fs::stat_t stats;
|
||||
stats.is_directory = false;
|
||||
stats.is_writable = false;
|
||||
fs::stat_t stats = edata_file.stat();
|
||||
stats.is_writable = false; // TODO
|
||||
stats.size = file_size;
|
||||
stats.atime = -1;
|
||||
stats.ctime = -1;
|
||||
stats.mtime = -1;
|
||||
return stats;
|
||||
}
|
||||
|
||||
|
||||
+60
-64
@@ -15,14 +15,12 @@ LOG_CHANNEL(pkg_log, "PKG");
|
||||
package_reader::package_reader(const std::string& path)
|
||||
: m_path(path)
|
||||
{
|
||||
if (!fs::is_file(path))
|
||||
if (!m_file.open(path))
|
||||
{
|
||||
pkg_log.error("PKG file not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_filelist.emplace_back(fs::file{path});
|
||||
|
||||
m_is_valid = read_header();
|
||||
|
||||
if (!m_is_valid)
|
||||
@@ -51,7 +49,7 @@ package_reader::~package_reader()
|
||||
|
||||
bool package_reader::read_header()
|
||||
{
|
||||
if (m_path.empty() || m_filelist.empty())
|
||||
if (m_path.empty() || !m_file)
|
||||
{
|
||||
pkg_log.error("Reading PKG header: no file to read!");
|
||||
return false;
|
||||
@@ -75,7 +73,7 @@ bool package_reader::read_header()
|
||||
pkg_log.notice("Header: data_size = 0x%x = %d", m_header.data_size, m_header.data_size);
|
||||
pkg_log.notice("Header: title_id = %s", m_header.title_id);
|
||||
pkg_log.notice("Header: qa_digest = 0x%x 0x%x", m_header.qa_digest[0], m_header.qa_digest[1]);
|
||||
//pkg_log.notice("Header: klicensee = 0x%x = %d", header.klicensee, header.klicensee);
|
||||
pkg_log.notice("Header: klicensee = %s", m_header.klicensee.value());
|
||||
|
||||
// Get extended PKG information for PSP or PSVita
|
||||
if (m_header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA)
|
||||
@@ -131,7 +129,7 @@ bool package_reader::read_header()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_header.pkg_size > m_filelist[0].size())
|
||||
if (m_header.pkg_size > m_file.size())
|
||||
{
|
||||
// Check if multi-files pkg
|
||||
if (!m_path.ends_with("_00.pkg"))
|
||||
@@ -140,11 +138,15 @@ bool package_reader::read_header()
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<fs::file> filelist;
|
||||
filelist.emplace_back(std::move(m_file));
|
||||
|
||||
const std::string name_wo_number = m_path.substr(0, m_path.size() - 7);
|
||||
u64 cursize = m_filelist[0].size();
|
||||
u64 cursize = filelist[0].size();
|
||||
|
||||
while (cursize < m_header.pkg_size)
|
||||
{
|
||||
const std::string archive_filename = fmt::format("%s_%02d.pkg", name_wo_number, m_filelist.size());
|
||||
const std::string archive_filename = fmt::format("%s_%02d.pkg", name_wo_number, filelist.size());
|
||||
|
||||
fs::file archive_file(archive_filename);
|
||||
if (!archive_file)
|
||||
@@ -153,9 +155,20 @@ bool package_reader::read_header()
|
||||
return false;
|
||||
}
|
||||
|
||||
cursize += archive_file.size();
|
||||
m_filelist.emplace_back(std::move(archive_file));
|
||||
const usz add_size = archive_file.size();
|
||||
|
||||
if (!add_size)
|
||||
{
|
||||
pkg_log.error("%s is empty, cannot read PKG", archive_filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
cursize += add_size;
|
||||
filelist.emplace_back(std::move(archive_file));
|
||||
}
|
||||
|
||||
// Gather files
|
||||
m_file = fs::make_gather(std::move(filelist));
|
||||
}
|
||||
|
||||
if (m_header.data_size + m_header.data_offset > m_header.pkg_size)
|
||||
@@ -510,10 +523,10 @@ bool package_reader::read_param_sfo()
|
||||
|
||||
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data());
|
||||
|
||||
const std::string name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
|
||||
const std::string_view name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
|
||||
|
||||
// We're looking for the PARAM.SFO file, if there is any
|
||||
if (name != "PARAM.SFO")
|
||||
if (usz ndelim = name.find_first_not_of('/'); ndelim == umax || name.substr(ndelim) != "PARAM.SFO")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -542,6 +555,12 @@ bool package_reader::read_param_sfo()
|
||||
|
||||
m_psf = psf::load_object(tmp);
|
||||
|
||||
if (m_psf.empty())
|
||||
{
|
||||
// Invalid
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -667,6 +686,8 @@ package_error package_reader::check_target_app_version() const
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose = false);
|
||||
|
||||
bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
{
|
||||
if (!m_is_valid)
|
||||
@@ -704,7 +725,9 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
break;
|
||||
}
|
||||
|
||||
dir += m_install_dir + '/';
|
||||
// TODO: Verify whether other content types require appending title ID
|
||||
if (m_metadata.content_type != PKG_CONTENT_TYPE_LICENSE)
|
||||
dir += m_install_dir + '/';
|
||||
|
||||
// If false, an existing directory is being overwritten: cannot cancel the operation
|
||||
const bool was_null = !fs::is_dir(dir);
|
||||
@@ -742,9 +765,10 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
const std::string name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
|
||||
const std::string path = dir + vfs::escape(name);
|
||||
|
||||
pkg_log.notice("Entry 0x%08x: %s", entry.type, name);
|
||||
const bool log_error = entry.pad || (entry.type & ~PKG_FILE_ENTRY_KNOWN_BITS);
|
||||
(log_error ? pkg_log.error : pkg_log.notice)("Entry 0x%08x: %s (pad=0x%x)", entry.type, name, entry.pad);
|
||||
|
||||
switch (entry.type & 0xff)
|
||||
switch (const u8 entry_type = entry.type & 0xff)
|
||||
{
|
||||
case PKG_FILE_ENTRY_NPDRM:
|
||||
case PKG_FILE_ENTRY_NPDRMEDAT:
|
||||
@@ -770,7 +794,14 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
break;
|
||||
}
|
||||
|
||||
if (fs::file out{ path, fs::rewrite })
|
||||
const bool is_buffered = entry_type == PKG_FILE_ENTRY_SDAT;
|
||||
|
||||
if (entry_type == PKG_FILE_ENTRY_NPDRMEDAT)
|
||||
{
|
||||
pkg_log.todo("NPDRM EDAT!");
|
||||
}
|
||||
|
||||
if (fs::file out = is_buffered ? fs::make_stream<std::vector<u8>>() : fs::file{ path, fs::rewrite })
|
||||
{
|
||||
bool extract_success = true;
|
||||
for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
|
||||
@@ -806,6 +837,17 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
}
|
||||
}
|
||||
|
||||
if (is_buffered)
|
||||
{
|
||||
out = DecryptEDAT(out, name, 1, reinterpret_cast<u8*>(&m_header.klicensee), true);
|
||||
if (!out || !fs::write_file(path, fs::rewrite, static_cast<fs::container_stream<std::vector<u8>>*>(out.release().get())->obj))
|
||||
{
|
||||
num_failures++;
|
||||
pkg_log.error("Failed to create file %s", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (extract_success)
|
||||
{
|
||||
if (did_overwrite)
|
||||
@@ -874,58 +916,12 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
|
||||
void package_reader::archive_seek(const s64 new_offset, const fs::seek_mode damode)
|
||||
{
|
||||
if (damode == fs::seek_set)
|
||||
m_cur_offset = new_offset;
|
||||
else if (damode == fs::seek_cur)
|
||||
m_cur_offset += new_offset;
|
||||
|
||||
u64 _offset = 0;
|
||||
for (usz i = 0; i < m_filelist.size(); i++)
|
||||
{
|
||||
if (m_cur_offset < (_offset + m_filelist[i].size()))
|
||||
{
|
||||
m_cur_file = i;
|
||||
m_cur_file_offset = m_cur_offset - _offset;
|
||||
m_filelist[i].seek(m_cur_file_offset);
|
||||
break;
|
||||
}
|
||||
_offset += m_filelist[i].size();
|
||||
}
|
||||
if (m_file) m_file.seek(new_offset, damode);
|
||||
};
|
||||
|
||||
u64 package_reader::archive_read(void* data_ptr, const u64 num_bytes)
|
||||
{
|
||||
ensure(m_filelist.size() > m_cur_file && m_filelist[m_cur_file]);
|
||||
|
||||
const u64 num_bytes_left = m_filelist[m_cur_file].size() - m_cur_file_offset;
|
||||
|
||||
// check if it continues in another file
|
||||
if (num_bytes > num_bytes_left)
|
||||
{
|
||||
m_filelist[m_cur_file].read(data_ptr, num_bytes_left);
|
||||
|
||||
if ((m_cur_file + 1) < m_filelist.size())
|
||||
{
|
||||
++m_cur_file;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cur_offset += num_bytes_left;
|
||||
m_cur_file_offset = m_filelist[m_cur_file].size();
|
||||
return num_bytes_left;
|
||||
}
|
||||
const u64 num_read = m_filelist[m_cur_file].read(static_cast<u8*>(data_ptr) + num_bytes_left, num_bytes - num_bytes_left);
|
||||
m_cur_offset += (num_read + num_bytes_left);
|
||||
m_cur_file_offset = num_read;
|
||||
return (num_read + num_bytes_left);
|
||||
}
|
||||
|
||||
const u64 num_read = m_filelist[m_cur_file].read(data_ptr, num_bytes);
|
||||
|
||||
m_cur_offset += num_read;
|
||||
m_cur_file_offset += num_read;
|
||||
|
||||
return num_read;
|
||||
return m_file ? m_file.read(data_ptr, num_bytes) : 0;
|
||||
};
|
||||
|
||||
u64 package_reader::decrypt(u64 offset, u64 size, const uchar* key)
|
||||
|
||||
@@ -35,6 +35,8 @@ enum : u32
|
||||
|
||||
PKG_FILE_ENTRY_OVERWRITE = 0x80000000,
|
||||
PKG_FILE_ENTRY_PSP = 0x10000000,
|
||||
|
||||
PKG_FILE_ENTRY_KNOWN_BITS = 0xff | PKG_FILE_ENTRY_PSP | PKG_FILE_ENTRY_OVERWRITE,
|
||||
};
|
||||
|
||||
enum : u32
|
||||
@@ -325,10 +327,7 @@ private:
|
||||
|
||||
std::string m_path{};
|
||||
std::string m_install_dir{};
|
||||
std::vector<fs::file> m_filelist{};
|
||||
usz m_cur_file = 0;
|
||||
u64 m_cur_offset = 0;
|
||||
u64 m_cur_file_offset = 0;
|
||||
fs::file m_file{};
|
||||
std::unique_ptr<u128[]> m_buf{};
|
||||
std::array<uchar, 16> m_dec_key{};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "utils.h"
|
||||
#include "aes.h"
|
||||
#include "sha1.h"
|
||||
#include "key_vault.h"
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
@@ -142,3 +143,80 @@ void mbedtls_zeroize(void *v, size_t n)
|
||||
static void *(*const volatile unop_memset)(void *, int, size_t) = &memset;
|
||||
(void)unop_memset(v, 0, n);
|
||||
}
|
||||
|
||||
|
||||
// SC passphrase crypto
|
||||
|
||||
void sc_form_key(const u8* sc_key, const std::array<u8, PASSPHRASE_KEY_LEN>& laid_paid, u8* key)
|
||||
{
|
||||
for (u32 i = 0; i < PASSPHRASE_KEY_LEN; i++)
|
||||
{
|
||||
key[i] = static_cast<u8>(sc_key[i] ^ laid_paid[i]);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<u8, PASSPHRASE_KEY_LEN> sc_combine_laid_paid(s64 laid, s64 paid)
|
||||
{
|
||||
const std::string paid_laid = fmt::format("%016llx%016llx", laid, paid);
|
||||
std::array<u8, PASSPHRASE_KEY_LEN> out{};
|
||||
hex_to_bytes(out.data(), paid_laid.c_str(), PASSPHRASE_KEY_LEN * 2);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::array<u8, PASSPHRASE_KEY_LEN> vtrm_get_laid_paid_from_type(int type)
|
||||
{
|
||||
// No idea what this type stands for
|
||||
switch (type)
|
||||
{
|
||||
case 0: return sc_combine_laid_paid(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
|
||||
case 1: return sc_combine_laid_paid(LAID_2, 0x1070000000000001L);
|
||||
case 2: return sc_combine_laid_paid(LAID_2, 0x0000000000000000L);
|
||||
case 3: return sc_combine_laid_paid(LAID_2, PAID_69);
|
||||
default:
|
||||
fmt::throw_exception("vtrm_get_laid_paid_from_type: Wrong type specified (type=%d)", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<u8, PASSPHRASE_KEY_LEN> vtrm_portability_laid_paid()
|
||||
{
|
||||
// 107000002A000001
|
||||
return sc_combine_laid_paid(0x0000000000000000L, 0x0000000000000000L);
|
||||
}
|
||||
|
||||
int sc_decrypt(const u8* sc_key, const std::array<u8, PASSPHRASE_KEY_LEN>& laid_paid, u8* iv, u8* input, u8* output)
|
||||
{
|
||||
aes_context ctx;
|
||||
u8 key[PASSPHRASE_KEY_LEN];
|
||||
sc_form_key(sc_key, laid_paid, key);
|
||||
aes_setkey_dec(&ctx, key, 128);
|
||||
return aes_crypt_cbc(&ctx, AES_DECRYPT, PASSPHRASE_OUT_LEN, iv, input, output);
|
||||
}
|
||||
|
||||
int vtrm_decrypt(int type, u8* iv, u8* input, u8* output)
|
||||
{
|
||||
return sc_decrypt(SC_ISO_SERIES_KEY_2, vtrm_get_laid_paid_from_type(type), iv, input, output);
|
||||
}
|
||||
|
||||
int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, u8* input, u8* output)
|
||||
{
|
||||
return sc_decrypt(SC_ISO_SERIES_INTERNAL_KEY_3, sc_combine_laid_paid(laid, paid), iv, input, output);
|
||||
}
|
||||
|
||||
const u8* vtrm_portability_type_mapper(int type)
|
||||
{
|
||||
// No idea what this type stands for
|
||||
switch (type)
|
||||
{
|
||||
//case 0: return key_for_type_1;
|
||||
case 1: return SC_ISO_SERIES_KEY_2;
|
||||
case 2: return SC_ISO_SERIES_KEY_1;
|
||||
case 3: return SC_KEY_FOR_MASTER_2;
|
||||
default:
|
||||
fmt::throw_exception("vtrm_portability_type_mapper: Wrong type specified (type=%d)", type);
|
||||
}
|
||||
}
|
||||
|
||||
int vtrm_decrypt_with_portability(int type, u8* iv, u8* input, u8* output)
|
||||
{
|
||||
return sc_decrypt(vtrm_portability_type_mapper(type), vtrm_portability_laid_paid(), iv, input, output);
|
||||
}
|
||||
|
||||
@@ -53,3 +53,9 @@ void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_
|
||||
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len);
|
||||
void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash);
|
||||
void mbedtls_zeroize(void *v, size_t n);
|
||||
|
||||
// SC passphrase crypto
|
||||
|
||||
int vtrm_decrypt(int type, u8* iv, u8* input, u8* output);
|
||||
int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, u8* input, u8* output);
|
||||
int vtrm_decrypt_with_portability(int type, u8* iv, u8* input, u8* output);
|
||||
|
||||
@@ -18,11 +18,11 @@ private:
|
||||
|
||||
IFACEMETHODIMP_(ULONG) AddRef() override { return 1; };
|
||||
IFACEMETHODIMP_(ULONG) Release() override { return 1; };
|
||||
IFACEMETHODIMP QueryInterface(REFIID iid, void** object) override { return S_OK; };
|
||||
IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceAdded(LPCWSTR device_id) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceRemoved(LPCWSTR device_id) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) override { return S_OK; };
|
||||
IFACEMETHODIMP QueryInterface(REFIID /*iid*/, void** /*object*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR /*device_id*/, const PROPERTYKEY /*key*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceAdded(LPCWSTR /*device_id*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceRemoved(LPCWSTR /*device_id*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR /*device_id*/, DWORD /*new_state*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id) override;
|
||||
} m_listener;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
system_config_types.cpp
|
||||
system_progress.cpp
|
||||
system_utils.cpp
|
||||
vfs_config.cpp
|
||||
VFS.cpp
|
||||
GDB.cpp
|
||||
title.cpp
|
||||
@@ -358,6 +359,7 @@ target_link_libraries(rpcs3_emu
|
||||
|
||||
# Io
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Io/camera_config.cpp
|
||||
Io/interception.cpp
|
||||
Io/KeyboardHandler.cpp
|
||||
Io/pad_config.cpp
|
||||
@@ -457,6 +459,7 @@ if(TARGET 3rdparty_vulkan)
|
||||
RSX/VK/vkutils/chip_class.cpp
|
||||
RSX/VK/vkutils/commands.cpp
|
||||
RSX/VK/vkutils/data_heap.cpp
|
||||
RSX/VK/vkutils/descriptors.cpp
|
||||
RSX/VK/vkutils/image.cpp
|
||||
RSX/VK/vkutils/image_helpers.cpp
|
||||
RSX/VK/vkutils/scratch.cpp
|
||||
|
||||
+26
-24
@@ -17,12 +17,13 @@ class cpu_thread;
|
||||
class CPUDisAsm
|
||||
{
|
||||
protected:
|
||||
const cpu_disasm_mode m_mode{};
|
||||
const std::add_pointer_t<const u8> m_offset{};
|
||||
cpu_disasm_mode m_mode{};
|
||||
const u8* m_offset{};
|
||||
const u32 m_start_pc;
|
||||
const std::add_pointer_t<const cpu_thread> m_cpu{};
|
||||
u32 m_op = 0;
|
||||
|
||||
void Write(const std::string& value)
|
||||
void format_by_mode()
|
||||
{
|
||||
switch (m_mode)
|
||||
{
|
||||
@@ -32,28 +33,27 @@ protected:
|
||||
static_cast<u8>(m_op >> 24),
|
||||
static_cast<u8>(m_op >> 16),
|
||||
static_cast<u8>(m_op >> 8),
|
||||
static_cast<u8>(m_op >> 0), value);
|
||||
static_cast<u8>(m_op >> 0), last_opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
case cpu_disasm_mode::interpreter:
|
||||
{
|
||||
last_opcode = fmt::format("[%08x] %02x %02x %02x %02x: %s", dump_pc,
|
||||
last_opcode.insert(0, fmt::format("[%08x] %02x %02x %02x %02x: ", dump_pc,
|
||||
static_cast<u8>(m_op >> 24),
|
||||
static_cast<u8>(m_op >> 16),
|
||||
static_cast<u8>(m_op >> 8),
|
||||
static_cast<u8>(m_op >> 0), value);
|
||||
static_cast<u8>(m_op >> 0)));
|
||||
break;
|
||||
}
|
||||
|
||||
case cpu_disasm_mode::compiler_elf:
|
||||
{
|
||||
last_opcode = value + '\n';
|
||||
last_opcode += '\n';
|
||||
break;
|
||||
}
|
||||
case cpu_disasm_mode::normal:
|
||||
{
|
||||
last_opcode = value;
|
||||
break;
|
||||
}
|
||||
default: fmt::throw_exception("Unreachable");
|
||||
@@ -64,26 +64,27 @@ public:
|
||||
std::string last_opcode{};
|
||||
u32 dump_pc{};
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_base_of_v<CPUDisAsm, T>, int> = 0>
|
||||
static T copy_and_change_mode(const T& dis, cpu_disasm_mode mode)
|
||||
cpu_disasm_mode change_mode(cpu_disasm_mode mode)
|
||||
{
|
||||
return T{mode, dis.m_offset, dis.m_cpu};
|
||||
return std::exchange(m_mode, mode);
|
||||
}
|
||||
|
||||
const u8* change_ptr(const u8* ptr)
|
||||
{
|
||||
return std::exchange(m_offset, ptr);
|
||||
}
|
||||
|
||||
protected:
|
||||
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu = nullptr)
|
||||
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0, const cpu_thread* cpu = nullptr)
|
||||
: m_mode(mode)
|
||||
, m_offset(offset)
|
||||
, m_offset(offset - start_pc)
|
||||
, m_start_pc(start_pc)
|
||||
, m_cpu(cpu)
|
||||
{
|
||||
}
|
||||
|
||||
CPUDisAsm(const CPUDisAsm&) = delete;
|
||||
|
||||
CPUDisAsm& operator=(const CPUDisAsm&) = delete;
|
||||
|
||||
virtual ~CPUDisAsm() = default;
|
||||
|
||||
virtual u32 DisAsmBranchTarget(s32 /*imm*/);
|
||||
|
||||
// TODO: Add builtin fmt helpper for best performance
|
||||
@@ -109,16 +110,17 @@ protected:
|
||||
return fmt::format("%s%s", v < 0 ? "-" : "", av);
|
||||
}
|
||||
|
||||
std::string FixOp(std::string op) const
|
||||
// Signify the formatting function the minimum required amount of characters to print for an instruction
|
||||
// Padding with spaces
|
||||
int PadOp(std::string_view op = {}, int min_spaces = 0) const
|
||||
{
|
||||
if (m_mode != cpu_disasm_mode::normal)
|
||||
{
|
||||
op.resize(std::max<usz>(op.length(), 10), ' ');
|
||||
}
|
||||
|
||||
return op;
|
||||
return m_mode == cpu_disasm_mode::normal ? (static_cast<int>(op.size()) + min_spaces) : 10;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~CPUDisAsm() = default;
|
||||
|
||||
virtual u32 disasm(u32 pc) = 0;
|
||||
virtual std::pair<const void*, usz> get_memory_span() const = 0;
|
||||
virtual std::unique_ptr<CPUDisAsm> copy_type_erased() const = 0;
|
||||
};
|
||||
|
||||
@@ -49,6 +49,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
|
||||
case cpu_flag::ret: return "ret";
|
||||
case cpu_flag::signal: return "sig";
|
||||
case cpu_flag::memory: return "mem";
|
||||
case cpu_flag::pending: return "pend";
|
||||
case cpu_flag::dbg_global_pause: return "G-PAUSE";
|
||||
case cpu_flag::dbg_pause: return "PAUSE";
|
||||
case cpu_flag::dbg_step: return "STEP";
|
||||
@@ -411,11 +412,6 @@ void cpu_thread::operator()()
|
||||
}
|
||||
if (id_type() == 2)
|
||||
{
|
||||
if (g_cfg.core.lower_spu_priority)
|
||||
{
|
||||
thread_ctrl::set_native_priority(-1);
|
||||
}
|
||||
|
||||
// force input/output denormals to zero for SPU threads (FTZ/DAZ)
|
||||
_mm_setcsr( _mm_getcsr() | 0x8040 );
|
||||
|
||||
@@ -469,7 +465,7 @@ void cpu_thread::operator()()
|
||||
cpu_thread* _cpu = get_current_cpu_thread();
|
||||
|
||||
// Wait flag isn't set asynchronously so this should be thread-safe
|
||||
if (progress == 0 && cpu_flag::wait - _cpu->state)
|
||||
if (progress == 0 && _cpu->state.none_of(cpu_flag::wait + cpu_flag::temp))
|
||||
{
|
||||
// Operation just started and syscall is imminent
|
||||
_cpu->state += cpu_flag::wait + cpu_flag::temp;
|
||||
@@ -739,6 +735,12 @@ bool cpu_thread::check_state() noexcept
|
||||
cpu_counter::add(this);
|
||||
}
|
||||
|
||||
if ((state0 & (cpu_flag::pending + cpu_flag::temp)) == cpu_flag::pending)
|
||||
{
|
||||
// Execute pending work
|
||||
cpu_work();
|
||||
}
|
||||
|
||||
if (retval)
|
||||
{
|
||||
cpu_on_stop();
|
||||
|
||||
@@ -21,6 +21,7 @@ enum class cpu_flag : u32
|
||||
ret, // Callback return requested
|
||||
signal, // Thread received a signal (HLE)
|
||||
memory, // Thread must unlock memory mutex
|
||||
pending, // Thread has postponed work
|
||||
|
||||
dbg_global_pause, // Emulation paused
|
||||
dbg_pause, // Thread paused
|
||||
@@ -169,6 +170,9 @@ public:
|
||||
// Callback for cpu_flag::suspend
|
||||
virtual void cpu_sleep() {}
|
||||
|
||||
// Callback for cpu_flag::pending
|
||||
virtual void cpu_work() {}
|
||||
|
||||
// Callback for cpu_flag::ret
|
||||
virtual void cpu_return() {}
|
||||
|
||||
|
||||
@@ -7,11 +7,60 @@
|
||||
|
||||
llvm::LLVMContext g_llvm_ctx;
|
||||
|
||||
llvm::Value* peek_through_bitcasts(llvm::Value* arg)
|
||||
{
|
||||
llvm::CastInst* i;
|
||||
|
||||
while ((i = llvm::dyn_cast_or_null<llvm::CastInst>(arg)) && i->getOpcode() == llvm::Instruction::BitCast)
|
||||
{
|
||||
arg = i->getOperand(0);
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
cpu_translator::cpu_translator(llvm::Module* _module, bool is_be)
|
||||
: m_context(g_llvm_ctx)
|
||||
, m_module(_module)
|
||||
, m_is_be(is_be)
|
||||
{
|
||||
register_intrinsic("x86_pshufb", [&](llvm::CallInst* ci) -> llvm::Value*
|
||||
{
|
||||
const auto data0 = ci->getOperand(0);
|
||||
const auto index = ci->getOperand(1);
|
||||
const auto zeros = llvm::ConstantAggregateZero::get(get_type<u8[16]>());
|
||||
|
||||
if (m_use_ssse3)
|
||||
{
|
||||
return m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_ssse3_pshuf_b_128), {data0, index});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Emulate PSHUFB (TODO)
|
||||
const auto mask = m_ir->CreateAnd(index, 0xf);
|
||||
const auto loop = llvm::BasicBlock::Create(m_context, "", m_ir->GetInsertBlock()->getParent());
|
||||
const auto prev = ci->getParent();
|
||||
const auto next = prev->splitBasicBlock(ci->getNextNode());
|
||||
|
||||
llvm::cast<llvm::BranchInst>(m_ir->GetInsertBlock()->getTerminator())->setOperand(0, loop);
|
||||
|
||||
llvm::Value* result;
|
||||
//m_ir->CreateBr(loop);
|
||||
m_ir->SetInsertPoint(loop);
|
||||
const auto i = m_ir->CreatePHI(get_type<u32>(), 2);
|
||||
const auto v = m_ir->CreatePHI(get_type<u8[16]>(), 2);
|
||||
i->addIncoming(m_ir->getInt32(0), prev);
|
||||
i->addIncoming(m_ir->CreateAdd(i, m_ir->getInt32(1)), loop);
|
||||
v->addIncoming(zeros, prev);
|
||||
result = m_ir->CreateInsertElement(v, m_ir->CreateExtractElement(data0, m_ir->CreateExtractElement(mask, i)), i);
|
||||
v->addIncoming(result, loop);
|
||||
m_ir->CreateCondBr(m_ir->CreateICmpULT(i, m_ir->getInt32(16)), loop, next);
|
||||
m_ir->SetInsertPoint(next->getFirstNonPHI());
|
||||
result = m_ir->CreateSelect(m_ir->CreateICmpSLT(index, zeros), zeros, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngine& engine)
|
||||
@@ -108,7 +157,7 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
|
||||
}
|
||||
|
||||
template <>
|
||||
std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32 a, u32 b)
|
||||
std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32 _pos, u32 _line)
|
||||
{
|
||||
v128 result{};
|
||||
|
||||
@@ -131,12 +180,12 @@ std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32
|
||||
return {true, result};
|
||||
}
|
||||
|
||||
fmt::throw_exception("[0x%x, %u] Not a vector", a, b);
|
||||
fmt::throw_exception("[0x%x, %u] Not a vector", _pos, _line);
|
||||
}
|
||||
|
||||
if (auto v = llvm::cast<llvm::VectorType>(t); v->getScalarSizeInBits() * v->getNumElements() != 128)
|
||||
{
|
||||
fmt::throw_exception("[0x%x, %u] Bad vector size: i%ux%u", a, b, v->getScalarSizeInBits(), v->getNumElements());
|
||||
fmt::throw_exception("[0x%x, %u] Bad vector size: i%ux%u", _pos, _line, v->getScalarSizeInBits(), v->getNumElements());
|
||||
}
|
||||
|
||||
const auto cv = llvm::dyn_cast<llvm::ConstantDataVector>(c);
|
||||
@@ -151,10 +200,10 @@ std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32
|
||||
if (llvm::isa<llvm::ConstantExpr>(c))
|
||||
{
|
||||
// Sorry, if we cannot evaluate it we cannot use it
|
||||
fmt::throw_exception("[0x%x, %u] Constant Expression!", a, b);
|
||||
fmt::throw_exception("[0x%x, %u] Constant Expression!", _pos, _line);
|
||||
}
|
||||
|
||||
fmt::throw_exception("[0x%x, %u] Unexpected constant type", a, b);
|
||||
fmt::throw_exception("[0x%x, %u] Unexpected constant type", _pos, _line);
|
||||
}
|
||||
|
||||
const auto sct = t->getScalarType();
|
||||
@@ -203,14 +252,14 @@ std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("[0x%x, %u] Unexpected vector element type", a, b);
|
||||
fmt::throw_exception("[0x%x, %u] Unexpected vector element type", _pos, _line);
|
||||
}
|
||||
|
||||
return {true, result};
|
||||
}
|
||||
|
||||
template <>
|
||||
llvm::Constant* cpu_translator::make_const_vector<v128>(v128 v, llvm::Type* t)
|
||||
llvm::Constant* cpu_translator::make_const_vector<v128>(v128 v, llvm::Type* t, u32 _line)
|
||||
{
|
||||
if (const auto ct = llvm::dyn_cast<llvm::IntegerType>(t); ct && ct->getBitWidth() == 128)
|
||||
{
|
||||
@@ -247,7 +296,55 @@ llvm::Constant* cpu_translator::make_const_vector<v128>(v128 v, llvm::Type* t)
|
||||
return llvm::ConstantDataVector::get(m_context, llvm::makeArrayRef(reinterpret_cast<const f64*>(v._bytes), 2));
|
||||
}
|
||||
|
||||
fmt::throw_exception("No supported constant type");
|
||||
fmt::throw_exception("[line %u] No supported constant type", _line);
|
||||
}
|
||||
|
||||
void cpu_translator::replace_intrinsics(llvm::Function& f)
|
||||
{
|
||||
for (auto& bb : f)
|
||||
{
|
||||
for (auto bit = bb.begin(); bit != bb.end();)
|
||||
{
|
||||
if (auto ci = llvm::dyn_cast<llvm::CallInst>(&*bit))
|
||||
{
|
||||
if (auto cf = ci->getCalledFunction())
|
||||
{
|
||||
if (auto it = m_intrinsics.find(std::string_view(cf->getName().data(), cf->getName().size())); it != m_intrinsics.end())
|
||||
{
|
||||
m_ir->SetInsertPoint(ci);
|
||||
ci->replaceAllUsesWith(it->second(ci));
|
||||
bit = ci->eraseFromParent();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_translator::erase_stores(llvm::ArrayRef<llvm::Value*> args)
|
||||
{
|
||||
for (auto v : args)
|
||||
{
|
||||
for (auto it = v->use_begin(); it != v->use_end(); ++it)
|
||||
{
|
||||
llvm::Value* i = *it;
|
||||
llvm::CastInst* bci = nullptr;
|
||||
|
||||
// Walk through bitcasts
|
||||
while (i && (bci = llvm::dyn_cast<llvm::CastInst>(i)) && bci->getOpcode() == llvm::Instruction::BitCast)
|
||||
{
|
||||
i = *bci->use_begin();
|
||||
}
|
||||
|
||||
if (auto si = llvm::dyn_cast_or_null<llvm::StoreInst>(i))
|
||||
{
|
||||
si->eraseFromParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+876
-242
File diff suppressed because it is too large
Load Diff
@@ -161,7 +161,7 @@ f32 audio_ringbuffer::set_frequency_ratio(f32 new_ratio)
|
||||
|
||||
u64 audio_ringbuffer::get_timestamp()
|
||||
{
|
||||
return get_system_time() - Emu.GetPauseTime();
|
||||
return get_system_time();
|
||||
}
|
||||
|
||||
void audio_ringbuffer::enqueue(const float* in_buffer)
|
||||
@@ -296,7 +296,7 @@ u64 audio_ringbuffer::update()
|
||||
}
|
||||
else
|
||||
{
|
||||
const u64 play_delta = timestamp - (play_timestamp > update_timestamp ? play_timestamp : update_timestamp);
|
||||
const u64 play_delta = (update_timestamp ? timestamp - std::max<u64>(play_timestamp, update_timestamp) : 0);
|
||||
|
||||
const u64 delta_samples_tmp = play_delta * static_cast<u64>(cfg.audio_sampling_rate * frequency_ratio) + last_remainder;
|
||||
last_remainder = delta_samples_tmp % 1'000'000;
|
||||
@@ -972,10 +972,10 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
|
||||
const float center = buf[in + 2] * m;
|
||||
[[maybe_unused]]
|
||||
const float low_freq = buf[in + 3] * m;
|
||||
const float rear_left = buf[in + 4] * m;
|
||||
const float rear_right = buf[in + 5] * m;
|
||||
const float side_left = buf[in + 6] * m;
|
||||
const float side_right = buf[in + 7] * m;
|
||||
const float side_left = buf[in + 4] * m;
|
||||
const float side_right = buf[in + 5] * m;
|
||||
const float rear_left = buf[in + 6] * m;
|
||||
const float rear_right = buf[in + 7] * m;
|
||||
|
||||
if constexpr (downmix == audio_downmix::downmix_to_stereo)
|
||||
{
|
||||
@@ -1017,10 +1017,10 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
|
||||
const float right = buf[in + 1] * m;
|
||||
const float center = buf[in + 2] * m;
|
||||
const float low_freq = buf[in + 3] * m;
|
||||
const float rear_left = buf[in + 4] * m;
|
||||
const float rear_right = buf[in + 5] * m;
|
||||
const float side_left = buf[in + 6] * m;
|
||||
const float side_right = buf[in + 7] * m;
|
||||
const float side_left = buf[in + 4] * m;
|
||||
const float side_right = buf[in + 5] * m;
|
||||
const float rear_left = buf[in + 6] * m;
|
||||
const float rear_right = buf[in + 7] * m;
|
||||
|
||||
if constexpr (downmix == audio_downmix::downmix_to_stereo)
|
||||
{
|
||||
@@ -1237,7 +1237,7 @@ error_code cellAudioPortOpen(vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32
|
||||
port->cur_pos = 0;
|
||||
port->global_counter = g_audio.m_counter;
|
||||
port->active_counter = 0;
|
||||
port->timestamp = g_audio.m_last_period_end;
|
||||
port->timestamp = get_guest_system_time(g_audio.m_last_period_end);
|
||||
|
||||
if (attr & CELL_AUDIO_PORTATTR_INITLEVEL)
|
||||
{
|
||||
@@ -1419,7 +1419,7 @@ error_code cellAudioGetPortTimestamp(u32 portNum, u64 tag, vm::ptr<u64> stamp)
|
||||
const u64 delta_tag_stamp = delta_tag * g_audio.cfg.audio_block_period;
|
||||
|
||||
// Apparently no error is returned if stamp is null
|
||||
*stamp = port.timestamp - delta_tag_stamp;
|
||||
*stamp = get_guest_system_time(port.timestamp - delta_tag_stamp);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "util/endian.hpp"
|
||||
|
||||
// Error codes
|
||||
enum CellAudioInError
|
||||
enum CellAudioInError : u32
|
||||
{
|
||||
CELL_AUDIO_IN_ERROR_NOT_IMPLEMENTED = 0x8002b260,
|
||||
CELL_AUDIO_IN_ERROR_ILLEGAL_CONFIGURATION = 0x8002b261,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// Error codes
|
||||
enum CellAudioOutError
|
||||
enum CellAudioOutError : u32
|
||||
{
|
||||
CELL_AUDIO_OUT_ERROR_NOT_IMPLEMENTED = 0x8002b240,
|
||||
CELL_AUDIO_OUT_ERROR_ILLEGAL_CONFIGURATION = 0x8002b241,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@
|
||||
#include "Utilities/Timer.h"
|
||||
#include "Emu/Cell/lv2/sys_memory.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include "Emu/Io/camera_handler_base.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -359,7 +360,7 @@ struct CellCameraInfo
|
||||
|
||||
struct CellCameraInfoEx
|
||||
{
|
||||
be_t<s32> format; // CellCameraFormat
|
||||
be_t<CellCameraFormat> format; // CellCameraFormat
|
||||
be_t<s32> resolution; // CellCameraResolution
|
||||
be_t<s32> framerate;
|
||||
|
||||
@@ -425,17 +426,31 @@ public:
|
||||
atomic_t<bool> is_open{false};
|
||||
|
||||
CellCameraInfoEx info{};
|
||||
atomic_t<u32> pbuf_write_index = 0;
|
||||
std::array<atomic_t<bool>, 2> pbuf_locked = { false, false };
|
||||
u32 pbuf_next_index() const;
|
||||
|
||||
struct attr_t
|
||||
{
|
||||
u32 v1, v2;
|
||||
};
|
||||
attr_t attr[500]{};
|
||||
atomic_t<u32> frame_num;
|
||||
atomic_t<bool> has_new_frame = false;
|
||||
atomic_t<u32> frame_num = 0;
|
||||
atomic_t<u32> frame_timestamp = 0;
|
||||
atomic_t<u32> bytes_read = 0;
|
||||
|
||||
atomic_t<u32> init = 0;
|
||||
|
||||
static constexpr auto thread_name = "Camera Thread"sv;
|
||||
|
||||
std::shared_ptr<camera_handler_base> handler;
|
||||
bool open_camera();
|
||||
bool start_camera();
|
||||
bool get_camera_frame(u8* dst, u32& width, u32& height, u64& frame_number, u64& bytes_read);
|
||||
void stop_camera();
|
||||
void close_camera();
|
||||
bool on_handler_state(camera_handler_base::camera_handler_state state);
|
||||
};
|
||||
|
||||
using camera_thread = named_thread<camera_context>;
|
||||
@@ -446,4 +461,8 @@ struct gem_camera_shared
|
||||
atomic_t<s64> frame_timestamp{}; // latest read timestamp from cellCamera (cellCameraRead(Ex))
|
||||
atomic_t<s32> width{640};
|
||||
atomic_t<s32> height{480};
|
||||
atomic_t<s32> size{0};
|
||||
atomic_t<CellCameraFormat> format{CELL_CAMERA_RAW8};
|
||||
};
|
||||
|
||||
static inline s32 get_video_buffer_size(s32 width, s32 height);
|
||||
|
||||
@@ -472,7 +472,9 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
|
||||
std::string dir;
|
||||
psf::registry sfo;
|
||||
|
||||
if (Emu.GetCat() == "DG")
|
||||
const std::string& cat = Emu.GetFakeCat();
|
||||
|
||||
if (cat == "DG")
|
||||
{
|
||||
*type = CELL_GAME_GAMETYPE_DISC;
|
||||
*attributes = 0; // TODO
|
||||
@@ -480,7 +482,7 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
|
||||
|
||||
sfo = psf::load_object(fs::file(vfs::get("/dev_bdvd/PS3_GAME/PARAM.SFO")));
|
||||
}
|
||||
else if (Emu.GetCat() == "GD")
|
||||
else if (cat == "GD")
|
||||
{
|
||||
*type = CELL_GAME_GAMETYPE_DISC;
|
||||
*attributes = CELL_GAME_ATTRIBUTE_PATCH; // TODO
|
||||
|
||||
@@ -1570,5 +1570,5 @@ DECLARE(ppu_module_manager::cellGcmSys)("cellGcmSys", []()
|
||||
REG_FUNC(cellGcmSys, cellGcmGpadCaptureSnapshot);
|
||||
|
||||
// Special
|
||||
REG_FUNC(cellGcmSys, cellGcmCallback).flag(MFF_HIDDEN);
|
||||
REG_HIDDEN_FUNC(cellGcmCallback);
|
||||
});
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
#include "cellGem.h"
|
||||
#include "cellCamera.h"
|
||||
|
||||
#include "Emu/Cell/lv2/sys_event.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/timers.hpp"
|
||||
#include "Emu/Io/MouseHandler.h"
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Input/pad_thread.h"
|
||||
|
||||
@@ -59,12 +61,39 @@ void fmt_class_string<CellGemStatus>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<CellGemVideoConvertFormatEnum>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
STR_CASE(CELL_GEM_NO_VIDEO_OUTPUT);
|
||||
STR_CASE(CELL_GEM_RGBA_640x480);
|
||||
STR_CASE(CELL_GEM_YUV_640x480);
|
||||
STR_CASE(CELL_GEM_YUV422_640x480);
|
||||
STR_CASE(CELL_GEM_YUV411_640x480);
|
||||
STR_CASE(CELL_GEM_RGBA_320x240);
|
||||
STR_CASE(CELL_GEM_BAYER_RESTORED);
|
||||
STR_CASE(CELL_GEM_BAYER_RESTORED_RGGB);
|
||||
STR_CASE(CELL_GEM_BAYER_RESTORED_RASTERIZED);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
// **********************
|
||||
// * HLE helper structs *
|
||||
// **********************
|
||||
|
||||
struct gem_config
|
||||
struct gem_config_data
|
||||
{
|
||||
public:
|
||||
void operator()();
|
||||
|
||||
static constexpr auto thread_name = "Gem Thread"sv;
|
||||
|
||||
atomic_t<u32> state = 0;
|
||||
|
||||
struct gem_color
|
||||
@@ -106,14 +135,16 @@ struct gem_config
|
||||
|
||||
CellGemAttribute attribute = {};
|
||||
CellGemVideoConvertAttribute vc_attribute = {};
|
||||
s32 video_data_out_size = -1;
|
||||
std::vector<u8> video_data_in;
|
||||
u64 status_flags = 0;
|
||||
bool enable_pitch_correction = false;
|
||||
u32 inertial_counter = 0;
|
||||
|
||||
std::array<gem_controller, CELL_GEM_MAX_NUM> controllers;
|
||||
u32 connected_controllers = 0;
|
||||
bool video_conversion_started{};
|
||||
bool update_started{};
|
||||
atomic_t<bool> video_conversion_in_progress{false};
|
||||
atomic_t<bool> update_started{false};
|
||||
u32 camera_frame{};
|
||||
u32 memory_ptr{};
|
||||
|
||||
@@ -173,6 +204,186 @@ struct gem_config
|
||||
}
|
||||
};
|
||||
|
||||
static inline int32_t cellGemGetVideoConvertSize(s32 output_format)
|
||||
{
|
||||
switch (output_format)
|
||||
{
|
||||
case CELL_GEM_RGBA_320x240: // RGBA output; 320*240*4-byte output buffer required
|
||||
return 320 * 240 * 4;
|
||||
case CELL_GEM_RGBA_640x480: // RGBA output; 640*480*4-byte output buffer required
|
||||
return 640 * 480 * 4;
|
||||
case CELL_GEM_YUV_640x480: // YUV output; 640*480+640*480+640*480-byte output buffer required (contiguous)
|
||||
return 640 * 480 + 640 * 480 + 640 * 480;
|
||||
case CELL_GEM_YUV422_640x480: // YUV output; 640*480+320*480+320*480-byte output buffer required (contiguous)
|
||||
return 640 * 480 + 320 * 480 + 320 * 480;
|
||||
case CELL_GEM_YUV411_640x480: // YUV411 output; 640*480+320*240+320*240-byte output buffer required (contiguous)
|
||||
return 640 * 480 + 320 * 240 + 320 * 240;
|
||||
case CELL_GEM_BAYER_RESTORED: // Bayer pattern output, 640x480, gamma and white balance applied, output buffer required
|
||||
case CELL_GEM_BAYER_RESTORED_RGGB: // Restored Bayer output, 2x2 pixels rearranged into 320x240 RG1G2B
|
||||
case CELL_GEM_BAYER_RESTORED_RASTERIZED: // Restored Bayer output, R,G1,G2,B rearranged into 4 contiguous 320x240 1-channel rasters
|
||||
return 640 * 480;
|
||||
case CELL_GEM_NO_VIDEO_OUTPUT: // Disable video output
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void gem_config_data::operator()()
|
||||
{
|
||||
cellGem.notice("Starting thread");
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped())
|
||||
{
|
||||
while (!video_conversion_in_progress && thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped())
|
||||
{
|
||||
thread_ctrl::wait_for(1000);
|
||||
}
|
||||
|
||||
if (thread_ctrl::state() == thread_state::aborting || Emu.IsStopped())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CellGemVideoConvertAttribute vc;
|
||||
{
|
||||
std::scoped_lock lock(mtx);
|
||||
vc = vc_attribute;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera != camera_handler::qt)
|
||||
{
|
||||
video_conversion_in_progress = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
|
||||
if (vc.output_format != CELL_GEM_NO_VIDEO_OUTPUT && !vc_attribute.video_data_out)
|
||||
{
|
||||
video_conversion_in_progress = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
extern u32 get_buffer_size_by_format(s32, s32, s32);
|
||||
const u32 required_in_size = get_buffer_size_by_format(static_cast<s32>(shared_data.format), shared_data.width, shared_data.height);
|
||||
const s32 required_out_size = cellGemGetVideoConvertSize(vc.output_format);
|
||||
|
||||
if (video_data_in.size() != required_in_size)
|
||||
{
|
||||
cellGem.error("convert: in_size mismatch: required=%d, actual=%d", required_in_size, video_data_in.size());
|
||||
video_conversion_in_progress = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (required_out_size < 0 || video_data_out_size != required_out_size)
|
||||
{
|
||||
cellGem.error("convert: out_size unknown: required=%d, format %d", required_out_size, vc.output_format);
|
||||
video_conversion_in_progress = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (required_out_size == 0)
|
||||
{
|
||||
video_conversion_in_progress = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (vc.output_format)
|
||||
{
|
||||
case CELL_GEM_RGBA_640x480: // RGBA output; 640*480*4-byte output buffer required
|
||||
{
|
||||
if (shared_data.format == CELL_CAMERA_RAW8)
|
||||
{
|
||||
constexpr u32 in_pitch = 640;
|
||||
constexpr u32 out_pitch = 640 * 4;
|
||||
|
||||
for (u32 y = 0; y < 480 - 1; y += 2)
|
||||
{
|
||||
for (u32 x = 0; x < 640 - 1; x += 2)
|
||||
{
|
||||
const u32 in_offset = 1 * (y * 640 + x);
|
||||
const u32 out_offset = 4 * (y * 640 + x);
|
||||
|
||||
const u8 b = video_data_in[in_offset + 0];
|
||||
const u8 g0 = video_data_in[in_offset + 1];
|
||||
const u8 g1 = video_data_in[in_offset + in_pitch + 0];
|
||||
const u8 r = video_data_in[in_offset + in_pitch + 1];
|
||||
|
||||
// Top-Left
|
||||
vc_attribute.video_data_out[out_offset + 0] = r; // R
|
||||
vc_attribute.video_data_out[out_offset + 1] = g0; // G
|
||||
vc_attribute.video_data_out[out_offset + 2] = b; // B
|
||||
vc_attribute.video_data_out[out_offset + 3] = 255; // A
|
||||
|
||||
// Top-Right Pixel
|
||||
vc_attribute.video_data_out[out_offset + 4] = r; // R
|
||||
vc_attribute.video_data_out[out_offset + 5] = g0; // G
|
||||
vc_attribute.video_data_out[out_offset + 6] = b; // B
|
||||
vc_attribute.video_data_out[out_offset + 7] = 255; // A
|
||||
|
||||
// Bottom-Left Pixel
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 0] = r; // R
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 1] = g1; // G
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 2] = b; // B
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 3] = 255; // A
|
||||
|
||||
// Bottom-Right Pixel
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 4] = r; // R
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 5] = g1; // G
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 6] = b; // B
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 7] = 255; // A
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s", shared_data.format.load(), vc.output_format);
|
||||
std::memcpy(vc_attribute.video_data_out.get_ptr(), video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GEM_BAYER_RESTORED: // Bayer pattern output, 640x480, gamma and white balance applied, output buffer required
|
||||
{
|
||||
if (shared_data.format == CELL_CAMERA_RAW8)
|
||||
{
|
||||
std::memcpy(vc_attribute.video_data_out.get_ptr(), video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s", shared_data.format.load(), vc.output_format);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GEM_RGBA_320x240: // RGBA output; 320*240*4-byte output buffer required
|
||||
case CELL_GEM_YUV_640x480: // YUV output; 640*480+640*480+640*480-byte output buffer required (contiguous)
|
||||
case CELL_GEM_YUV422_640x480: // YUV output; 640*480+320*480+320*480-byte output buffer required (contiguous)
|
||||
case CELL_GEM_YUV411_640x480: // YUV411 output; 640*480+320*240+320*240-byte output buffer required (contiguous)
|
||||
case CELL_GEM_BAYER_RESTORED_RGGB: // Restored Bayer output, 2x2 pixels rearranged into 320x240 RG1G2B
|
||||
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", shared_data.format.load(), vc.output_format);
|
||||
break;
|
||||
}
|
||||
case CELL_GEM_NO_VIDEO_OUTPUT: // Disable video output
|
||||
{
|
||||
cellGem.trace("Ignoring frame conversion for CELL_GEM_NO_VIDEO_OUTPUT");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Trying to convert %s to %s", shared_data.format.load(), vc.output_format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cellGem.notice("Converted video frame of format %s to %s", shared_data.format.load(), vc.output_format.get());
|
||||
video_conversion_in_progress = false;
|
||||
}
|
||||
}
|
||||
|
||||
using gem_config = named_thread<gem_config_data>;
|
||||
|
||||
/**
|
||||
* \brief Verifies that a Move controller id is valid
|
||||
* \param gem_num Move controler ID to verify
|
||||
@@ -519,7 +730,7 @@ error_code cellGemClearStatusFlags(u32 gem_num, u64 mask)
|
||||
|
||||
error_code cellGemConvertVideoFinish()
|
||||
{
|
||||
cellGem.todo("cellGemConvertVideoFinish()");
|
||||
cellGem.warning("cellGemConvertVideoFinish()");
|
||||
|
||||
auto& gem = g_fxo->get<gem_config>();
|
||||
|
||||
@@ -528,19 +739,22 @@ error_code cellGemConvertVideoFinish()
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!std::exchange(gem.video_conversion_started, false))
|
||||
if (!gem.video_conversion_in_progress)
|
||||
{
|
||||
return CELL_GEM_ERROR_CONVERT_NOT_STARTED;
|
||||
}
|
||||
|
||||
// TODO: wait until image is converted
|
||||
while (gem.video_conversion_in_progress && !Emu.IsStopped())
|
||||
{
|
||||
thread_ctrl::wait_for(100);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGemConvertVideoStart(vm::cptr<void> video_frame)
|
||||
{
|
||||
cellGem.todo("cellGemConvertVideoStart(video_frame=*0x%x)", video_frame);
|
||||
cellGem.warning("cellGemConvertVideoStart(video_frame=*0x%x)", video_frame);
|
||||
|
||||
auto& gem = g_fxo->get<gem_config>();
|
||||
|
||||
@@ -554,18 +768,20 @@ error_code cellGemConvertVideoStart(vm::cptr<void> video_frame)
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// TODO: The alignment checks seem to break Time Crisis Razing Storm [BLUS30528]
|
||||
//if (!video_frame.aligned(128))
|
||||
//{
|
||||
// return CELL_GEM_ERROR_INVALID_ALIGNMENT;
|
||||
//}
|
||||
if (!video_frame.aligned(128))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
if (std::exchange(gem.video_conversion_started, true))
|
||||
if (gem.video_conversion_in_progress)
|
||||
{
|
||||
return CELL_GEM_ERROR_CONVERT_NOT_FINISHED;
|
||||
}
|
||||
|
||||
// TODO: start image conversion of video_frame async to gem.vc_attribute.video_data_out
|
||||
const auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
gem.video_data_in.resize(shared_data.size);
|
||||
std::memcpy(gem.video_data_in.data(), video_frame.get_ptr(), gem.video_data_in.size());
|
||||
gem.video_conversion_in_progress = true;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -753,11 +969,6 @@ error_code cellGemGetCameraState(vm::ptr<CellGemCameraState> camera_state)
|
||||
|
||||
auto& gem = g_fxo->get<gem_config>();
|
||||
|
||||
if (!gem.state)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!camera_state)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
@@ -826,7 +1037,7 @@ error_code cellGemGetHuePixels(vm::cptr<void> camera_frame, u32 hue, vm::ptr<u8>
|
||||
|
||||
error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> gem_image_state)
|
||||
{
|
||||
cellGem.todo("cellGemGetImageState(gem_num=%d, image_state=&0x%x)", gem_num, gem_image_state);
|
||||
cellGem.warning("cellGemGetImageState(gem_num=%d, image_state=&0x%x)", gem_num, gem_image_state);
|
||||
|
||||
auto& gem = g_fxo->get<gem_config>();
|
||||
|
||||
@@ -879,7 +1090,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num) || state_flag > CELL_GEM_INERTIAL_STATE_FLAG_NEXT || !inertial_state || !gem.is_controller_ready(gem_num))
|
||||
if (!check_gem_num(gem_num) || !inertial_state || !gem.is_controller_ready(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
@@ -1303,12 +1514,17 @@ s32 cellGemIsTrackableHue(u32 hue)
|
||||
|
||||
auto& gem = g_fxo->get<gem_config>();
|
||||
|
||||
if (!gem.state || hue > 359)
|
||||
if (!gem.state)
|
||||
{
|
||||
return false;
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (hue > 359)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
error_code cellGemPrepareCamera(s32 max_exposure, f32 image_quality)
|
||||
@@ -1337,7 +1553,7 @@ error_code cellGemPrepareCamera(s32 max_exposure, f32 image_quality)
|
||||
|
||||
error_code cellGemPrepareVideoConvert(vm::cptr<CellGemVideoConvertAttribute> vc_attribute)
|
||||
{
|
||||
cellGem.todo("cellGemPrepareVideoConvert(vc_attribute=*0x%x)", vc_attribute);
|
||||
cellGem.warning("cellGemPrepareVideoConvert(vc_attribute=*0x%x)", vc_attribute);
|
||||
|
||||
auto& gem = g_fxo->get<gem_config>();
|
||||
|
||||
@@ -1351,29 +1567,36 @@ error_code cellGemPrepareVideoConvert(vm::cptr<CellGemVideoConvertAttribute> vc_
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const auto vc = *vc_attribute;
|
||||
const CellGemVideoConvertAttribute vc = *vc_attribute;
|
||||
|
||||
if (!vc_attribute || vc.version != CELL_GEM_VERSION)
|
||||
if (vc.version != CELL_GEM_VERSION)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (vc.output_format != CELL_GEM_NO_VIDEO_OUTPUT)
|
||||
{
|
||||
if (!vc.video_data_out || ((vc.conversion_flags & CELL_GEM_COMBINE_PREVIOUS_INPUT_FRAME) && !vc.buffer_memory))
|
||||
if (!vc.video_data_out)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The alignment checks seem to break Time Crisis Razing Storm [BLUS30528]
|
||||
//if (!vc.video_data_out.aligned(16) || !vc.buffer_memory.aligned(128))
|
||||
//{
|
||||
// return CELL_GEM_ERROR_INVALID_ALIGNMENT;
|
||||
//}
|
||||
if ((vc.conversion_flags & CELL_GEM_COMBINE_PREVIOUS_INPUT_FRAME) && !vc.buffer_memory)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!vc.video_data_out.aligned(128) || !vc.buffer_memory.aligned(16))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
gem.vc_attribute = vc;
|
||||
|
||||
const s32 buffer_size = cellGemGetVideoConvertSize(vc.output_format);
|
||||
gem.video_data_out_size = buffer_size;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1434,7 +1657,7 @@ error_code cellGemReset(u32 gem_num)
|
||||
|
||||
error_code cellGemSetRumble(u32 gem_num, u8 rumble)
|
||||
{
|
||||
cellGem.todo("cellGemSetRumble(gem_num=%d, rumble=0x%x)", gem_num, rumble);
|
||||
cellGem.warning("cellGemSetRumble(gem_num=%d, rumble=0x%x)", gem_num, rumble);
|
||||
|
||||
auto& gem = g_fxo->get<gem_config>();
|
||||
|
||||
@@ -1468,7 +1691,7 @@ error_code cellGemSetYaw(u32 gem_num, vm::ptr<f32> z_direction)
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!z_direction)
|
||||
if (!z_direction || !check_gem_num(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
@@ -1571,7 +1794,7 @@ error_code cellGemUpdateFinish()
|
||||
|
||||
std::scoped_lock lock(gem.mtx);
|
||||
|
||||
if (!std::exchange(gem.update_started, false))
|
||||
if (!gem.update_started.exchange(false))
|
||||
{
|
||||
return CELL_GEM_ERROR_UPDATE_NOT_STARTED;
|
||||
}
|
||||
@@ -1598,16 +1821,15 @@ error_code cellGemUpdateStart(vm::cptr<void> camera_frame, u64 timestamp)
|
||||
std::scoped_lock lock(gem.mtx);
|
||||
|
||||
// Update is starting even when camera_frame is null
|
||||
if (std::exchange(gem.update_started, true))
|
||||
if (gem.update_started.exchange(true))
|
||||
{
|
||||
return CELL_GEM_ERROR_UPDATE_NOT_FINISHED;
|
||||
}
|
||||
|
||||
// TODO: The alignment checks seem to break Time Crisis Razing Storm [BLUS30528]
|
||||
//if (!camera_frame.aligned(128))
|
||||
//{
|
||||
// return CELL_GEM_ERROR_INVALID_ALIGNMENT;
|
||||
//}
|
||||
if (!camera_frame.aligned(128))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
gem.camera_frame = camera_frame.addr();
|
||||
if (!camera_frame)
|
||||
|
||||
@@ -145,7 +145,7 @@ enum
|
||||
};
|
||||
|
||||
// Video conversion output formats
|
||||
enum
|
||||
enum CellGemVideoConvertFormatEnum : s32
|
||||
{
|
||||
CELL_GEM_NO_VIDEO_OUTPUT = 1,
|
||||
CELL_GEM_RGBA_640x480 = 2,
|
||||
@@ -261,13 +261,13 @@ struct CellGemState
|
||||
struct CellGemVideoConvertAttribute
|
||||
{
|
||||
be_t<s32> version;
|
||||
be_t<s32> output_format;
|
||||
be_t<CellGemVideoConvertFormatEnum> output_format;
|
||||
be_t<s32> conversion_flags;
|
||||
be_t<f32> gain;
|
||||
be_t<f32> red_gain;
|
||||
be_t<f32> green_gain;
|
||||
be_t<f32> blue_gain;
|
||||
vm::ptr<void> buffer_memory;
|
||||
vm::ptr<void> video_data_out;
|
||||
vm::bptr<u8> buffer_memory;
|
||||
vm::bptr<u8> video_data_out;
|
||||
u8 alpha;
|
||||
};
|
||||
|
||||
@@ -301,9 +301,22 @@ error_code cellKbRead(u32 port_no, vm::ptr<CellKbData> data)
|
||||
data->mkey = current_data.mkey;
|
||||
data->len = std::min<s32>(CELL_KB_MAX_KEYCODES, current_data.len);
|
||||
|
||||
for (s32 i = 0; i < current_data.len; i++)
|
||||
if (current_data.len > 0)
|
||||
{
|
||||
data->keycode[i] = current_data.keycode[i].first;
|
||||
for (s32 i = 0; i < current_data.len; i++)
|
||||
{
|
||||
data->keycode[i] = current_data.keycode[i].first;
|
||||
}
|
||||
|
||||
KbConfig& current_config = handler.GetConfig(port_no);
|
||||
|
||||
// For single character mode to work properly we need to "flush" the buffer after reading or else we'll constantly get the same key presses with each call.
|
||||
// Actual key repeats are handled by adding a new key code to the buffer periodically. Key releases are handled in a similar fashion.
|
||||
// Warning: Don't do this in packet mode, which is basically the mouse and keyboard gaming mode. Otherwise games like Unreal Tournament will be unplayable.
|
||||
if (current_config.read_mode == CELL_KB_RMODE_INPUTCHAR)
|
||||
{
|
||||
current_data.len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
@@ -386,6 +399,10 @@ error_code cellKbSetReadMode(u32 port_no, u32 rmode)
|
||||
KbConfig& current_config = handler.GetConfig(port_no);
|
||||
current_config.read_mode = rmode;
|
||||
|
||||
// Key repeat must be disabled in packet mode. But let's just always enable it otherwise.
|
||||
Keyboard& keyboard = handler.GetKeyboards()[port_no];
|
||||
keyboard.m_key_repeat = rmode != CELL_KB_RMODE_PACKET;
|
||||
|
||||
// can also return CELL_KB_ERROR_SYS_SETTING_FAILED
|
||||
|
||||
return CELL_OK;
|
||||
|
||||
@@ -949,7 +949,7 @@ error_code cellMicReadRaw(s32 dev_num, vm::ptr<void> data, s32 max_bytes)
|
||||
|
||||
error_code cellMicRead(s32 dev_num, vm::ptr<void> data, u32 max_bytes)
|
||||
{
|
||||
cellMic.todo("cellMicRead(dev_num=%d, data=0x%x, maxBytes=0x%x)", dev_num, data, max_bytes);
|
||||
cellMic.warning("cellMicRead(dev_num=%d, data=0x%x, maxBytes=0x%x)", dev_num, data, max_bytes);
|
||||
|
||||
auto& mic_thr = g_fxo->get<mic_thread>();
|
||||
const std::lock_guard lock(mic_thr.mutex);
|
||||
|
||||
@@ -618,5 +618,5 @@ void cellSysutil_MsgDialog_init()
|
||||
REG_FUNC(cellSysutil, cellMsgDialogAbort);
|
||||
|
||||
// Helper Function
|
||||
REG_FUNC(cellSysutil, exit_game).flag(MFF_HIDDEN);
|
||||
REG_HIDDEN_FUNC(exit_game);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,23 @@ void fmt_class_string<CellOskDialogError>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<CellOskDialogContinuousMode>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
STR_CASE(CELL_OSKDIALOG_CONTINUOUS_MODE_NONE);
|
||||
STR_CASE(CELL_OSKDIALOG_CONTINUOUS_MODE_REMAIN_OPEN);
|
||||
STR_CASE(CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE);
|
||||
STR_CASE(CELL_OSKDIALOG_CONTINUOUS_MODE_SHOW);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
OskDialogBase::~OskDialogBase()
|
||||
{
|
||||
}
|
||||
@@ -41,13 +58,68 @@ struct osk_info
|
||||
{
|
||||
std::shared_ptr<OskDialogBase> dlg;
|
||||
|
||||
std::array<char16_t, CELL_OSKDIALOG_STRING_SIZE> valid_text{};
|
||||
shared_mutex text_mtx;
|
||||
|
||||
atomic_t<bool> use_separate_windows = false;
|
||||
|
||||
atomic_t<bool> lock_ext_input = false;
|
||||
atomic_t<u32> device_mask = 0; // 0 means all devices can influence the OSK
|
||||
atomic_t<u32> key_layout = CELL_OSKDIALOG_10KEY_PANEL;
|
||||
atomic_t<CellOskDialogInitialKeyLayout> initial_key_layout = CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_SYSTEM;
|
||||
atomic_t<CellOskDialogInputDevice> initial_input_device = CELL_OSKDIALOG_INPUT_DEVICE_PAD;
|
||||
|
||||
atomic_t<bool> clipboard_enabled = false; // For copy and paste
|
||||
atomic_t<bool> half_byte_kana_enabled = false;
|
||||
atomic_t<u32> supported_languages = 0; // Used to enable non-default languages in the OSK
|
||||
|
||||
atomic_t<bool> dimmer_enabled = true;
|
||||
atomic_t<f32> base_color_red = 1.0f;
|
||||
atomic_t<f32> base_color_blue = 1.0f;
|
||||
atomic_t<f32> base_color_green = 1.0f;
|
||||
atomic_t<f32> base_color_alpha = 1.0f;
|
||||
|
||||
atomic_t<bool> pointer_enabled = false;
|
||||
CellOskDialogPoint pointer_pos{0.0f, 0.0f};
|
||||
atomic_t<f32> initial_scale = 1.0f;
|
||||
|
||||
atomic_t<u32> layout_mode = CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT | CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP;
|
||||
|
||||
atomic_t<CellOskDialogContinuousMode> osk_continuous_mode = CELL_OSKDIALOG_CONTINUOUS_MODE_NONE;
|
||||
atomic_t<u32> last_dialog_state = CELL_SYSUTIL_OSKDIALOG_UNLOADED; // Used for continuous seperate window dialog
|
||||
|
||||
atomic_t<vm::ptr<cellOskDialogConfirmWordFilterCallback>> osk_confirm_callback{};
|
||||
|
||||
stx::init_mutex init;
|
||||
|
||||
void reset()
|
||||
{
|
||||
std::lock_guard lock(text_mtx);
|
||||
|
||||
dlg.reset();
|
||||
valid_text = {};
|
||||
use_separate_windows = false;
|
||||
lock_ext_input = false;
|
||||
device_mask = 0;
|
||||
key_layout = CELL_OSKDIALOG_10KEY_PANEL;
|
||||
initial_key_layout = CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_SYSTEM;
|
||||
initial_input_device = CELL_OSKDIALOG_INPUT_DEVICE_PAD;
|
||||
clipboard_enabled = false;
|
||||
half_byte_kana_enabled = false;
|
||||
supported_languages = 0;
|
||||
dimmer_enabled = true;
|
||||
base_color_red = 1.0f;
|
||||
base_color_blue = 1.0f;
|
||||
base_color_green = 1.0f;
|
||||
base_color_alpha = 1.0f;
|
||||
pointer_enabled = false;
|
||||
pointer_pos = {0.0f, 0.0f};
|
||||
initial_scale = 1.0f;
|
||||
layout_mode = CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT | CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP;
|
||||
osk_continuous_mode = CELL_OSKDIALOG_CONTINUOUS_MODE_NONE;
|
||||
last_dialog_state = CELL_SYSUTIL_OSKDIALOG_UNLOADED;
|
||||
osk_confirm_callback.store({});
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: don't use this function
|
||||
@@ -76,17 +148,15 @@ std::shared_ptr<OskDialogBase> _get_osk_dialog(bool create = false)
|
||||
|
||||
return osk.dlg;
|
||||
}
|
||||
else
|
||||
|
||||
const auto init = osk.init.access();
|
||||
|
||||
if (!init)
|
||||
{
|
||||
const auto init = osk.init.access();
|
||||
|
||||
if (!init)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return osk.dlg;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return osk.dlg;
|
||||
}
|
||||
|
||||
error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dialogParam, vm::ptr<CellOskDialogInputFieldInfo> inputFieldInfo)
|
||||
@@ -101,7 +171,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
||||
auto osk = _get_osk_dialog(true);
|
||||
|
||||
// Can't open another dialog if this one is already open.
|
||||
if (!osk || osk->state.load() == OskDialogState::Open)
|
||||
if (!osk || osk->state.load() != OskDialogState::Unloaded)
|
||||
{
|
||||
return CELL_SYSUTIL_ERROR_BUSY;
|
||||
}
|
||||
@@ -115,14 +185,19 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
||||
// Get init text and prepare return value
|
||||
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK;
|
||||
std::memset(osk->osk_text, 0, sizeof(osk->osk_text));
|
||||
std::memset(osk->osk_text_old, 0, sizeof(osk->osk_text_old));
|
||||
|
||||
// Also clear the info text just to be sure (it should be zeroed at this point anyway)
|
||||
{
|
||||
auto& info = g_fxo->get<osk_info>();
|
||||
std::lock_guard lock(info.text_mtx);
|
||||
info.valid_text = {};
|
||||
}
|
||||
|
||||
if (inputFieldInfo->init_text)
|
||||
{
|
||||
for (u32 i = 0; (i < maxLength) && (inputFieldInfo->init_text[i] != 0); i++)
|
||||
{
|
||||
osk->osk_text[i] = inputFieldInfo->init_text[i];
|
||||
osk->osk_text_old[i] = inputFieldInfo->init_text[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,33 +217,23 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
||||
|
||||
osk->on_osk_close = [wptr = std::weak_ptr<OskDialogBase>(osk)](s32 status)
|
||||
{
|
||||
cellOskDialog.error("on_osk_close(status=%d)", status);
|
||||
|
||||
const auto osk = wptr.lock();
|
||||
osk->state = OskDialogState::Closed;
|
||||
|
||||
if (osk->state.atomic_op([&](OskDialogState& state)
|
||||
auto& info = g_fxo->get<osk_info>();
|
||||
|
||||
const bool keep_seperate_window_open = info.use_separate_windows.load() && (info.osk_continuous_mode.load() != CELL_OSKDIALOG_CONTINUOUS_MODE_NONE);
|
||||
|
||||
switch (status)
|
||||
{
|
||||
if (state == OskDialogState::Abort)
|
||||
case CELL_OSKDIALOG_CLOSE_CONFIRM:
|
||||
{
|
||||
if (auto ccb = info.osk_confirm_callback.exchange({}))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
state = OskDialogState::Close;
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
input::SetIntercepted(false);
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool accepted = status == CELL_MSGDIALOG_BUTTON_OK;
|
||||
|
||||
if (accepted)
|
||||
{
|
||||
if (auto ccb = g_fxo->get<osk_info>().osk_confirm_callback.exchange({}))
|
||||
{
|
||||
vm::ptr<u16> string_to_send = vm::cast(vm::alloc(CELL_OSKDIALOG_STRING_SIZE * 2, vm::main));
|
||||
std::vector<u16> string_to_send(CELL_OSKDIALOG_STRING_SIZE);
|
||||
atomic_t<bool> done = false;
|
||||
u32 return_value;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
|
||||
@@ -177,14 +242,16 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
||||
if (osk->osk_text[i] == 0) break;
|
||||
}
|
||||
|
||||
sysutil_register_cb([&, length = i](ppu_thread& cb_ppu) -> s32
|
||||
sysutil_register_cb([&, length = i, string_to_send = std::move(string_to_send)](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
return_value = ccb(cb_ppu, string_to_send, static_cast<s32>(length));
|
||||
vm::var<u16[], vm::page_allocator<>> string_var(CELL_OSKDIALOG_STRING_SIZE, string_to_send.data());
|
||||
|
||||
const u32 return_value = ccb(cb_ppu, string_var.begin(), static_cast<s32>(length));
|
||||
cellOskDialog.warning("osk_confirm_callback return_value=%d", return_value);
|
||||
|
||||
for (u32 i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
|
||||
{
|
||||
osk->osk_text[i] = string_to_send[i];
|
||||
osk->osk_text[i] = string_var.begin()[i];
|
||||
}
|
||||
|
||||
done = true;
|
||||
@@ -198,7 +265,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
||||
}
|
||||
}
|
||||
|
||||
if (g_fxo->get<osk_info>().use_separate_windows.load() && osk->osk_text[0] == 0)
|
||||
if (info.use_separate_windows.load() && osk->osk_text[0] == 0)
|
||||
{
|
||||
cellOskDialog.warning("cellOskDialogLoadAsync: input result is CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT");
|
||||
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT;
|
||||
@@ -207,41 +274,74 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
||||
{
|
||||
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
case CELL_OSKDIALOG_CLOSE_CANCEL:
|
||||
{
|
||||
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_CANCELED;
|
||||
break;
|
||||
}
|
||||
case FAKE_CELL_OSKDIALOG_CLOSE_ABORT:
|
||||
{
|
||||
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_ABORT;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellOskDialog.fatal("on_osk_close: Unknown status (%d)", status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Send OSK status
|
||||
if (g_fxo->get<osk_info>().use_separate_windows.load() && (g_fxo->get<osk_info>().osk_continuous_mode.load() != CELL_OSKDIALOG_CONTINUOUS_MODE_NONE))
|
||||
if (keep_seperate_window_open)
|
||||
{
|
||||
if (accepted)
|
||||
switch (status)
|
||||
{
|
||||
case CELL_OSKDIALOG_CLOSE_CONFIRM:
|
||||
{
|
||||
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED;
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED, 0);
|
||||
break;
|
||||
}
|
||||
else
|
||||
case CELL_OSKDIALOG_CLOSE_CANCEL:
|
||||
{
|
||||
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED;
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED, 0);
|
||||
break;
|
||||
}
|
||||
case FAKE_CELL_OSKDIALOG_CLOSE_ABORT:
|
||||
{
|
||||
// Handled in cellOskDialogAbort
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellOskDialog.fatal("on_osk_close: Unknown status (%d)", status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (info.osk_continuous_mode.load() == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
|
||||
{
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_DISPLAY_CHANGED, CELL_OSKDIALOG_DISPLAY_STATUS_HIDE);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (status != FAKE_CELL_OSKDIALOG_CLOSE_ABORT) // Handled in cellOskDialogAbort
|
||||
{
|
||||
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_FINISHED;
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
|
||||
}
|
||||
|
||||
input::SetIntercepted(false);
|
||||
};
|
||||
|
||||
osk->on_osk_input_entered = [wptr = std::weak_ptr<OskDialogBase>(osk)]()
|
||||
if (auto& info = g_fxo->get<osk_info>(); info.osk_continuous_mode == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
|
||||
{
|
||||
const auto osk = wptr.lock();
|
||||
|
||||
if (g_fxo->get<osk_info>().use_separate_windows.load())
|
||||
{
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED, 0);
|
||||
}
|
||||
};
|
||||
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_LOADED;
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_LOADED, 0);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
input::SetIntercepted(true);
|
||||
|
||||
@@ -250,8 +350,14 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
||||
osk->Create(get_localized_string(localized_string_id::CELL_OSK_DIALOG_TITLE), message, osk->osk_text, maxLength, prohibitFlgs, allowOskPanelFlg, firstViewPanel);
|
||||
result = true;
|
||||
result.notify_one();
|
||||
|
||||
if (g_fxo->get<osk_info>().osk_continuous_mode.load() == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
|
||||
{
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_DISPLAY_CHANGED, CELL_OSKDIALOG_DISPLAY_STATUS_SHOW);
|
||||
}
|
||||
});
|
||||
|
||||
g_fxo->get<osk_info>().last_dialog_state = CELL_SYSUTIL_OSKDIALOG_LOADED;
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_LOADED, 0);
|
||||
|
||||
while (!result && !Emu.IsStopped())
|
||||
@@ -264,7 +370,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
||||
|
||||
error_code cellOskDialogLoadAsyncExt()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellOskDialog);
|
||||
cellOskDialog.todo("cellOskDialogLoadAsyncExt()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -282,13 +388,28 @@ error_code getText(vm::ptr<CellOskDialogCallbackReturnParam> OutputInfo, bool is
|
||||
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
|
||||
}
|
||||
|
||||
auto& info = g_fxo->get<osk_info>();
|
||||
|
||||
const bool keep_seperate_window_open = info.use_separate_windows.load() && (info.osk_continuous_mode.load() != CELL_OSKDIALOG_CONTINUOUS_MODE_NONE);
|
||||
|
||||
info.text_mtx.lock();
|
||||
|
||||
// Update text buffer if called from cellOskDialogUnloadAsync or if the user accepted the dialog during continuous seperate window mode.
|
||||
if (is_unload || (keep_seperate_window_open && info.last_dialog_state == CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED))
|
||||
{
|
||||
for (s32 i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
|
||||
{
|
||||
info.valid_text[i] = osk->osk_text[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (is_unload)
|
||||
{
|
||||
OutputInfo->result = osk->osk_input_result.load();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (memcmp(osk->osk_text_old, osk->osk_text, sizeof(osk->osk_text)) == 0)
|
||||
if (info.valid_text[0] == 0)
|
||||
{
|
||||
OutputInfo->result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT;
|
||||
}
|
||||
@@ -298,41 +419,54 @@ error_code getText(vm::ptr<CellOskDialogCallbackReturnParam> OutputInfo, bool is
|
||||
}
|
||||
}
|
||||
|
||||
bool do_copy = OutputInfo->pResultString && (OutputInfo->result == CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK || (is_unload && OutputInfo->result == CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT));
|
||||
const bool do_copy = OutputInfo->pResultString && (OutputInfo->result == CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK || (is_unload && OutputInfo->result == CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT));
|
||||
|
||||
for (s32 i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
|
||||
for (s32 i = 0; do_copy && i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
|
||||
{
|
||||
osk->osk_text_old[i] = osk->osk_text[i];
|
||||
|
||||
if (do_copy)
|
||||
if (i < OutputInfo->numCharsResultString)
|
||||
{
|
||||
if (i < OutputInfo->numCharsResultString)
|
||||
OutputInfo->pResultString[i] = info.valid_text[i];
|
||||
|
||||
if (info.valid_text[i] == 0)
|
||||
{
|
||||
if (osk->osk_text[i] == 0)
|
||||
{
|
||||
do_copy = false;
|
||||
}
|
||||
OutputInfo->pResultString[i] = osk->osk_text[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputInfo->pResultString[i] = 0;
|
||||
do_copy = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputInfo->pResultString[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
info.text_mtx.unlock();
|
||||
|
||||
if (is_unload)
|
||||
{
|
||||
// Unload should be called last, so remove the dialog here
|
||||
if (const auto reset_lock = g_fxo->get<osk_info>().init.reset())
|
||||
if (const auto reset_lock = info.init.reset())
|
||||
{
|
||||
// TODO
|
||||
g_fxo->get<osk_info>().dlg.reset();
|
||||
info.reset();
|
||||
}
|
||||
|
||||
osk->state = OskDialogState::Unloaded;
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_UNLOADED, 0);
|
||||
}
|
||||
else if (keep_seperate_window_open)
|
||||
{
|
||||
// Clear text buffer unless the dialog is still open during continuous seperate window mode.
|
||||
switch (info.last_dialog_state)
|
||||
{
|
||||
case CELL_SYSUTIL_OSKDIALOG_FINISHED:
|
||||
case CELL_SYSUTIL_OSKDIALOG_UNLOADED:
|
||||
case CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED:
|
||||
case CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED:
|
||||
info.valid_text = {};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -379,18 +513,12 @@ error_code cellOskDialogAbort()
|
||||
|
||||
const s32 result = osk->state.atomic_op([](OskDialogState& state)
|
||||
{
|
||||
// Check for open dialog. In this case the dialog is only "Open" if it was not aborted before.
|
||||
if (state == OskDialogState::Abort)
|
||||
// Check for open dialog. In this case the dialog is "Open" if it was not unloaded before.
|
||||
if (state == OskDialogState::Unloaded)
|
||||
{
|
||||
return static_cast<s32>(CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED);
|
||||
}
|
||||
|
||||
// If the dialog has the Open state then it is in use. Only dialogs with the Close state can be aborted.
|
||||
if (state == OskDialogState::Open)
|
||||
{
|
||||
return static_cast<s32>(CELL_SYSUTIL_ERROR_BUSY);
|
||||
}
|
||||
|
||||
state = OskDialogState::Abort;
|
||||
|
||||
return static_cast<s32>(CELL_OK);
|
||||
@@ -402,7 +530,10 @@ error_code cellOskDialogAbort()
|
||||
}
|
||||
|
||||
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_ABORT;
|
||||
osk->Close(false);
|
||||
osk->Close(FAKE_CELL_OSKDIALOG_CLOSE_ABORT);
|
||||
|
||||
g_fxo->get<osk_info>().last_dialog_state = CELL_SYSUTIL_OSKDIALOG_FINISHED;
|
||||
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -410,6 +541,13 @@ error_code cellOskDialogAbort()
|
||||
error_code cellOskDialogSetDeviceMask(u32 deviceMask)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogSetDeviceMask(deviceMask=0x%x)", deviceMask);
|
||||
|
||||
// TODO: error checks. It probably checks for use_separate_windows
|
||||
|
||||
g_fxo->get<osk_info>().device_mask = deviceMask;
|
||||
|
||||
// TODO: change osk device input
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -422,30 +560,52 @@ error_code cellOskDialogSetSeparateWindowOption(vm::ptr<CellOskDialogSeparateWin
|
||||
return CELL_OSKDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (auto& osk = g_fxo->get<osk_info>(); true)
|
||||
{
|
||||
osk.use_separate_windows = true;
|
||||
osk.osk_continuous_mode = static_cast<CellOskDialogContinuousMode>(+windowOption->continuousMode);
|
||||
}
|
||||
auto& osk = g_fxo->get<osk_info>();
|
||||
osk.use_separate_windows = true;
|
||||
osk.osk_continuous_mode = static_cast<CellOskDialogContinuousMode>(+windowOption->continuousMode);
|
||||
// TODO: handle rest of windowOption
|
||||
|
||||
cellOskDialog.warning("cellOskDialogSetSeparateWindowOption: continuousMode=%s)", osk.osk_continuous_mode.load());
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogSetInitialInputDevice(vm::ptr<CellOskDialogInputDevice> inputDevice)
|
||||
error_code cellOskDialogSetInitialInputDevice(u32 inputDevice)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogSetInitialInputDevice(inputDevice=*0x%x)", inputDevice);
|
||||
cellOskDialog.todo("cellOskDialogSetInitialInputDevice(inputDevice=%d)", inputDevice);
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().initial_input_device = static_cast<CellOskDialogInputDevice>(inputDevice);
|
||||
|
||||
// TODO: use value
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogSetInitialKeyLayout(vm::ptr<CellOskDialogInitialKeyLayout> initialKeyLayout)
|
||||
error_code cellOskDialogSetInitialKeyLayout(u32 initialKeyLayout)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogSetInitialKeyLayout(initialKeyLayout=*0x%x)", initialKeyLayout);
|
||||
cellOskDialog.todo("cellOskDialogSetInitialKeyLayout(initialKeyLayout=%d)", initialKeyLayout);
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().initial_key_layout = static_cast<CellOskDialogInitialKeyLayout>(initialKeyLayout);
|
||||
|
||||
// TODO: use value
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogDisableDimmer()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogDisableDimmer()");
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().dimmer_enabled = false;
|
||||
|
||||
// TODO: use value
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -458,18 +618,47 @@ error_code cellOskDialogSetKeyLayoutOption(u32 option)
|
||||
return CELL_OSKDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
g_fxo->get<osk_info>().key_layout = option;
|
||||
|
||||
// TODO: use value
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogAddSupportLanguage(u32 supportLanguage)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogAddSupportLanguage(supportLanguage=0x%x)", supportLanguage);
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().supported_languages = supportLanguage;
|
||||
|
||||
// TODO: disable extra languages unless they were enabled here
|
||||
// Extra languages are:
|
||||
// CELL_OSKDIALOG_PANELMODE_POLISH
|
||||
// CELL_OSKDIALOG_PANELMODE_KOREAN
|
||||
// CELL_OSKDIALOG_PANELMODE_TURKEY
|
||||
// CELL_OSKDIALOG_PANELMODE_TRADITIONAL_CHINESE
|
||||
// CELL_OSKDIALOG_PANELMODE_SIMPLIFIED_CHINESE
|
||||
// CELL_OSKDIALOG_PANELMODE_PORTUGUESE_BRAZIL
|
||||
// CELL_OSKDIALOG_PANELMODE_DANISH
|
||||
// CELL_OSKDIALOG_PANELMODE_SWEDISH
|
||||
// CELL_OSKDIALOG_PANELMODE_NORWEGIAN
|
||||
// CELL_OSKDIALOG_PANELMODE_FINNISH
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogSetLayoutMode(s32 layoutMode)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogSetLayoutMode(layoutMode=%d)", layoutMode);
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().layout_mode = layoutMode;
|
||||
|
||||
// TODO: use layout mode
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -479,12 +668,6 @@ error_code cellOskDialogGetInputText(vm::ptr<CellOskDialogCallbackReturnParam> O
|
||||
return getText(OutputInfo, false);
|
||||
}
|
||||
|
||||
error_code cellOskDialogExtInputDeviceUnlock()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtInputDeviceUnlock()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code register_keyboard_event_hook_callback(u16 hookEventMode, vm::ptr<cellOskDialogHardwareKeyboardEventHookCallback> pCallback)
|
||||
{
|
||||
cellOskDialog.todo("register_keyboard_event_hook_callback(hookEventMode=%u, pCallback=*0x%x)", hookEventMode, pCallback);
|
||||
@@ -494,6 +677,8 @@ error_code register_keyboard_event_hook_callback(u16 hookEventMode, vm::ptr<cell
|
||||
return CELL_OSKDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
// TODO: register callback and and use it
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -530,6 +715,13 @@ error_code cellOskDialogExtAddJapaneseOptionDictionary(vm::cpptr<char> filePath)
|
||||
error_code cellOskDialogExtEnableClipboard()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtEnableClipboard()");
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().clipboard_enabled = true;
|
||||
|
||||
// TODO: implement copy paste
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -539,13 +731,13 @@ error_code cellOskDialogExtSendFinishMessage(u32 /*CellOskDialogFinishReason*/ f
|
||||
|
||||
const auto osk = _get_osk_dialog();
|
||||
|
||||
// Check for open dialog.
|
||||
if (!osk || osk->state.load() != OskDialogState::Open)
|
||||
// Check for "Open" dialog.
|
||||
if (!osk || osk->state.load() == OskDialogState::Unloaded)
|
||||
{
|
||||
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
|
||||
}
|
||||
|
||||
osk->Close(finishReason == CELL_OSKDIALOG_CLOSE_CONFIRM);
|
||||
osk->Close(finishReason);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -559,18 +751,56 @@ error_code cellOskDialogExtAddOptionDictionary(vm::cptr<CellOskDialogImeDictiona
|
||||
error_code cellOskDialogExtSetInitialScale(f32 initialScale)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtSetInitialScale(initialScale=%f)", initialScale);
|
||||
|
||||
// TODO: error checks (CELL_OSKDIALOG_SCALE_MIN, CELL_OSKDIALOG_SCALE_MAX)
|
||||
|
||||
g_fxo->get<osk_info>().initial_scale = initialScale;
|
||||
|
||||
// TODO: implement overlay scaling
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogExtInputDeviceLock()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtInputDeviceLock()");
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().lock_ext_input = true;
|
||||
|
||||
// TODO: change osk device input
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogExtInputDeviceUnlock()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtInputDeviceUnlock()");
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().lock_ext_input = false;
|
||||
|
||||
// TODO: change osk device input
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogExtSetBaseColor(f32 red, f32 blue, f32 green, f32 alpha)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtSetBaseColor(red=%f, blue=%f, green=%f, alpha=%f)", red, blue, green, alpha);
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
auto& osk = g_fxo->get<osk_info>();
|
||||
osk.base_color_red = red;
|
||||
osk.base_color_blue = blue;
|
||||
osk.base_color_green = green;
|
||||
osk.base_color_alpha = alpha;
|
||||
|
||||
// TODO: use osk base color
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -583,10 +813,7 @@ error_code cellOskDialogExtRegisterConfirmWordFilterCallback(vm::ptr<cellOskDial
|
||||
return CELL_OSKDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (auto& osk = g_fxo->get<osk_info>(); true)
|
||||
{
|
||||
osk.osk_confirm_callback = pCallback;
|
||||
}
|
||||
g_fxo->get<osk_info>().osk_confirm_callback = pCallback;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -594,30 +821,82 @@ error_code cellOskDialogExtRegisterConfirmWordFilterCallback(vm::ptr<cellOskDial
|
||||
error_code cellOskDialogExtUpdateInputText()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtUpdateInputText()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogExtDisableHalfByteKana()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtDisableHalfByteKana()");
|
||||
// Usually, user input is only available when the dialog was accepted.
|
||||
// This function seems to be called in order to copy the current text to an internal buffer.
|
||||
// Afterwards, cellOskDialogGetInputText can be called to fetch the current text regardless of
|
||||
// user confirmation, even if the dialog is still in use.
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
const auto osk = _get_osk_dialog();
|
||||
|
||||
if (osk)
|
||||
{
|
||||
auto& info = g_fxo->get<osk_info>();
|
||||
std::lock_guard lock(info.text_mtx);
|
||||
|
||||
for (s32 i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
|
||||
{
|
||||
info.valid_text[i] = osk->osk_text[i];
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogExtSetPointerEnable(b8 enable)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtSetPointerEnable(enable=%d)", enable);
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().pointer_enabled = enable;
|
||||
|
||||
// TODO: use new value in osk
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogExtUpdatePointerDisplayPos(/*const CellOskDialogPoint pos*/)
|
||||
error_code cellOskDialogExtUpdatePointerDisplayPos(vm::cptr<CellOskDialogPoint> pos)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtUpdatePointerDisplayPos(posX=%f, posY=%f)"/*, pos.x, pos.y*/);
|
||||
cellOskDialog.todo("cellOskDialogExtUpdatePointerDisplayPos(pos=0x%x, posX=%f, posY=%f)", pos->x, pos->y);
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
if (pos)
|
||||
{
|
||||
g_fxo->get<osk_info>().pointer_pos = *pos;
|
||||
}
|
||||
|
||||
// TODO: use new value in osk
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogExtEnableHalfByteKana()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtEnableHalfByteKana()");
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().half_byte_kana_enabled = true;
|
||||
|
||||
// TODO: use new value in osk
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellOskDialogExtDisableHalfByteKana()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtDisableHalfByteKana()");
|
||||
|
||||
// TODO: error checks
|
||||
|
||||
g_fxo->get<osk_info>().half_byte_kana_enabled = false;
|
||||
|
||||
// TODO: use new value in osk
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -630,6 +909,8 @@ error_code cellOskDialogExtRegisterForceFinishCallback(vm::ptr<cellOskDialogForc
|
||||
return CELL_OSKDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
// TODO: register and use force finish callback (PS button during continuous mode)
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,11 @@ enum CellOskDialogFinishReason
|
||||
CELL_OSKDIALOG_CLOSE_CANCEL = 1,
|
||||
};
|
||||
|
||||
enum CellOskDialogFinishReasonFake // Helper. Must be negative values.
|
||||
{
|
||||
FAKE_CELL_OSKDIALOG_CLOSE_ABORT = -1,
|
||||
};
|
||||
|
||||
enum CellOskDialogType
|
||||
{
|
||||
CELL_OSKDIALOG_TYPE_SINGLELINE_OSK = 0,
|
||||
@@ -239,24 +244,28 @@ using cellOskDialogForceFinishCallback = class b8();
|
||||
|
||||
enum class OskDialogState
|
||||
{
|
||||
Unloaded,
|
||||
Open,
|
||||
Abort,
|
||||
Close,
|
||||
Closed
|
||||
};
|
||||
|
||||
class OskDialogBase
|
||||
{
|
||||
public:
|
||||
virtual void Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit, u32 prohibit_flags, u32 panel_flag, u32 first_view_panel) = 0;
|
||||
virtual void Close(bool accepted) = 0;
|
||||
|
||||
// Closes the dialog.
|
||||
// Set status to CELL_OSKDIALOG_CLOSE_CONFIRM or CELL_OSKDIALOG_CLOSE_CANCEL for user input.
|
||||
// Set status to -1 if closed by the game or system.
|
||||
virtual void Close(s32 status) = 0;
|
||||
virtual ~OskDialogBase();
|
||||
|
||||
std::function<void(s32 status)> on_osk_close;
|
||||
std::function<void()> on_osk_input_entered;
|
||||
|
||||
atomic_t<OskDialogState> state{ OskDialogState::Close };
|
||||
atomic_t<OskDialogState> state{ OskDialogState::Unloaded };
|
||||
|
||||
atomic_t<CellOskDialogInputFieldResult> osk_input_result{ CellOskDialogInputFieldResult::CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK };
|
||||
char16_t osk_text[CELL_OSKDIALOG_STRING_SIZE]{};
|
||||
char16_t osk_text_old[CELL_OSKDIALOG_STRING_SIZE]{};
|
||||
};
|
||||
|
||||
@@ -113,6 +113,81 @@ void parse_metadata(D& dst, const utils::media_info& mi, const std::string& key,
|
||||
strcpy_trunc(dst, value);
|
||||
};
|
||||
|
||||
error_code check_search_state(search_state state, search_state action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case search_state::initializing:
|
||||
switch (state)
|
||||
{
|
||||
case search_state::not_initialized:
|
||||
break;
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
break;
|
||||
case search_state::finalizing:
|
||||
switch (state)
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::initializing:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
}
|
||||
break;
|
||||
case search_state::canceling:
|
||||
switch (state)
|
||||
{
|
||||
case search_state::in_progress:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
case search_state::idle:
|
||||
return CELL_SEARCH_ERROR_ALREADY_GOT_RESULT;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
}
|
||||
break;
|
||||
case search_state::in_progress:
|
||||
default:
|
||||
switch (state)
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
void populate_music_info(CellSearchMusicInfo& info, const utils::media_info& mi, const fs::dir_entry& item)
|
||||
{
|
||||
parse_metadata(info.artistName, mi, "artist", "Unknown Artist", CELL_SEARCH_TITLE_LEN_MAX);
|
||||
@@ -296,16 +371,9 @@ error_code cellSearchInitialize(CellSearchMode mode, u32 container, vm::ptr<Cell
|
||||
|
||||
auto& search = g_fxo->get<search_info>();
|
||||
|
||||
switch (search.state.compare_and_swap(search_state::not_initialized, search_state::initializing))
|
||||
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::not_initialized, search_state::initializing), search_state::initializing))
|
||||
{
|
||||
case search_state::not_initialized:
|
||||
break;
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_ALREADY_INITIALIZED;
|
||||
return error;
|
||||
}
|
||||
|
||||
search.func = func;
|
||||
@@ -327,20 +395,9 @@ error_code cellSearchFinalize()
|
||||
|
||||
auto& search = g_fxo->get<search_info>();
|
||||
|
||||
switch (search.state.compare_and_swap(search_state::idle, search_state::finalizing))
|
||||
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::finalizing), search_state::finalizing))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::initializing:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
sysutil_register_cb([&search](ppu_thread& ppu) -> s32
|
||||
@@ -396,20 +453,9 @@ error_code cellSearchStartListSearch(CellSearchListSearchType type, CellSearchSo
|
||||
|
||||
auto& search = g_fxo->get<search_info>();
|
||||
|
||||
switch (search.state.compare_and_swap(search_state::idle, search_state::in_progress))
|
||||
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::in_progress), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
const u32 id = *outSearchId = idm::make<search_object_t>();
|
||||
@@ -465,12 +511,7 @@ error_code cellSearchStartListSearch(CellSearchListSearchType type, CellSearchSo
|
||||
{
|
||||
item.name = vfs::unescape(item.name);
|
||||
|
||||
if (item.name == "." || item.name == "..")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!item.is_directory)
|
||||
if (item.name == "." || item.name == ".." || !item.is_directory)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -623,20 +664,9 @@ error_code cellSearchStartContentSearchInList(vm::cptr<CellSearchContentId> list
|
||||
|
||||
auto& search = g_fxo->get<search_info>();
|
||||
|
||||
switch (search.state.compare_and_swap(search_state::idle, search_state::in_progress))
|
||||
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::in_progress), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& content_map = g_fxo->get<content_id_map>();
|
||||
@@ -860,20 +890,9 @@ error_code cellSearchStartContentSearch(CellSearchContentSearchType type, CellSe
|
||||
|
||||
auto& search = g_fxo->get<search_info>();
|
||||
|
||||
switch (search.state.compare_and_swap(search_state::idle, search_state::in_progress))
|
||||
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::in_progress), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
const u32 id = *outSearchId = idm::make<search_object_t>();
|
||||
@@ -1033,20 +1052,23 @@ error_code cellSearchStartSceneSearchInVideo(vm::cptr<CellSearchContentId> video
|
||||
|
||||
auto& search = g_fxo->get<search_info>();
|
||||
|
||||
switch (search.state.compare_and_swap(search_state::idle, search_state::in_progress))
|
||||
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::in_progress), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& content_map = g_fxo->get<content_id_map>();
|
||||
auto found = content_map.map.find(*reinterpret_cast<const u64*>(videoId->data));
|
||||
if (found == content_map.map.end())
|
||||
{
|
||||
// content ID not found, perform a search first
|
||||
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
const auto& content_info = found->second;
|
||||
if (content_info->type != CELL_SEARCH_CONTENTTYPE_VIDEO)
|
||||
{
|
||||
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
|
||||
}
|
||||
|
||||
const u32 id = *outSearchId = idm::make<search_object_t>();
|
||||
@@ -1087,22 +1109,27 @@ error_code cellSearchStartSceneSearch(CellSearchSceneSearchType searchType, vm::
|
||||
return CELL_SEARCH_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (tagNum) // TODO: find out if this is the correct location for these checks
|
||||
{
|
||||
if (tagNum > CELL_SEARCH_TAG_NUM_MAX || !tags)
|
||||
{
|
||||
return CELL_SEARCH_ERROR_TAG;
|
||||
}
|
||||
|
||||
for (u32 n = 0; n < tagNum; n++)
|
||||
{
|
||||
if (!tags[tagNum] || !memchr(&tags[tagNum], '\0', CELL_SEARCH_TAG_LEN_MAX))
|
||||
{
|
||||
return CELL_SEARCH_ERROR_TAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& search = g_fxo->get<search_info>();
|
||||
|
||||
switch (search.state.compare_and_swap(search_state::idle, search_state::in_progress))
|
||||
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::in_progress), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
const u32 id = *outSearchId = idm::make<search_object_t>();
|
||||
@@ -1137,48 +1164,38 @@ error_code cellSearchGetContentInfoByOffset(CellSearchId searchId, s32 offset, v
|
||||
return CELL_SEARCH_ERROR_INVALID_SEARCHID;
|
||||
}
|
||||
|
||||
switch (g_fxo->get<search_info>().state.load())
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (offset >= 0 && offset + 0u < searchObject->content_ids.size())
|
||||
{
|
||||
const auto& content_id = searchObject->content_ids[offset];
|
||||
const auto& content_info = content_id.second;
|
||||
|
||||
switch (content_info->type)
|
||||
{
|
||||
case CELL_SEARCH_CONTENTTYPE_MUSIC:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.music, sizeof(content_info->data.music));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.music, sizeof(content_info->data.music));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_PHOTO:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo, sizeof(content_info->data.photo));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo, sizeof(content_info->data.photo));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_VIDEO:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.video, sizeof(content_info->data.photo));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.video, sizeof(content_info->data.photo));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_MUSICLIST:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.music_list, sizeof(content_info->data.music_list));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.music_list, sizeof(content_info->data.music_list));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_PHOTOLIST:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo_list, sizeof(content_info->data.photo_list));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo_list, sizeof(content_info->data.photo_list));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_VIDEOLIST:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.video_list, sizeof(content_info->data.video_list));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.video_list, sizeof(content_info->data.video_list));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_SCENE:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.scene, sizeof(content_info->data.scene));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.scene, sizeof(content_info->data.scene));
|
||||
break;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
@@ -1205,20 +1222,9 @@ error_code cellSearchGetContentInfoByContentId(vm::cptr<CellSearchContentId> con
|
||||
return CELL_SEARCH_ERROR_PARAM;
|
||||
}
|
||||
|
||||
switch (g_fxo->get<search_info>().state.load())
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& content_map = g_fxo->get<content_id_map>();
|
||||
@@ -1229,25 +1235,25 @@ error_code cellSearchGetContentInfoByContentId(vm::cptr<CellSearchContentId> con
|
||||
switch (content_info->type)
|
||||
{
|
||||
case CELL_SEARCH_CONTENTTYPE_MUSIC:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.music, sizeof(content_info->data.music));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.music, sizeof(content_info->data.music));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_PHOTO:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo, sizeof(content_info->data.photo));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo, sizeof(content_info->data.photo));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_VIDEO:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.video, sizeof(content_info->data.photo));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.video, sizeof(content_info->data.photo));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_MUSICLIST:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.music_list, sizeof(content_info->data.music_list));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.music_list, sizeof(content_info->data.music_list));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_PHOTOLIST:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo_list, sizeof(content_info->data.photo_list));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo_list, sizeof(content_info->data.photo_list));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_VIDEOLIST:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.video_list, sizeof(content_info->data.video_list));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.video_list, sizeof(content_info->data.video_list));
|
||||
break;
|
||||
case CELL_SEARCH_CONTENTTYPE_SCENE:
|
||||
std::memcpy(infoBuffer.get_ptr(), &content_info->data.scene, sizeof(content_info->data.scene));
|
||||
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.scene, sizeof(content_info->data.scene));
|
||||
break;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
@@ -1272,20 +1278,9 @@ error_code cellSearchGetOffsetByContentId(CellSearchId searchId, vm::cptr<CellSe
|
||||
return CELL_SEARCH_ERROR_PARAM;
|
||||
}
|
||||
|
||||
switch (g_fxo->get<search_info>().state.load())
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
const auto searchObject = idm::get<search_object_t>(searchId);
|
||||
@@ -1326,20 +1321,9 @@ error_code cellSearchGetContentIdByOffset(CellSearchId searchId, s32 offset, vm:
|
||||
return CELL_SEARCH_ERROR_INVALID_SEARCHID;
|
||||
}
|
||||
|
||||
switch (g_fxo->get<search_info>().state.load())
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (offset >= 0 && offset + 0u < searchObject->content_ids.size())
|
||||
@@ -1367,11 +1351,38 @@ error_code cellSearchGetContentInfoGameComment(vm::cptr<CellSearchContentId> con
|
||||
{
|
||||
cellSearch.todo("cellSearchGetContentInfoGameComment(contentId=*0x%x, gameComment=*0x%x)", contentId, gameComment);
|
||||
|
||||
if (!gameComment)
|
||||
if (!contentId || !gameComment)
|
||||
{
|
||||
return CELL_SEARCH_ERROR_PARAM;
|
||||
}
|
||||
|
||||
// TODO: find out if this check is correct
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& content_map = g_fxo->get<content_id_map>();
|
||||
auto found = content_map.map.find(*reinterpret_cast<const u64*>(contentId->data));
|
||||
if (found == content_map.map.end())
|
||||
{
|
||||
// content ID not found, perform a search first
|
||||
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
const auto& content_info = found->second;
|
||||
switch (content_info->type)
|
||||
{
|
||||
case CELL_SEARCH_CONTENTTYPE_MUSIC:
|
||||
case CELL_SEARCH_CONTENTTYPE_PHOTO:
|
||||
case CELL_SEARCH_CONTENTTYPE_VIDEO:
|
||||
break;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
|
||||
}
|
||||
|
||||
// TODO: retrieve gameComment
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1391,6 +1402,63 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
|
||||
return CELL_SEARCH_ERROR_INVALID_SEARCHID;
|
||||
}
|
||||
|
||||
// TODO: find out if this check is correct
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (searchObject->content_ids.empty())
|
||||
{
|
||||
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Use the first track in order to get info about this search
|
||||
const auto& first_content_id = searchObject->content_ids[0];
|
||||
const auto& first_content = first_content_id.second;
|
||||
ensure(first_content);
|
||||
|
||||
if (contentId)
|
||||
{
|
||||
// Try to find the specified content
|
||||
const u64 content_hash = *reinterpret_cast<const u64*>(contentId->data);
|
||||
auto content = std::find_if(searchObject->content_ids.begin(), searchObject->content_ids.end(), [&content_hash](const content_id_type& cid){ return cid.first == content_hash; });
|
||||
if (content != searchObject->content_ids.cend() && content->second)
|
||||
{
|
||||
// Check if the type of the found content is correct
|
||||
if (content->second->type != first_content->type)
|
||||
{
|
||||
return CELL_SEARCH_ERROR_NOT_SUPPORTED_CONTEXT;
|
||||
}
|
||||
|
||||
// TODO: Use the found content
|
||||
// first track/playlist of context = content->second;
|
||||
}
|
||||
else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
|
||||
{
|
||||
// Abort if we can't find the playlist.
|
||||
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Select the first track by default
|
||||
// first track of context = first_content;
|
||||
}
|
||||
}
|
||||
else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
|
||||
{
|
||||
// Abort if we don't have the necessary info to select a playlist.
|
||||
return CELL_SEARCH_ERROR_NOT_SUPPORTED_CONTEXT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Select the first track by default
|
||||
// first track of context = first_content;
|
||||
}
|
||||
|
||||
// TODO: Use repeatMode and option in our context, depending on the type (list vs single)
|
||||
// TODO: assign outContext
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1403,6 +1471,28 @@ error_code cellSearchGetMusicSelectionContextOfSingleTrack(vm::cptr<CellSearchCo
|
||||
return CELL_SEARCH_ERROR_PARAM;
|
||||
}
|
||||
|
||||
// TODO: find out if this check is correct
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& content_map = g_fxo->get<content_id_map>();
|
||||
auto found = content_map.map.find(*reinterpret_cast<const u64*>(contentId->data));
|
||||
if (found == content_map.map.end())
|
||||
{
|
||||
// content ID not found, perform a search first
|
||||
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
const auto& content_info = found->second;
|
||||
if (content_info->type != CELL_SEARCH_CONTENTTYPE_MUSIC)
|
||||
{
|
||||
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
|
||||
}
|
||||
|
||||
// TODO: assign outContext
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1415,20 +1505,9 @@ error_code cellSearchGetContentInfoPath(vm::cptr<CellSearchContentId> contentId,
|
||||
return CELL_SEARCH_ERROR_PARAM;
|
||||
}
|
||||
|
||||
switch (g_fxo->get<search_info>().state.load())
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
const u64 id = *reinterpret_cast<const u64*>(contentId->data);
|
||||
@@ -1453,11 +1532,34 @@ error_code cellSearchGetContentInfoPathMovieThumb(vm::cptr<CellSearchContentId>
|
||||
{
|
||||
cellSearch.todo("cellSearchGetContentInfoPathMovieThumb(contentId=*0x%x, infoMt=*0x%x)", contentId, infoMt);
|
||||
|
||||
if (!infoMt)
|
||||
if (!contentId || !infoMt)
|
||||
{
|
||||
return CELL_SEARCH_ERROR_PARAM;
|
||||
}
|
||||
|
||||
// TODO: find out if this check is correct
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& content_map = g_fxo->get<content_id_map>();
|
||||
auto found = content_map.map.find(*reinterpret_cast<const u64*>(contentId->data));
|
||||
if (found == content_map.map.end())
|
||||
{
|
||||
// content ID not found, perform a search first
|
||||
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
const auto& content_info = found->second;
|
||||
if (content_info->type != CELL_SEARCH_CONTENTTYPE_VIDEO)
|
||||
{
|
||||
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
|
||||
}
|
||||
|
||||
strcpy_trunc(infoMt->movieThumbnailPath, content_info->infoPath.thumbnailPath);
|
||||
// TODO: set infoMt->movieThumbnailOption
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1471,20 +1573,10 @@ error_code cellSearchPrepareFile(vm::cptr<char> path)
|
||||
}
|
||||
|
||||
auto& search = g_fxo->get<search_info>();
|
||||
switch (search.state.load())
|
||||
|
||||
if (error_code error = check_search_state(search.state.load(), search_state::in_progress))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
reader_lock lock(search.links_mutex);
|
||||
@@ -1506,6 +1598,32 @@ error_code cellSearchGetContentInfoDeveloperData(vm::cptr<CellSearchContentId> c
|
||||
return CELL_SEARCH_ERROR_PARAM;
|
||||
}
|
||||
|
||||
// TODO: find out if this check is correct
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& content_map = g_fxo->get<content_id_map>();
|
||||
auto found = content_map.map.find(*reinterpret_cast<const u64*>(contentId->data));
|
||||
if (found == content_map.map.end())
|
||||
{
|
||||
// content ID not found, perform a search first
|
||||
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
const auto& content_info = found->second;
|
||||
switch (content_info->type)
|
||||
{
|
||||
case CELL_SEARCH_CONTENTTYPE_VIDEO:
|
||||
case CELL_SEARCH_CONTENTTYPE_SCENE:
|
||||
break;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
|
||||
}
|
||||
|
||||
// TODO: retrieve developerData
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1518,6 +1636,29 @@ error_code cellSearchGetContentInfoSharable(vm::cptr<CellSearchContentId> conten
|
||||
return CELL_SEARCH_ERROR_PARAM;
|
||||
}
|
||||
|
||||
// TODO: find out if this check is correct
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& content_map = g_fxo->get<content_id_map>();
|
||||
auto found = content_map.map.find(*reinterpret_cast<const u64*>(contentId->data));
|
||||
if (found == content_map.map.end())
|
||||
{
|
||||
// content ID not found, perform a search first
|
||||
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
const auto& content_info = found->second;
|
||||
if (content_info->type != CELL_SEARCH_CONTENTTYPE_VIDEO)
|
||||
{
|
||||
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
|
||||
}
|
||||
|
||||
// TODO: retrieve sharable
|
||||
*sharable = CELL_SEARCH_SHARABLETYPE_PROHIBITED;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1532,21 +1673,9 @@ error_code cellSearchCancel(CellSearchId searchId)
|
||||
return CELL_SEARCH_ERROR_INVALID_SEARCHID;
|
||||
}
|
||||
|
||||
switch (g_fxo->get<search_info>().state.load())
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::canceling))
|
||||
{
|
||||
case search_state::in_progress:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
case search_state::initializing:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
case search_state::idle:
|
||||
return CELL_SEARCH_ERROR_ALREADY_GOT_RESULT;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
// TODO
|
||||
@@ -1558,20 +1687,9 @@ error_code cellSearchEnd(CellSearchId searchId)
|
||||
{
|
||||
cellSearch.todo("cellSearchEnd(searchId=0x%x)", searchId);
|
||||
|
||||
switch (g_fxo->get<search_info>().state.load())
|
||||
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::finalizing))
|
||||
{
|
||||
case search_state::idle:
|
||||
break;
|
||||
case search_state::not_initialized:
|
||||
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
|
||||
case search_state::finalizing:
|
||||
return CELL_SEARCH_ERROR_FINALIZING;
|
||||
case search_state::in_progress:
|
||||
case search_state::initializing:
|
||||
case search_state::canceling:
|
||||
return CELL_SEARCH_ERROR_BUSY;
|
||||
default:
|
||||
return CELL_SEARCH_ERROR_GENERIC;
|
||||
return error;
|
||||
}
|
||||
|
||||
const auto searchObject = idm::get<search_object_t>(searchId);
|
||||
|
||||
@@ -110,6 +110,15 @@ error_code cellSysutilAvc2StartVoiceDetection()
|
||||
error_code cellSysutilAvc2UnloadAsync()
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2UnloadAsync()");
|
||||
|
||||
if (avc2_cb)
|
||||
{
|
||||
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 {
|
||||
avc2_cb(cb_ppu, CELL_AVC2_EVENT_UNLOAD_SUCCEEDED, 0, avc2_cb_arg);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,21 +136,6 @@ struct vdec_context final
|
||||
, cb_func(func)
|
||||
, cb_arg(arg)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
// TODO: This function should be removed at some point, since ffmpeg does it automatically now.
|
||||
// We'll keep it for compatibility for now until more system ffmpeg libs are up to date.
|
||||
avcodec_register_all();
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CELL_VDEC_CODEC_TYPE_MPEG2:
|
||||
@@ -1265,5 +1250,5 @@ DECLARE(ppu_module_manager::cellVdec)("libvdec", []()
|
||||
REG_FUNC(libvdec, cellVdecSetFrameRateExt); // 0xcffc42a5
|
||||
REG_FUNC(libvdec, cellVdecSetPts); // 0x3ce2e4f8
|
||||
|
||||
REG_FUNC(libvdec, vdecEntry).flag(MFF_HIDDEN);
|
||||
REG_HIDDEN_FUNC(vdecEntry);
|
||||
});
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Emu/VFS.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Io/interception.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
#include "sysPrxForUser.h"
|
||||
#include "Emu/IdManager.h"
|
||||
@@ -396,6 +398,11 @@ void fmt_class_string<SceNpError>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
void message_data::print() const
|
||||
{
|
||||
sceNp.notice("commId: %s, msgId: %d, mainType: %d, subType: %d, subject: %s, body: %s, data_size: %d", static_cast<const char *>(commId.data), msgId, mainType, subType, subject, body, data.size());
|
||||
}
|
||||
|
||||
error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr)
|
||||
{
|
||||
sceNp.warning("sceNpInit(poolsize=0x%x, poolptr=*0x%x)", poolsize, poolptr);
|
||||
@@ -409,18 +416,14 @@ error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr)
|
||||
return SCE_NP_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
if (poolsize == 0)
|
||||
if (poolsize == 0 || !poolptr)
|
||||
{
|
||||
return SCE_NP_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
else if (poolsize < SCE_NP_MIN_POOLSIZE)
|
||||
{
|
||||
return SCE_NP_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
if (!poolptr)
|
||||
if (poolsize < SCE_NP_MIN_POOLSIZE)
|
||||
{
|
||||
return SCE_NP_ERROR_INVALID_ARGUMENT;
|
||||
return SCE_NP_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
nph.init_NP(poolsize, poolptr);
|
||||
@@ -450,6 +453,11 @@ error_code sceNpTerm()
|
||||
|
||||
error_code npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_path)
|
||||
{
|
||||
if (!drm_path)
|
||||
{
|
||||
return SCE_NP_DRM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
u128 k_licensee{};
|
||||
|
||||
if (k_licensee_addr)
|
||||
@@ -458,7 +466,7 @@ error_code npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_pat
|
||||
sceNp.notice("npDrmIsAvailable(): KLicense key %s", std::bit_cast<be_t<u128>>(k_licensee));
|
||||
}
|
||||
|
||||
if (Emu.GetCat() == "PE")
|
||||
if (Emu.GetFakeCat() == "PE")
|
||||
{
|
||||
std::memcpy(&k_licensee, NP_PSP_KEY_2, sizeof(k_licensee));
|
||||
sceNp.success("npDrmIsAvailable(): PSP remaster KLicense key applied.");
|
||||
@@ -543,52 +551,34 @@ error_code sceNpDrmIsAvailable2(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm
|
||||
return npDrmIsAvailable(k_licensee_addr, drm_path);
|
||||
}
|
||||
|
||||
error_code sceNpDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
|
||||
error_code npDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
|
||||
{
|
||||
sceNp.warning("sceNpDrmVerifyUpgradeLicense(content_id=*0x%x)", content_id);
|
||||
|
||||
if (!content_id)
|
||||
{
|
||||
return SCE_NP_DRM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
const std::string content_str(content_id.get_ptr(), std::find(content_id.get_ptr(), content_id.get_ptr() + 0x2f, '\0'));
|
||||
const std::string rap_file = rpcs3::utils::get_rap_file_path(content_str);
|
||||
sceNp.warning("npDrmVerifyUpgradeLicense(): content_id='%s'", content_id);
|
||||
|
||||
sceNp.warning(u8"sceNpDrmVerifyUpgradeLicense(): content_id=“%s”", content_id);
|
||||
|
||||
if (fs::stat_t s{}; !fs::stat(rap_file, s) || s.is_directory || s.size < 0x10)
|
||||
{
|
||||
// Game hasn't been purchased therefore no RAP file present
|
||||
if (!rpcs3::utils::verify_c00_unlock_edat(content_str))
|
||||
return SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Game has been purchased and there's a RAP file present
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
|
||||
{
|
||||
sceNp.warning("sceNpDrmVerifyUpgradeLicense(content_id=*0x%x)", content_id);
|
||||
|
||||
return npDrmVerifyUpgradeLicense(content_id);
|
||||
}
|
||||
|
||||
error_code sceNpDrmVerifyUpgradeLicense2(vm::cptr<char> content_id)
|
||||
{
|
||||
sceNp.warning("sceNpDrmVerifyUpgradeLicense2(content_id=*0x%x)", content_id);
|
||||
|
||||
if (!content_id)
|
||||
{
|
||||
return SCE_NP_DRM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
const std::string content_str(content_id.get_ptr(), std::find(content_id.get_ptr(), content_id.get_ptr() + 0x2f, '\0'));
|
||||
const std::string rap_file = rpcs3::utils::get_rap_file_path(content_str);
|
||||
|
||||
sceNp.warning(u8"sceNpDrmVerifyUpgradeLicense2(): content_id=“%s”", content_id);
|
||||
|
||||
if (fs::stat_t s{}; !fs::stat(rap_file, s) || s.is_directory || s.size < 0x10)
|
||||
{
|
||||
// Game hasn't been purchased therefore no RAP file present
|
||||
return SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Game has been purchased and there's a RAP file present
|
||||
return CELL_OK;
|
||||
return npDrmVerifyUpgradeLicense(content_id);
|
||||
}
|
||||
|
||||
error_code sceNpDrmExecuteGamePurchase()
|
||||
@@ -647,7 +637,7 @@ error_code sceNpDrmProcessExitSpawn2(ppu_thread& ppu, vm::cptr<u8> klicensee, vm
|
||||
|
||||
error_code sceNpBasicRegisterHandler(vm::cptr<SceNpCommunicationId> context, vm::ptr<SceNpBasicEventHandler> handler, vm::ptr<void> arg)
|
||||
{
|
||||
sceNp.warning("sceNpBasicRegisterHandler(context=*0x%x, handler=*0x%x, arg=*0x%x)", context, handler, arg);
|
||||
sceNp.warning("sceNpBasicRegisterHandler(context=*0x%x(%s), handler=*0x%x, arg=*0x%x)", context, context ? static_cast<const char *>(context->data) : "", handler, arg);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
@@ -656,13 +646,21 @@ error_code sceNpBasicRegisterHandler(vm::cptr<SceNpCommunicationId> context, vm:
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
|
||||
if (!context || !handler)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
nph.basic_handler = handler;
|
||||
nph.basic_handler_arg = arg;
|
||||
memcpy(&nph.basic_handler.context, context.get_ptr(), sizeof(nph.basic_handler.context));
|
||||
nph.basic_handler.handler_func = handler;
|
||||
nph.basic_handler.handler_arg = arg;
|
||||
nph.basic_handler.registered = true;
|
||||
nph.basic_handler.context_sensitive = false;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -678,11 +676,22 @@ error_code sceNpBasicRegisterContextSensitiveHandler(vm::cptr<SceNpCommunication
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
|
||||
if (!context || !handler)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
memcpy(&nph.basic_handler.context, context.get_ptr(), sizeof(nph.basic_handler.context));
|
||||
nph.basic_handler.handler_func = handler;
|
||||
nph.basic_handler.handler_arg = arg;
|
||||
nph.basic_handler.registered = true;
|
||||
nph.basic_handler.context_sensitive = true;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -697,10 +706,17 @@ error_code sceNpBasicUnregisterHandler()
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
nph.basic_handler.registered = false;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpBasicSetPresence(vm::cptr<void> data, u64 size)
|
||||
error_code sceNpBasicSetPresence(vm::cptr<u8> data, u64 size)
|
||||
{
|
||||
sceNp.todo("sceNpBasicSetPresence(data=*0x%x, size=%d)", data, size);
|
||||
|
||||
@@ -711,6 +727,17 @@ error_code sceNpBasicSetPresence(vm::cptr<void> data, u64 size)
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
// TODO: Correct but causes issues atm(breaks bomberman ultra)
|
||||
// if (!data || !data[0])
|
||||
// {
|
||||
// return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
// }
|
||||
|
||||
if (size > SCE_NP_BASIC_MAX_PRESENCE_SIZE)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
|
||||
@@ -730,6 +757,11 @@ error_code sceNpBasicSetPresenceDetails(vm::cptr<SceNpBasicPresenceDetails> pres
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!pres || options > SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -754,6 +786,11 @@ error_code sceNpBasicSetPresenceDetails2(vm::cptr<SceNpBasicPresenceDetails2> pr
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!pres || options > SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -778,6 +815,11 @@ error_code sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u64
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!to || to->handle.data[0] == '\0' || !data || !size)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -793,7 +835,18 @@ error_code sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u64
|
||||
|
||||
error_code sceNpBasicSendMessageGui(vm::cptr<SceNpBasicMessageDetails> msg, sys_memory_container_t containerId)
|
||||
{
|
||||
sceNp.todo("sceNpBasicSendMessageGui(msg=*0x%x, containerId=%d)", msg, containerId);
|
||||
sceNp.warning("sceNpBasicSendMessageGui(msg=*0x%x, containerId=%d)", msg, containerId);
|
||||
|
||||
if (msg)
|
||||
{
|
||||
sceNp.notice("sceNpBasicSendMessageGui: msgId: %d, mainType: %d, subType: %d, msgFeatures: %d, count: %d", msg->msgId, msg->mainType, msg->subType, msg->msgFeatures, msg->count);
|
||||
for (u32 i = 0; i < msg->count; i++)
|
||||
{
|
||||
sceNp.trace("sceNpBasicSendMessageGui: NpId[%d] = %s", i, static_cast<char*>(&msg->npids[i].handle.data[0]));
|
||||
}
|
||||
sceNp.notice("sceNpBasicSendMessageGui: subject: %s", msg->subject);
|
||||
sceNp.notice("sceNpBasicSendMessageGui: body: %s", msg->body);
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
@@ -802,11 +855,19 @@ error_code sceNpBasicSendMessageGui(vm::cptr<SceNpBasicMessageDetails> msg, sys_
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!msg || msg->count > SCE_NP_BASIC_SEND_MESSAGE_MAX_RECIPIENTS || msg->npids.handle.data[0] == '\0' || !(msg->msgFeatures & SCE_NP_BASIC_MESSAGE_FEATURES_ALL_FEATURES))
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!msg || msg->count > SCE_NP_BASIC_SEND_MESSAGE_MAX_RECIPIENTS || (msg->msgFeatures & ~SCE_NP_BASIC_MESSAGE_FEATURES_ALL_FEATURES) ||
|
||||
msg->mainType > SCE_NP_BASIC_MESSAGE_MAIN_TYPE_URL_ATTACHMENT || msg->msgId != 0ull)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// TODO: SCE_NP_BASIC_ERROR_NOT_SUPPORTED, might be in between argument checks
|
||||
|
||||
if (msg->size > SCE_NP_BASIC_MAX_MESSAGE_SIZE)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
|
||||
@@ -817,6 +878,82 @@ error_code sceNpBasicSendMessageGui(vm::cptr<SceNpBasicMessageDetails> msg, sys_
|
||||
return not_an_error(SCE_NP_BASIC_ERROR_NOT_CONNECTED);
|
||||
}
|
||||
|
||||
// Prepare message data
|
||||
message_data msg_data = {
|
||||
.commId = nph.basic_handler.context,
|
||||
.msgId = msg->msgId,
|
||||
.mainType = msg->mainType,
|
||||
.subType = msg->subType,
|
||||
.msgFeatures = msg->msgFeatures};
|
||||
std::set<std::string> npids;
|
||||
|
||||
for (u32 i = 0; i < msg->count; i++)
|
||||
{
|
||||
npids.insert(std::string(msg->npids[i].handle.data));
|
||||
}
|
||||
|
||||
if (msg->subject)
|
||||
{
|
||||
msg_data.subject = std::string(msg->subject.get_ptr());
|
||||
}
|
||||
|
||||
if (msg->body)
|
||||
{
|
||||
msg_data.body = std::string(msg->body.get_ptr());
|
||||
}
|
||||
|
||||
if (msg->size)
|
||||
{
|
||||
msg_data.data.assign(msg->data.get_ptr(), msg->data.get_ptr() + msg->size);
|
||||
}
|
||||
|
||||
atomic_t<bool> wake_up = false;
|
||||
bool result = false;
|
||||
|
||||
input::SetIntercepted(true);
|
||||
|
||||
Emu.CallAfter([=, &wake_up, &result, msg_data = std::move(msg_data), npids = std::move(npids)]() mutable
|
||||
{
|
||||
auto send_dlg = Emu.GetCallbacks().get_sendmessage_dialog();
|
||||
result = send_dlg->Exec(msg_data, npids);
|
||||
wake_up = true;
|
||||
wake_up.notify_one();
|
||||
});
|
||||
|
||||
while (!wake_up && !Emu.IsStopped())
|
||||
{
|
||||
thread_ctrl::wait_on(wake_up, false);
|
||||
}
|
||||
|
||||
input::SetIntercepted(false);
|
||||
|
||||
s32 callback_result = result ? 0 : -1;
|
||||
s32 event = 0;
|
||||
|
||||
switch (msg->mainType)
|
||||
{
|
||||
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_DATA_ATTACHMENT:
|
||||
event = SCE_NP_BASIC_EVENT_SEND_ATTACHMENT_RESULT;
|
||||
break;
|
||||
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_GENERAL:
|
||||
event = SCE_NP_BASIC_EVENT_SEND_MESSAGE_RESULT;
|
||||
break;
|
||||
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_ADD_FRIEND:
|
||||
event = SCE_NP_BASIC_EVENT_ADD_FRIEND_RESULT;
|
||||
break;
|
||||
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE:
|
||||
event = SCE_NP_BASIC_EVENT_SEND_INVITATION_RESULT;
|
||||
break;
|
||||
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_CUSTOM_DATA:
|
||||
event = SCE_NP_BASIC_EVENT_SEND_CUSTOM_DATA_RESULT;
|
||||
break;
|
||||
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_URL_ATTACHMENT:
|
||||
event = SCE_NP_BASIC_EVENT_SEND_URL_ATTACHMENT_RESULT;
|
||||
break;
|
||||
}
|
||||
|
||||
nph.send_basic_event(event, callback_result, 0);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -831,12 +968,19 @@ error_code sceNpBasicSendMessageAttachment(vm::cptr<SceNpId> to, vm::cptr<char>
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!to || to->handle.data[0] == '\0' || !data || !size)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (strlen(subject.get_ptr()) > SCE_NP_BASIC_BODY_CHARACTER_MAX || strlen(body.get_ptr()) > SCE_NP_BASIC_BODY_CHARACTER_MAX)
|
||||
// TODO: SCE_NP_BASIC_ERROR_NOT_SUPPORTED, might be in between argument checks
|
||||
|
||||
if (strlen(subject.get_ptr()) > SCE_NP_BASIC_SUBJECT_CHARACTER_MAX || strlen(body.get_ptr()) > SCE_NP_BASIC_BODY_CHARACTER_MAX)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
@@ -860,12 +1004,17 @@ error_code sceNpBasicRecvMessageAttachment(sys_memory_container_t containerId)
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpBasicRecvMessageAttachmentLoad(u32 id, vm::ptr<void> buffer, vm::ptr<u64> size)
|
||||
error_code sceNpBasicRecvMessageAttachmentLoad(u32 id, vm::ptr<void> buffer, vm::ptr<u32> size)
|
||||
{
|
||||
sceNp.todo("sceNpBasicRecvMessageAttachmentLoad(id=%d, buffer=*0x%x, size=*0x%x)", id, buffer, size);
|
||||
sceNp.warning("sceNpBasicRecvMessageAttachmentLoad(id=%d, buffer=*0x%x, size=*0x%x)", id, buffer, size);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
@@ -874,22 +1023,41 @@ error_code sceNpBasicRecvMessageAttachmentLoad(u32 id, vm::ptr<void> buffer, vm:
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!buffer || !size)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (id > SCE_NP_BASIC_SELECTED_MESSAGE_DATA)
|
||||
const auto opt_msg = nph.get_message(id);
|
||||
if (!opt_msg)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_DATA_ID;
|
||||
}
|
||||
|
||||
const auto msg_pair = opt_msg.value();
|
||||
const auto msg = msg_pair->second;
|
||||
|
||||
const u32 orig_size = *size;
|
||||
const u32 size_to_copy = std::min(static_cast<u32>(msg.data.size()), orig_size);
|
||||
memcpy(buffer.get_ptr(), msg.data.data(), size_to_copy);
|
||||
|
||||
*size = size_to_copy;
|
||||
if (size_to_copy < msg.data.size())
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_DATA_LOST;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpBasicRecvMessageCustom(u16 mainType, u32 recvOptions, sys_memory_container_t containerId)
|
||||
{
|
||||
sceNp.todo("sceNpBasicRecvMessageCustom(mainType=%d, recvOptions=%d, containerId=%d)", mainType, recvOptions, containerId);
|
||||
sceNp.warning("sceNpBasicRecvMessageCustom(mainType=%d, recvOptions=%d, containerId=%d)", mainType, recvOptions, containerId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
@@ -898,11 +1066,66 @@ error_code sceNpBasicRecvMessageCustom(u16 mainType, u32 recvOptions, sys_memory
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!(recvOptions & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_ALL_OPTIONS))
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
// TODO: SCE_NP_BASIC_ERROR_NOT_SUPPORTED
|
||||
|
||||
if ((recvOptions & ~SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_ALL_OPTIONS))
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
atomic_t<bool> wake_up = false;
|
||||
bool result = false;
|
||||
|
||||
input::SetIntercepted(true);
|
||||
|
||||
SceNpBasicMessageRecvAction recv_result;
|
||||
u64 chosen_msg_id;
|
||||
|
||||
Emu.CallAfter([=, &wake_up, &result, &recv_result, &chosen_msg_id]()
|
||||
{
|
||||
auto recv_dlg = Emu.GetCallbacks().get_recvmessage_dialog();
|
||||
result = recv_dlg->Exec(static_cast<SceNpBasicMessageMainType>(mainType), static_cast<SceNpBasicMessageRecvOptions>(recvOptions), recv_result, chosen_msg_id);
|
||||
wake_up = true;
|
||||
wake_up.notify_one();
|
||||
});
|
||||
|
||||
while (!wake_up && !Emu.IsStopped())
|
||||
{
|
||||
thread_ctrl::wait_on(wake_up, false);
|
||||
}
|
||||
|
||||
input::SetIntercepted(false);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_CANCEL;
|
||||
}
|
||||
|
||||
const auto msg_pair = nph.get_message(chosen_msg_id).value();
|
||||
const auto& msg = msg_pair->second;
|
||||
|
||||
const u32 event_to_send = (mainType == SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE) ? SCE_NP_BASIC_EVENT_RECV_INVITATION_RESULT : SCE_NP_BASIC_EVENT_RECV_CUSTOM_DATA_RESULT;
|
||||
np_handler::basic_event to_add{};
|
||||
to_add.event = event_to_send;
|
||||
strcpy_trunc(to_add.from.userId.handle.data, msg_pair->first);
|
||||
strcpy_trunc(to_add.from.name.data, msg_pair->first);
|
||||
to_add.data.resize(sizeof(SceNpBasicExtendedAttachmentData));
|
||||
SceNpBasicExtendedAttachmentData* att_data = reinterpret_cast<SceNpBasicExtendedAttachmentData*>(to_add.data.data());
|
||||
att_data->flags = 0; // ?
|
||||
att_data->msgId = chosen_msg_id;
|
||||
att_data->data.id = static_cast<u32>(chosen_msg_id);
|
||||
att_data->data.size = static_cast<u32>(msg.data.size());
|
||||
att_data->userAction = recv_result;
|
||||
att_data->markedAsUsed = (recvOptions & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_PRESERVE) ? 0 : 1;
|
||||
|
||||
nph.queue_basic_event(to_add);
|
||||
nph.send_basic_event(event_to_send, 0, 0);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -917,6 +1140,11 @@ error_code sceNpBasicMarkMessageAsUsed(SceNpBasicMessageId msgId)
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
//if (!msgId > ?)
|
||||
//{
|
||||
// return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -936,6 +1164,13 @@ error_code sceNpBasicAbortGui()
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
// TODO: abort GUI interaction
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -950,11 +1185,18 @@ error_code sceNpBasicAddFriend(vm::cptr<SceNpId> contact, vm::cptr<char> body, s
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!contact || contact->handle.data[0] == '\0')
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// TODO: SCE_NP_BASIC_ERROR_NOT_SUPPORTED, might be in between argument checks
|
||||
|
||||
if (strlen(body.get_ptr()) > SCE_NP_BASIC_BODY_CHARACTER_MAX)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
|
||||
@@ -990,8 +1232,7 @@ error_code sceNpBasicGetFriendListEntryCount(vm::ptr<u32> count)
|
||||
return SCE_NP_ERROR_ID_NOT_FOUND;
|
||||
}
|
||||
|
||||
// TODO: Check if there are any friends
|
||||
*count = 0;
|
||||
*count = nph.get_num_friends();
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1026,6 +1267,11 @@ error_code sceNpBasicGetFriendPresenceByIndex(u32 index, vm::ptr<SceNpUserInfo>
|
||||
{
|
||||
sceNp.todo("sceNpBasicGetFriendPresenceByIndex(index=%d, user=*0x%x, pres=*0x%x, options=%d)", index, user, pres, options);
|
||||
|
||||
if (!pres)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
@@ -1033,7 +1279,7 @@ error_code sceNpBasicGetFriendPresenceByIndex(u32 index, vm::ptr<SceNpUserInfo>
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!user || !pres)
|
||||
if (!user)
|
||||
{
|
||||
// TODO: check index and (options & SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS) depending on fw
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1046,6 +1292,11 @@ error_code sceNpBasicGetFriendPresenceByIndex2(u32 index, vm::ptr<SceNpUserInfo>
|
||||
{
|
||||
sceNp.todo("sceNpBasicGetFriendPresenceByIndex2(index=%d, user=*0x%x, pres=*0x%x, options=%d)", index, user, pres, options);
|
||||
|
||||
if (!pres)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
@@ -1053,7 +1304,7 @@ error_code sceNpBasicGetFriendPresenceByIndex2(u32 index, vm::ptr<SceNpUserInfo>
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!user || !pres)
|
||||
if (!user)
|
||||
{
|
||||
// TODO: check index and (options & SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS) depending on fw
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1068,12 +1319,17 @@ error_code sceNpBasicGetFriendPresenceByNpId(vm::cptr<SceNpId> npid, vm::ptr<Sce
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
if (!pres)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!npid || !pres)
|
||||
if (!npid)
|
||||
{
|
||||
// TODO: check (options & SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS) depending on fw
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1088,12 +1344,17 @@ error_code sceNpBasicGetFriendPresenceByNpId2(vm::cptr<SceNpId> npid, vm::ptr<Sc
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
if (!pres)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!npid || !pres)
|
||||
if (!npid)
|
||||
{
|
||||
// TODO: check (options & SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS) depending on fw
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1179,6 +1440,11 @@ error_code sceNpBasicGetPlayersHistoryEntryCount(u32 options, vm::ptr<u32> count
|
||||
{
|
||||
sceNp.todo("sceNpBasicGetPlayersHistoryEntryCount(options=%d, count=*0x%x)", options, count);
|
||||
|
||||
if (options > SCE_NP_BASIC_PLAYERS_HISTORY_OPTIONS_ALL)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
@@ -1186,6 +1452,11 @@ error_code sceNpBasicGetPlayersHistoryEntryCount(u32 options, vm::ptr<u32> count
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1207,6 +1478,11 @@ error_code sceNpBasicGetPlayersHistoryEntry(u32 options, u32 index, vm::ptr<SceN
|
||||
{
|
||||
sceNp.warning("sceNpBasicGetPlayersHistoryEntry(options=%d, index=%d, npid=*0x%x)", options, index, npid);
|
||||
|
||||
if (options > SCE_NP_BASIC_PLAYERS_HISTORY_OPTIONS_ALL)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
@@ -1214,6 +1490,11 @@ error_code sceNpBasicGetPlayersHistoryEntry(u32 options, u32 index, vm::ptr<SceN
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!npid)
|
||||
{
|
||||
// TODO: Check index
|
||||
@@ -1240,6 +1521,11 @@ error_code sceNpBasicAddBlockListEntry(vm::cptr<SceNpId> npid)
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!npid || npid->handle.data[0] == '\0')
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1276,7 +1562,7 @@ error_code sceNpBasicGetBlockListEntryCount(vm::ptr<u32> count)
|
||||
}
|
||||
|
||||
// TODO: Check if there are block lists
|
||||
*count = 0;
|
||||
*count = nph.get_num_blocks();
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1318,6 +1604,11 @@ error_code sceNpBasicGetMessageAttachmentEntryCount(vm::ptr<u32> count)
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1346,6 +1637,11 @@ error_code sceNpBasicGetMessageAttachmentEntry(u32 index, vm::ptr<SceNpUserInfo>
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!from)
|
||||
{
|
||||
// TODO: check index
|
||||
@@ -1365,17 +1661,27 @@ error_code sceNpBasicGetCustomInvitationEntryCount(vm::ptr<u32> count)
|
||||
{
|
||||
sceNp.todo("sceNpBasicGetCustomInvitationEntryCount(count=*0x%x)", count);
|
||||
|
||||
if (!count)
|
||||
{
|
||||
return SCE_NP_AUTH_EINVAL;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
// TODO: Find the correct test which returns SCE_NP_AUTH_ESRCH
|
||||
if (!nph.is_NP_init)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// TODO: Find the correct test which returns SCE_NP_ERROR_ID_NOT_FOUND
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_AUTH_ESRCH;
|
||||
return SCE_NP_ERROR_ID_NOT_FOUND;
|
||||
}
|
||||
|
||||
// TODO: Check if there are custom invitations
|
||||
@@ -1388,18 +1694,28 @@ error_code sceNpBasicGetCustomInvitationEntry(u32 index, vm::ptr<SceNpUserInfo>
|
||||
{
|
||||
sceNp.todo("sceNpBasicGetCustomInvitationEntry(index=%d, from=*0x%x)", index, from);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!from)
|
||||
{
|
||||
// TODO: check index
|
||||
return SCE_NP_AUTH_EINVAL;
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
// TODO: Find the correct test which returns SCE_NP_ERROR_ID_NOT_FOUND
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_AUTH_ESRCH;
|
||||
return SCE_NP_ERROR_ID_NOT_FOUND;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
@@ -1416,6 +1732,11 @@ error_code sceNpBasicGetMatchingInvitationEntryCount(vm::ptr<u32> count)
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1444,6 +1765,11 @@ error_code sceNpBasicGetMatchingInvitationEntry(u32 index, vm::ptr<SceNpUserInfo
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!from)
|
||||
{
|
||||
// TODO: check index
|
||||
@@ -1470,6 +1796,11 @@ error_code sceNpBasicGetClanMessageEntryCount(vm::ptr<u32> count)
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1498,6 +1829,11 @@ error_code sceNpBasicGetClanMessageEntry(u32 index, vm::ptr<SceNpUserInfo> from)
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!from)
|
||||
{
|
||||
// TODO: check index
|
||||
@@ -1517,6 +1853,12 @@ error_code sceNpBasicGetMessageEntryCount(u32 type, vm::ptr<u32> count)
|
||||
{
|
||||
sceNp.todo("sceNpBasicGetMessageEntryCount(type=%d, count=*0x%x)", type, count);
|
||||
|
||||
// TODO: verify this check and its location
|
||||
if (type > SCE_NP_BASIC_MESSAGE_INFO_TYPE_BOOTABLE_CUSTOM_DATA_MESSAGE)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
@@ -1545,6 +1887,12 @@ error_code sceNpBasicGetMessageEntry(u32 type, u32 index, vm::ptr<SceNpUserInfo>
|
||||
{
|
||||
sceNp.todo("sceNpBasicGetMessageEntry(type=%d, index=%d, from=*0x%x)", type, index, from);
|
||||
|
||||
// TODO: verify this check and its location
|
||||
if (type > SCE_NP_BASIC_MESSAGE_INFO_TYPE_BOOTABLE_CUSTOM_DATA_MESSAGE)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np_handler>>();
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
@@ -1578,15 +1926,19 @@ error_code sceNpBasicGetEvent(vm::ptr<s32> event, vm::ptr<SceNpUserInfo> from, v
|
||||
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!nph.basic_handler.registered)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if (!event || !from || !data || !size)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// TODO: Check for other error and pass other events
|
||||
//*event = SCE_NP_BASIC_EVENT_OFFLINE; // This event only indicates a contact is offline, not the current status of the connection
|
||||
|
||||
return not_an_error(SCE_NP_BASIC_ERROR_NO_EVENT);
|
||||
return nph.get_basic_event(event, from, data, size);
|
||||
}
|
||||
|
||||
error_code sceNpCommerceCreateCtx(u32 version, vm::ptr<SceNpId> npId, vm::ptr<SceNpCommerceHandler> handler, vm::ptr<void> arg, vm::ptr<u32> ctx_id)
|
||||
@@ -1814,11 +2166,29 @@ error_code sceNpCustomMenuRegisterActions(vm::cptr<SceNpCustomMenu> menu, vm::pt
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!menu || !handler)
|
||||
if (!menu || !handler) // TODO: handler check might come later
|
||||
{
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (menu->numActions > SCE_NP_CUSTOM_MENU_ACTION_ITEMS_TOTAL_MAX)
|
||||
{
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < menu->numActions; i++)
|
||||
{
|
||||
if (!menu->actions[i].name)
|
||||
{
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!memchr(menu->actions[i].name.get_ptr(), '\0', SCE_NP_CUSTOM_MENU_ACTION_CHARACTER_MAX))
|
||||
{
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1833,6 +2203,8 @@ error_code sceNpCustomMenuActionSetActivation(vm::cptr<SceNpCustomMenuIndexArray
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// TODO: SCE_NP_CUSTOM_MENU_ERROR_NOT_REGISTERED
|
||||
|
||||
if (!array)
|
||||
{
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1852,17 +2224,18 @@ error_code sceNpCustomMenuRegisterExceptionList(vm::cptr<SceNpCustomMenuActionEx
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// TODO: SCE_NP_CUSTOM_MENU_ERROR_NOT_REGISTERED
|
||||
|
||||
if (numItems > SCE_NP_CUSTOM_MENU_EXCEPTION_ITEMS_MAX)
|
||||
{
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
|
||||
if (!items)
|
||||
{
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (numItems > SCE_NP_CUSTOM_MENU_ACTION_ITEMS_MAX)
|
||||
{
|
||||
// TODO: what about SCE_NP_CUSTOM_MENU_ACTION_ITEMS_TOTAL_MAX
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1874,9 +2247,11 @@ error_code sceNpFriendlist(vm::ptr<SceNpFriendlistResultHandler> resultHandler,
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
{
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
|
||||
return SCE_NP_FRIENDLIST_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// TODO: SCE_NP_FRIENDLIST_ERROR_BUSY
|
||||
|
||||
if (!resultHandler)
|
||||
{
|
||||
return SCE_NP_FRIENDLIST_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1893,9 +2268,11 @@ error_code sceNpFriendlistCustom(SceNpFriendlistCustomOptions options, vm::ptr<S
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
{
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
|
||||
return SCE_NP_FRIENDLIST_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// TODO: SCE_NP_FRIENDLIST_ERROR_BUSY
|
||||
|
||||
if (!resultHandler)
|
||||
{
|
||||
return SCE_NP_FRIENDLIST_ERROR_INVALID_ARGUMENT;
|
||||
@@ -1912,9 +2289,11 @@ error_code sceNpFriendlistAbortGui()
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
{
|
||||
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
|
||||
return SCE_NP_FRIENDLIST_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// TODO: abort friendlist GUI interaction
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -2931,10 +3310,10 @@ error_code sceNpManagerGetTicket(vm::ptr<void> buffer, vm::ptr<u32> bufferSize)
|
||||
}
|
||||
|
||||
const auto& ticket = nph.get_ticket();
|
||||
*bufferSize = static_cast<u32>(ticket.size());
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
*bufferSize = static_cast<u32>(ticket.size());
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "cellRtc.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr);
|
||||
error_code sceNpTerm();
|
||||
|
||||
@@ -1022,11 +1024,11 @@ struct SceNpBasicMessageDetails
|
||||
be_t<u16> mainType;
|
||||
be_t<u16> subType;
|
||||
be_t<u32> msgFeatures;
|
||||
const SceNpId npids;
|
||||
vm::bptr<SceNpId> npids;
|
||||
be_t<u32> count;
|
||||
const s8 subject;
|
||||
const s8 body;
|
||||
const be_t<u32> data;
|
||||
vm::bptr<char> subject;
|
||||
vm::bptr<char> body;
|
||||
vm::bptr<u8> data;
|
||||
be_t<u32> size;
|
||||
};
|
||||
|
||||
@@ -1203,7 +1205,7 @@ struct SceNpSignalingNetInfo
|
||||
struct SceNpCustomMenuAction
|
||||
{
|
||||
be_t<u32> options;
|
||||
char name[SCE_NP_CUSTOM_MENU_ACTION_CHARACTER_MAX];
|
||||
vm::bcptr<char> name;
|
||||
be_t<SceNpCustomMenuActionMask> mask;
|
||||
};
|
||||
|
||||
@@ -1359,3 +1361,33 @@ using SceNpMatchingGUIHandler = void(u32 ctx_id, s32 event, s32 error_code, vm::
|
||||
using SceNpProfileResultHandler = s32(s32 result, vm::ptr<void> arg);
|
||||
|
||||
using SceNpManagerSubSigninCallback = void(s32 result, vm::ptr<SceNpId> npId, vm::ptr<void> cb_arg);
|
||||
|
||||
// Used to pass data to UI/RPCN
|
||||
struct message_data
|
||||
{
|
||||
SceNpCommunicationId commId{};
|
||||
u64 msgId = 0;
|
||||
u16 mainType = 0;
|
||||
u16 subType = 0;
|
||||
u32 msgFeatures = 0;
|
||||
std::string subject;
|
||||
std::string body;
|
||||
std::vector<u8> data;
|
||||
void print() const;
|
||||
};
|
||||
|
||||
class SendMessageDialogBase
|
||||
{
|
||||
public:
|
||||
virtual ~SendMessageDialogBase() = default;
|
||||
|
||||
virtual bool Exec(message_data& msg_data, std::set<std::string>& npids) = 0;
|
||||
};
|
||||
|
||||
class RecvMessageDialogBase
|
||||
{
|
||||
public:
|
||||
virtual ~RecvMessageDialogBase() = default;
|
||||
|
||||
virtual bool Exec(SceNpBasicMessageMainType type, SceNpBasicMessageRecvOptions options, SceNpBasicMessageRecvAction& recv_result, u64& chosen_msg_id) = 0;
|
||||
};
|
||||
|
||||
@@ -613,17 +613,23 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 1:
|
||||
case SCE_NP_SIGNALING_CONN_INFO_RTT:
|
||||
{
|
||||
connInfo->rtt = 20000; // HACK
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH:
|
||||
{
|
||||
connInfo->bandwidth = 10'000'000; // 10 MBPS HACK
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
case SCE_NP_SIGNALING_CONN_INFO_PEER_NPID:
|
||||
{
|
||||
// TODO: need an update to whole signaling as matching2 signaling ignores npids atm
|
||||
sceNp2.fatal("sceNpMatching2SignalingGetConnectionInfo Unimplemented SCE_NP_SIGNALING_CONN_INFO_PEER_NPID");
|
||||
break;
|
||||
}
|
||||
case SCE_NP_SIGNALING_CONN_INFO_PEER_ADDRESS:
|
||||
{
|
||||
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
const auto si = sigh.get_sig2_infos(roomId, memberId);
|
||||
@@ -631,7 +637,15 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
|
||||
connInfo->address.addr.np_s_addr = si.addr;
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
case SCE_NP_SIGNALING_CONN_INFO_MAPPED_ADDRESS:
|
||||
{
|
||||
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
const auto si = sigh.get_sig2_infos(roomId, memberId);
|
||||
connInfo->address.port = std::bit_cast<u16, be_t<u16>>(si.mapped_port);
|
||||
connInfo->address.addr.np_s_addr = si.mapped_addr;
|
||||
break;
|
||||
}
|
||||
case SCE_NP_SIGNALING_CONN_INFO_PACKET_LOSS:
|
||||
{
|
||||
connInfo->packet_loss = 1; // HACK
|
||||
break;
|
||||
@@ -1274,6 +1288,8 @@ error_code sceNpMatching2GetRoomDataExternalList(
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.get_roomdata_external_list(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,16 +95,16 @@ error_code console_getc()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
void console_putc(char ch)
|
||||
void console_putc(ppu_thread& ppu, char ch)
|
||||
{
|
||||
sysPrxForUser.trace("console_putc(ch=0x%x)", ch);
|
||||
sys_tty_write(0, vm::var<char>(ch), 1, vm::var<u32>{});
|
||||
sys_tty_write(ppu, 0, vm::var<char>(ch), 1, vm::var<u32>{});
|
||||
}
|
||||
|
||||
error_code console_write(vm::ptr<char> data, u32 len)
|
||||
error_code console_write(ppu_thread& ppu, vm::ptr<char> data, u32 len)
|
||||
{
|
||||
sysPrxForUser.trace("console_write(data=*0x%x, len=%d)", data, len);
|
||||
sys_tty_write(0, data, len, vm::var<u32>{});
|
||||
sys_tty_write(ppu, 0, data, len, vm::var<u32>{});
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -416,7 +416,7 @@ error_code _sys_printf(ppu_thread& ppu, vm::cptr<char> fmt, ppu_va_args_t va_arg
|
||||
|
||||
const auto buf = vm::make_str(ps3_fmt(ppu, fmt, va_args.count));
|
||||
|
||||
sys_tty_write(0, buf, buf.get_count() - 1, vm::var<u32>{});
|
||||
sys_tty_write(ppu, 0, buf, buf.get_count() - 1, vm::var<u32>{});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
+124
-92
@@ -5,232 +5,264 @@
|
||||
class PPCDisAsm : public CPUDisAsm
|
||||
{
|
||||
protected:
|
||||
PPCDisAsm(cpu_disasm_mode mode, const u8* offset) : CPUDisAsm(mode, offset)
|
||||
PPCDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0) : CPUDisAsm(mode, offset, start_pc)
|
||||
{
|
||||
}
|
||||
|
||||
virtual u32 DisAsmBranchTarget(const s32 imm) override = 0;
|
||||
|
||||
void DisAsm_V4(const std::string& op, u32 v0, u32 v1, u32 v2, u32 v3)
|
||||
usz insert_char_if(usz pos, bool insert, char c)
|
||||
{
|
||||
Write(fmt::format("%s v%d,v%d,v%d,v%d", FixOp(op), v0, v1, v2, v3));
|
||||
if (!insert)
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
ensure(std::exchange(last_opcode[pos], c) == ' ' && last_opcode[pos + 1] == ' ');
|
||||
return pos + 1;
|
||||
}
|
||||
void DisAsm_V3_UIMM(const std::string& op, u32 v0, u32 v1, u32 v2, u32 uimm)
|
||||
|
||||
usz insert_char_if(std::string_view op, bool insert, char c = '.')
|
||||
{
|
||||
Write(fmt::format("%s v%d,v%d,v%d,%s", FixOp(op), v0, v1, v2, uimm));
|
||||
return insert_char_if(op.size(), insert, c);
|
||||
}
|
||||
void DisAsm_V3(const std::string& op, u32 v0, u32 v1, u32 v2)
|
||||
|
||||
void DisAsm_V4(std::string_view op, u32 v0, u32 v1, u32 v2, u32 v3)
|
||||
{
|
||||
Write(fmt::format("%s v%d,v%d,v%d", FixOp(op), v0, v1, v2));
|
||||
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,v%d", PadOp(), op, v0, v1, v2, v3);
|
||||
}
|
||||
void DisAsm_V2_UIMM(const std::string& op, u32 v0, u32 v1, u32 uimm)
|
||||
void DisAsm_V3_UIMM(std::string_view op, u32 v0, u32 v1, u32 v2, u32 uimm)
|
||||
{
|
||||
Write(fmt::format("%s v%d,v%d,%s", FixOp(op), v0, v1, uimm));
|
||||
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,%s", PadOp(), op, v0, v1, v2, uimm);
|
||||
}
|
||||
void DisAsm_V2(const std::string& op, u32 v0, u32 v1)
|
||||
void DisAsm_V3(std::string_view op, u32 v0, u32 v1, u32 v2)
|
||||
{
|
||||
Write(fmt::format("%s v%d,v%d", FixOp(op), v0, v1));
|
||||
fmt::append(last_opcode, "%-*s v%d,v%d,v%d", PadOp(), op, v0, v1, v2);
|
||||
}
|
||||
void DisAsm_V1_SIMM(const std::string& op, u32 v0, s32 simm)
|
||||
void DisAsm_V2_UIMM(std::string_view op, u32 v0, u32 v1, u32 uimm)
|
||||
{
|
||||
Write(fmt::format("%s v%d,%s", FixOp(op), v0, SignedHex(simm)));
|
||||
fmt::append(last_opcode, "%-*s v%d,v%d,%s", PadOp(), op, v0, v1, uimm);
|
||||
}
|
||||
void DisAsm_V1(const std::string& op, u32 v0)
|
||||
void DisAsm_V2(std::string_view op, u32 v0, u32 v1)
|
||||
{
|
||||
Write(fmt::format("%s v%d", FixOp(op), v0));
|
||||
fmt::append(last_opcode, "%-*s v%d,v%d", PadOp(), op, v0, v1);
|
||||
}
|
||||
void DisAsm_V1_R2(const std::string& op, u32 v0, u32 r1, u32 r2)
|
||||
void DisAsm_V1_SIMM(std::string_view op, u32 v0, s32 simm)
|
||||
{
|
||||
Write(fmt::format("%s v%d,r%d,r%d", FixOp(op), v0, r1, r2));
|
||||
fmt::append(last_opcode, "%-*s v%d,%s", PadOp(), op, v0, SignedHex(simm));
|
||||
}
|
||||
void DisAsm_CR1_F2_RC(const std::string& op, u32 cr0, u32 f0, u32 f1, u32 rc)
|
||||
void DisAsm_V1(std::string_view op, u32 v0)
|
||||
{
|
||||
Write(fmt::format("%s%s cr%d,f%d,f%d", FixOp(op), (rc ? "." : ""), cr0, f0, f1));
|
||||
fmt::append(last_opcode, "%-*s v%d", PadOp(), op, v0);
|
||||
}
|
||||
void DisAsm_CR1_F2(const std::string& op, u32 cr0, u32 f0, u32 f1)
|
||||
void DisAsm_V1_R2(std::string_view op, u32 v0, u32 r1, u32 r2)
|
||||
{
|
||||
fmt::append(last_opcode, "%-*s v%d,r%d,r%d", PadOp(), op, v0, r1, r2);
|
||||
}
|
||||
void DisAsm_CR1_F2_RC(std::string_view op, u32 cr0, u32 f0, u32 f1, u32 rc)
|
||||
{
|
||||
fmt::append(last_opcode, "%-*s cr%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, cr0, f0, f1);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_CR1_F2(std::string_view op, u32 cr0, u32 f0, u32 f1)
|
||||
{
|
||||
DisAsm_CR1_F2_RC(op, cr0, f0, f1, false);
|
||||
}
|
||||
void DisAsm_INT1_R2(const std::string& op, u32 i0, u32 r0, u32 r1)
|
||||
void DisAsm_INT1_R2(std::string_view op, u32 i0, u32 r0, u32 r1)
|
||||
{
|
||||
Write(fmt::format("%s %d,r%d,r%d", FixOp(op), i0, r0, r1));
|
||||
fmt::append(last_opcode, "%-*s %d,r%d,r%d", PadOp(), op, i0, r0, r1);
|
||||
}
|
||||
void DisAsm_INT1_R1_IMM(const std::string& op, u32 i0, u32 r0, s32 imm0)
|
||||
void DisAsm_INT1_R1_IMM(std::string_view op, u32 i0, u32 r0, s32 imm0)
|
||||
{
|
||||
Write(fmt::format("%s %d,r%d,%s", FixOp(op), i0, r0, SignedHex(imm0)));
|
||||
fmt::append(last_opcode, "%-*s %d,r%d,%s", PadOp(), op, i0, r0, SignedHex(imm0));
|
||||
}
|
||||
void DisAsm_INT1_R1_RC(const std::string& op, u32 i0, u32 r0, u32 rc)
|
||||
void DisAsm_INT1_R1_RC(std::string_view op, u32 i0, u32 r0, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s %d,r%d", FixOp(op), (rc ? "." : ""), i0, r0));
|
||||
fmt::append(last_opcode, "%-*s %d,r%d", PadOp(op, rc ? 1 : 0), op, i0, r0);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_INT1_R1(const std::string& op, u32 i0, u32 r0)
|
||||
void DisAsm_INT1_R1(std::string_view op, u32 i0, u32 r0)
|
||||
{
|
||||
DisAsm_INT1_R1_RC(op, i0, r0, false);
|
||||
}
|
||||
void DisAsm_F4_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
|
||||
void DisAsm_F4_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s f%d,f%d,f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1, f2, f3));
|
||||
fmt::append(last_opcode, "%-*s f%d,f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2, f3);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_F3_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 rc)
|
||||
void DisAsm_F3_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s f%d,f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1, f2));
|
||||
fmt::append(last_opcode, "%-*s f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_F3(const std::string& op, u32 f0, u32 f1, u32 f2)
|
||||
void DisAsm_F3(std::string_view op, u32 f0, u32 f1, u32 f2)
|
||||
{
|
||||
DisAsm_F3_RC(op, f0, f1, f2, false);
|
||||
}
|
||||
void DisAsm_F2_RC(const std::string& op, u32 f0, u32 f1, u32 rc)
|
||||
void DisAsm_F2_RC(std::string_view op, u32 f0, u32 f1, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1));
|
||||
fmt::append(last_opcode, "%-*s f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_F2(const std::string& op, u32 f0, u32 f1)
|
||||
void DisAsm_F2(std::string_view op, u32 f0, u32 f1)
|
||||
{
|
||||
DisAsm_F2_RC(op, f0, f1, false);
|
||||
}
|
||||
void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1)
|
||||
void DisAsm_F1_R2(std::string_view op, u32 f0, u32 r0, u32 r1)
|
||||
{
|
||||
if(m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1));
|
||||
fmt::append(last_opcode, "%-*s f%d,r%d,r%d", PadOp(), op, f0, r0, r1);
|
||||
return;
|
||||
}
|
||||
|
||||
Write(fmt::format("%s f%d,r%d(r%d)", FixOp(op), f0, r0, r1));
|
||||
fmt::append(last_opcode, "%-*s f%d,r%d(r%d)", PadOp(), op, f0, r0, r1);
|
||||
}
|
||||
void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc)
|
||||
void DisAsm_F1_IMM_R1_RC(std::string_view op, u32 f0, s32 imm0, u32 r0, u32 rc)
|
||||
{
|
||||
if(m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s%s f%d,r%d,%s", FixOp(op), (rc ? "." : ""), f0, r0, SignedHex(imm0)));
|
||||
fmt::append(last_opcode, "%-*s f%d,r%d,%s", PadOp(op, rc ? 1 : 0), op, f0, r0, SignedHex(imm0));
|
||||
insert_char_if(op, !!rc);
|
||||
return;
|
||||
}
|
||||
|
||||
Write(fmt::format("%s%s f%d,%s(r%d)", FixOp(op), (rc ? "." : ""), f0, SignedHex(imm0), r0));
|
||||
fmt::append(last_opcode, "%-*s f%d,%s(r%d)", PadOp(op, rc ? 1 : 0), op, f0, SignedHex(imm0), r0);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_F1_IMM_R1(const std::string& op, u32 f0, s32 imm0, u32 r0)
|
||||
void DisAsm_F1_IMM_R1(std::string_view op, u32 f0, s32 imm0, u32 r0)
|
||||
{
|
||||
DisAsm_F1_IMM_R1_RC(op, f0, imm0, r0, false);
|
||||
}
|
||||
void DisAsm_F1_RC(const std::string& op, u32 f0, u32 rc)
|
||||
void DisAsm_F1_RC(std::string_view op, u32 f0, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s f%d", FixOp(op), (rc ? "." : ""), f0));
|
||||
fmt::append(last_opcode, "%-*s f%d", PadOp(op, rc ? 1 : 0), op, f0);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R1_RC(const std::string& op, u32 r0, u32 rc)
|
||||
void DisAsm_R1_RC(std::string_view op, u32 r0, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s r%d", FixOp(op), (rc ? "." : ""), r0));
|
||||
fmt::append(last_opcode, "%-*s r%d", PadOp(op, rc ? 1 : 0), op, r0);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R1(const std::string& op, u32 r0)
|
||||
void DisAsm_R1(std::string_view op, u32 r0)
|
||||
{
|
||||
DisAsm_R1_RC(op, r0, false);
|
||||
}
|
||||
void DisAsm_R2_OE_RC(const std::string& op, u32 r0, u32 r1, u32 oe, u32 rc)
|
||||
void DisAsm_R2_OE_RC(std::string_view op, u32 r0, u32 r1, u32 oe, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s%s r%d,r%d", FixOp(op), (oe ? "o" : ""), (rc ? "." : ""), r0, r1));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d", PadOp(op, (rc ? 1 : 0) + (oe ? 1 : 0)), op, r0, r1);
|
||||
insert_char_if(insert_char_if(op, !!oe, 'o'), !!rc, '.');
|
||||
}
|
||||
void DisAsm_R2_RC(const std::string& op, u32 r0, u32 r1, u32 rc)
|
||||
void DisAsm_R2_RC(std::string_view op, u32 r0, u32 r1, u32 rc)
|
||||
{
|
||||
DisAsm_R2_OE_RC(op, r0, r1, false, rc);
|
||||
}
|
||||
void DisAsm_R2(const std::string& op, u32 r0, u32 r1)
|
||||
void DisAsm_R2(std::string_view op, u32 r0, u32 r1)
|
||||
{
|
||||
DisAsm_R2_RC(op, r0, r1, false);
|
||||
}
|
||||
void DisAsm_R3_OE_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 oe, u32 rc)
|
||||
void DisAsm_R3_OE_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 oe, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s%s r%d,r%d,r%d", FixOp(op), (oe ? "o" : ""), (rc ? "." : ""), r0, r1, r2));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,r%d", PadOp(op, (rc ? 1 : 0) + (oe ? 1 : 0)), op, r0, r1, r2);
|
||||
insert_char_if(insert_char_if(op, !!oe, 'o'), !!rc, '.');
|
||||
}
|
||||
void DisAsm_R3_INT2_RC(const std::string& op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
|
||||
void DisAsm_R3_INT2_RC(std::string_view op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s r%d,r%d,r%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, r2, i0, i1));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, r2, i0, i1);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R3_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 rc)
|
||||
void DisAsm_R3_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 rc)
|
||||
{
|
||||
DisAsm_R3_OE_RC(op, r0, r1, r2, false, rc);
|
||||
}
|
||||
void DisAsm_R3(const std::string& op, u32 r0, u32 r1, u32 r2)
|
||||
void DisAsm_R3(std::string_view op, u32 r0, u32 r1, u32 r2)
|
||||
{
|
||||
DisAsm_R3_RC(op, r0, r1, r2, false);
|
||||
}
|
||||
void DisAsm_R2_INT3_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
|
||||
void DisAsm_R2_INT3_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s r%d,r%d,%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0, i1, i2));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1, i2);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R2_INT3(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
|
||||
void DisAsm_R2_INT3(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
|
||||
{
|
||||
DisAsm_R2_INT3_RC(op, r0, r1, i0, i1, i2, false);
|
||||
}
|
||||
void DisAsm_R2_INT2_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
|
||||
void DisAsm_R2_INT2_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s r%d,r%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0, i1));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R2_INT2(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1)
|
||||
void DisAsm_R2_INT2(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1)
|
||||
{
|
||||
DisAsm_R2_INT2_RC(op, r0, r1, i0, i1, false);
|
||||
}
|
||||
void DisAsm_R2_INT1_RC(const std::string& op, u32 r0, u32 r1, s32 i0, u32 rc)
|
||||
void DisAsm_R2_INT1_RC(std::string_view op, u32 r0, u32 r1, s32 i0, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s r%d,r%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R2_INT1(const std::string& op, u32 r0, u32 r1, s32 i0)
|
||||
void DisAsm_R2_INT1(std::string_view op, u32 r0, u32 r1, s32 i0)
|
||||
{
|
||||
DisAsm_R2_INT1_RC(op, r0, r1, i0, false);
|
||||
}
|
||||
void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0)
|
||||
void DisAsm_R2_IMM(std::string_view op, u32 r0, u32 r1, s32 imm0)
|
||||
{
|
||||
if(m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0)));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,%s", PadOp(), op, r0, r1, SignedHex(imm0));
|
||||
return;
|
||||
}
|
||||
|
||||
Write(fmt::format("%s r%d,%s(r%d)", FixOp(op), r0, SignedHex(imm0), r1));
|
||||
fmt::append(last_opcode, "%-*s r%d,%s(r%d)", PadOp(), op, r0, SignedHex(imm0), r1);
|
||||
}
|
||||
void DisAsm_R1_IMM(const std::string& op, u32 r0, s32 imm0)
|
||||
void DisAsm_R1_IMM(std::string_view op, u32 r0, s32 imm0)
|
||||
{
|
||||
Write(fmt::format("%s r%d,%s", FixOp(op), r0, SignedHex(imm0)));
|
||||
fmt::append(last_opcode, "%-*s r%d,%s", PadOp(), op, r0, SignedHex(imm0));
|
||||
}
|
||||
void DisAsm_IMM_R1(const std::string& op, s32 imm0, u32 r0)
|
||||
void DisAsm_IMM_R1(std::string_view op, s32 imm0, u32 r0)
|
||||
{
|
||||
Write(fmt::format("%s %d,r%d #%x", FixOp(op), imm0, r0, imm0));
|
||||
fmt::append(last_opcode, "%-*s %d,r%d #%x", PadOp(), op, imm0, r0, imm0);
|
||||
}
|
||||
void DisAsm_CR1_R1_IMM(const std::string& op, u32 cr0, u32 r0, s32 imm0)
|
||||
void DisAsm_CR1_R1_IMM(std::string_view op, u32 cr0, u32 r0, s32 imm0)
|
||||
{
|
||||
Write(fmt::format("%s cr%d,r%d,%s", FixOp(op), cr0, r0, SignedHex(imm0)));
|
||||
fmt::append(last_opcode, "%-*s cr%d,r%d,%s", PadOp(), op, cr0, r0, SignedHex(imm0));
|
||||
}
|
||||
void DisAsm_CR1_R2_RC(const std::string& op, u32 cr0, u32 r0, u32 r1, u32 rc)
|
||||
void DisAsm_CR1_R2_RC(std::string_view op, u32 cr0, u32 r0, u32 r1, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s cr%d,r%d,r%d", FixOp(op), (rc ? "." : ""), cr0, r0, r1));
|
||||
fmt::append(last_opcode, "%-*s cr%d,r%d,r%d", PadOp(op, rc ? 1 : 0), op, cr0, r0, r1);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_CR1_R2(const std::string& op, u32 cr0, u32 r0, u32 r1)
|
||||
void DisAsm_CR1_R2(std::string_view op, u32 cr0, u32 r0, u32 r1)
|
||||
{
|
||||
DisAsm_CR1_R2_RC(op, cr0, r0, r1, false);
|
||||
}
|
||||
void DisAsm_CR2(const std::string& op, u32 cr0, u32 cr1)
|
||||
void DisAsm_CR2(std::string_view op, u32 cr0, u32 cr1)
|
||||
{
|
||||
Write(fmt::format("%s cr%d,cr%d", FixOp(op), cr0, cr1));
|
||||
fmt::append(last_opcode, "%-*s cr%d,cr%d", PadOp(), op, cr0, cr1);
|
||||
}
|
||||
void DisAsm_INT3(const std::string& op, const int i0, const int i1, const int i2)
|
||||
void DisAsm_INT3(std::string_view op, const int i0, const int i1, const int i2)
|
||||
{
|
||||
Write(fmt::format("%s %d,%d,%d", FixOp(op), i0, i1, i2));
|
||||
fmt::append(last_opcode, "%-*s %d,%d,%d", PadOp(), op, i0, i1, i2);
|
||||
}
|
||||
void DisAsm_INT1(const std::string& op, const int i0)
|
||||
void DisAsm_INT1(std::string_view op, const int i0)
|
||||
{
|
||||
Write(fmt::format("%s %d", FixOp(op), i0));
|
||||
fmt::append(last_opcode, "%-*s %d", PadOp(), op, i0);
|
||||
}
|
||||
void DisAsm_BRANCH(const std::string& op, const int pc)
|
||||
void DisAsm_BRANCH(std::string_view op, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s 0x%x", FixOp(op), DisAsmBranchTarget(pc)));
|
||||
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, DisAsmBranchTarget(pc));
|
||||
}
|
||||
void DisAsm_BRANCH_A(const std::string& op, const int pc)
|
||||
void DisAsm_BRANCH_A(std::string_view op, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s 0x%x", FixOp(op), pc));
|
||||
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, pc);
|
||||
}
|
||||
void DisAsm_B2_BRANCH(const std::string& op, u32 b0, u32 b1, const int pc)
|
||||
void DisAsm_B2_BRANCH(std::string_view op, u32 b0, u32 b1, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s %d,%d,0x%x ", FixOp(op), b0, b1, DisAsmBranchTarget(pc)));
|
||||
fmt::append(last_opcode, "%-*s %d,%d,0x%x ", PadOp(), op, b0, b1, DisAsmBranchTarget(pc));
|
||||
}
|
||||
void DisAsm_CR_BRANCH(const std::string& op, u32 cr, const int pc)
|
||||
void DisAsm_CR_BRANCH(std::string_view op, u32 cr, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, DisAsmBranchTarget(pc)));
|
||||
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, DisAsmBranchTarget(pc));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -37,6 +37,8 @@ void fmt_class_string<bs_t<ppu_attr>>::format(std::string& out, u64 arg)
|
||||
format_bitset(out, arg, "[", ",", "]", &fmt_class_string<ppu_attr>::format);
|
||||
}
|
||||
|
||||
u32 ppu_get_far_jump(u32 pc);
|
||||
|
||||
void ppu_module::validate(u32 reloc)
|
||||
{
|
||||
// Load custom PRX configuration if available
|
||||
@@ -1199,6 +1201,12 @@ void ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
|
||||
const ppu_opcode_t op{*_ptr++};
|
||||
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
|
||||
|
||||
if (ppu_get_far_jump(iaddr))
|
||||
{
|
||||
block.second = _ptr.addr() - block.first - 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == ppu_itype::UNK)
|
||||
{
|
||||
// Invalid blocks will remain empty
|
||||
@@ -1388,6 +1396,11 @@ void ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
|
||||
const ppu_opcode_t op{*_ptr++};
|
||||
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
|
||||
|
||||
if (ppu_get_far_jump(iaddr))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == ppu_itype::B || type == ppu_itype::BC)
|
||||
{
|
||||
const u32 target = (op.aa ? 0 : iaddr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14);
|
||||
@@ -1462,7 +1475,11 @@ void ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
|
||||
const ppu_opcode_t op{*_ptr++};
|
||||
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
|
||||
|
||||
if (type == ppu_itype::UNK)
|
||||
if (ppu_get_far_jump(addr))
|
||||
{
|
||||
_ptr.set(next);
|
||||
}
|
||||
else if (type == ppu_itype::UNK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -1674,6 +1691,11 @@ void ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
|
||||
|
||||
for (; i_pos < lim; i_pos += 4)
|
||||
{
|
||||
if (ppu_get_far_jump(i_pos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 opc = vm::_ref<u32>(i_pos);
|
||||
|
||||
switch (auto type = s_ppu_itype.decode(opc))
|
||||
|
||||
+475
-28
@@ -1,19 +1,53 @@
|
||||
#include "stdafx.h"
|
||||
#include "PPUDisAsm.h"
|
||||
#include "PPUFunction.h"
|
||||
#include "PPUAnalyser.h"
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
#include "util/asm.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
const ppu_decoder<PPUDisAsm> s_ppu_disasm;
|
||||
const ppu_decoder<ppu_itype> s_ppu_itype;
|
||||
|
||||
extern const std::unordered_map<u32, std::string_view>& get_exported_function_names_as_addr_indexed_map();
|
||||
|
||||
enum class ppu_syscall_code : u64;
|
||||
|
||||
extern std::shared_ptr<CPUDisAsm> make_basic_ppu_disasm()
|
||||
{
|
||||
return std::make_shared<PPUDisAsm>(cpu_disasm_mode::normal, vm::g_sudo_addr);
|
||||
}
|
||||
|
||||
u32 PPUDisAsm::disasm(u32 pc)
|
||||
{
|
||||
last_opcode.clear();
|
||||
|
||||
if (pc < m_start_pc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_offset == vm::g_sudo_addr && !vm::check_addr(pc, vm::page_executable))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
dump_pc = pc;
|
||||
be_t<u32> op{};
|
||||
std::memcpy(&op, m_offset + pc, 4);
|
||||
m_op = op;
|
||||
|
||||
(this->*(s_ppu_disasm.decode(m_op)))({ m_op });
|
||||
|
||||
if (m_offset != vm::g_sudo_addr)
|
||||
{
|
||||
// Exported functions lookup is not allowed in this case
|
||||
format_by_mode();
|
||||
return 4;
|
||||
}
|
||||
|
||||
const auto& map = get_exported_function_names_as_addr_indexed_map();
|
||||
|
||||
if (auto it = map.find(pc); it != map.end())
|
||||
@@ -22,9 +56,323 @@ u32 PPUDisAsm::disasm(u32 pc)
|
||||
last_opcode += it->second;
|
||||
}
|
||||
|
||||
format_by_mode();
|
||||
return 4;
|
||||
}
|
||||
|
||||
std::pair<const void*, usz> PPUDisAsm::get_memory_span() const
|
||||
{
|
||||
return {m_offset + m_start_pc, (1ull << 32) - m_start_pc};
|
||||
}
|
||||
|
||||
std::unique_ptr<CPUDisAsm> PPUDisAsm::copy_type_erased() const
|
||||
{
|
||||
return std::make_unique<PPUDisAsm>(*this);
|
||||
}
|
||||
|
||||
std::pair<PPUDisAsm::const_op, u64> PPUDisAsm::try_get_const_op_gpr_value(u32 reg, u32 pc, u32 TTL) const
|
||||
{
|
||||
if (!TTL)
|
||||
{
|
||||
// Recursion limit (Time To Live)
|
||||
return {};
|
||||
}
|
||||
|
||||
if (pc == umax)
|
||||
{
|
||||
// Default arg: choose pc of previous instruction
|
||||
|
||||
if (dump_pc == 0)
|
||||
{
|
||||
// Do not underflow
|
||||
return {};
|
||||
}
|
||||
|
||||
pc = dump_pc - 4;
|
||||
}
|
||||
|
||||
#if __cpp_using_enum >= 201907
|
||||
using enum const_op;
|
||||
#else
|
||||
constexpr const_op none = const_op::none, form = const_op::form, xor_mask = const_op::xor_mask;
|
||||
#endif
|
||||
|
||||
// Scan PPU executable memory backwards until unmapped or non-executable memory block is encountered
|
||||
|
||||
for (u32 i = pc; i >= m_start_pc && (m_offset != vm::g_sudo_addr || vm::check_addr(i, vm::page_executable));)
|
||||
{
|
||||
const u32 opcode = *reinterpret_cast<const be_t<u32>*>(m_offset + i);
|
||||
const ppu_opcode_t op{ opcode };
|
||||
|
||||
const auto type = s_ppu_itype.decode(opcode);
|
||||
|
||||
auto is_branch = [](enum ppu_itype::type itype)
|
||||
{
|
||||
return itype == ppu_itype::BC || itype == ppu_itype::B || itype == ppu_itype::BCLR || itype == ppu_itype::BCCTR;
|
||||
};
|
||||
|
||||
if (is_branch(type) || type == ppu_itype::UNK)
|
||||
{
|
||||
// TODO: Detect calls, ignore them if reg is a non-volatile register
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get constant register value
|
||||
#define GET_CONST_OP_REG(var, reg, op) \
|
||||
{\
|
||||
/* Search for the constant value of the register*/\
|
||||
const auto [const_op, value] = try_get_const_op_gpr_value(reg, i - 4, TTL - 1);\
|
||||
\
|
||||
if (const_op != const_op::op)\
|
||||
{\
|
||||
/* Cannot compute constant value if register/operation is not constant*/\
|
||||
return {};\
|
||||
}\
|
||||
\
|
||||
var = value;\
|
||||
} void() /*<- Require a semicolon*/
|
||||
|
||||
#define GET_CONST_REG(var, reg) GET_CONST_OP_REG(var, reg, form)
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ppu_itype::ADDI:
|
||||
{
|
||||
if (op.rd != reg)
|
||||
{
|
||||
// Destination register is not relevant to us
|
||||
break;
|
||||
}
|
||||
|
||||
u64 reg_ra = 0;
|
||||
|
||||
if (op.ra)
|
||||
{
|
||||
GET_CONST_REG(reg_ra, op.ra);
|
||||
}
|
||||
|
||||
return { form, reg_ra + op.simm16 };
|
||||
}
|
||||
case ppu_itype::ADDIS:
|
||||
{
|
||||
if (op.rd != reg)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
u64 reg_ra = 0;
|
||||
|
||||
if (op.ra)
|
||||
{
|
||||
GET_CONST_REG(reg_ra, op.ra);
|
||||
}
|
||||
|
||||
return { form, reg_ra + op.simm16 * 65536 };
|
||||
}
|
||||
case ppu_itype::ORI:
|
||||
{
|
||||
if (op.rs == op.ra && !op.uimm16)
|
||||
{
|
||||
// NO-OP
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.ra != reg)
|
||||
{
|
||||
// Destination register is not relevant to us
|
||||
break;
|
||||
}
|
||||
|
||||
u64 reg_rs = 0;
|
||||
|
||||
GET_CONST_REG(reg_rs, op.rs);
|
||||
|
||||
return { form, reg_rs | op.uimm16 };
|
||||
}
|
||||
case ppu_itype::ORIS:
|
||||
{
|
||||
if (op.rs == op.ra && !op.uimm16)
|
||||
{
|
||||
// NO-OP
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.ra != reg)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
u64 reg_rs = 0;
|
||||
|
||||
GET_CONST_REG(reg_rs, op.rs);
|
||||
|
||||
return { form, reg_rs | (u64{op.uimm16} << 16)};
|
||||
}
|
||||
case ppu_itype::XORIS:
|
||||
{
|
||||
if (op.ra != reg)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto [const_op, reg_rs] = try_get_const_op_gpr_value(op.rs, i - 4, TTL - 1);
|
||||
|
||||
if (const_op == none)
|
||||
{
|
||||
return { xor_mask, (u64{op.uimm16} << 16) };
|
||||
}
|
||||
|
||||
if (const_op != form)
|
||||
{
|
||||
// Unexpected
|
||||
return {};
|
||||
}
|
||||
|
||||
return { form, reg_rs ^ (u64{op.uimm16} << 16)};
|
||||
}
|
||||
case ppu_itype::RLDICR:
|
||||
{
|
||||
if (op.ra != reg)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
u64 reg_rs = 0;
|
||||
|
||||
GET_CONST_REG(reg_rs, op.rs);
|
||||
|
||||
return { form, utils::rol64(reg_rs, op.sh64) & (~0ull << (op.mbe64 ^ 63)) };
|
||||
}
|
||||
case ppu_itype::OR:
|
||||
{
|
||||
if (op.rs == op.rb && op.rs == op.ra)
|
||||
{
|
||||
// NO-OP
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.ra != reg)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
u64 reg_rs = 0, reg_rb = 0;
|
||||
|
||||
GET_CONST_REG(reg_rs, op.rs);
|
||||
|
||||
// Try to optimize if it's a register move operation
|
||||
if (op.rs != op.rb)
|
||||
{
|
||||
GET_CONST_REG(reg_rb, op.rb);
|
||||
}
|
||||
|
||||
return { form, reg_rs | reg_rb };
|
||||
}
|
||||
case ppu_itype::XOR:
|
||||
{
|
||||
if (op.ra != reg)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.rs == op.rb)
|
||||
{
|
||||
return { form, 0 };
|
||||
}
|
||||
|
||||
const auto [const_op_rs, reg_rs] = try_get_const_op_gpr_value(op.rs, i - 4, TTL - 1);
|
||||
const auto [const_op_rb, reg_rb] = try_get_const_op_gpr_value(op.rb, i - 4, TTL - 1);
|
||||
|
||||
if (const_op_rs == form && const_op_rb == form)
|
||||
{
|
||||
// Normally it is not the case
|
||||
return { form, reg_rs ^ reg_rb };
|
||||
}
|
||||
|
||||
if (const_op_rs == form && const_op_rb == none)
|
||||
{
|
||||
return { xor_mask, reg_rs };
|
||||
}
|
||||
else if (const_op_rb == form && const_op_rs == none)
|
||||
{
|
||||
return { xor_mask, reg_rb };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Ordinary test
|
||||
// TODO: Proper detection of destination register(s) modification (if there are any)
|
||||
if (op.ra == reg || op.rd == reg)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
i -= 4;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
enum CellError : u32;
|
||||
|
||||
void comment_constant(std::string& last_opcode, u64 value, bool print_float = false)
|
||||
{
|
||||
// Test if potentially a CELL error
|
||||
if ((value >> 28) == 0xf'ffff'fff8u || (value >> 28) == 0x8u)
|
||||
{
|
||||
const usz old_size = last_opcode.size();
|
||||
|
||||
// Comment as CELL error
|
||||
fmt::append(last_opcode, " #%s (0x%xh)", CellError{static_cast<u32>(value)}, value);
|
||||
|
||||
// Test if failed to format (appended " #0x8".. in such case)
|
||||
if (last_opcode[old_size + 2] != '0')
|
||||
{
|
||||
// Success
|
||||
return;
|
||||
}
|
||||
|
||||
// Revert and fallback
|
||||
last_opcode.resize(old_size);
|
||||
}
|
||||
|
||||
// Comment constant formation
|
||||
fmt::append(last_opcode, " #0x%xh", value);
|
||||
|
||||
if (print_float && ((value >> 31) <= 1u || (value >> 31) == 0x1'ffff'ffffu))
|
||||
{
|
||||
const f32 float_val = std::bit_cast<f32>(static_cast<u32>(value));
|
||||
|
||||
if (std::isfinite(float_val))
|
||||
{
|
||||
const usz old_size = last_opcode.size();
|
||||
|
||||
fmt::append(last_opcode, " (%.6gf)", float_val);
|
||||
|
||||
if (usz pos = last_opcode.find_first_of('.', old_size); pos == umax)
|
||||
{
|
||||
// No decimal point has been inserted, force insertion
|
||||
last_opcode.insert(last_opcode.size() - 2, ".0"sv);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::append(last_opcode, " (%g)", float_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr std::pair<const char*, char> get_BC_info(u32 bo, u32 bi)
|
||||
{
|
||||
std::pair<const char*, char> info{};
|
||||
@@ -903,12 +1251,41 @@ void PPUDisAsm::SUBFIC(ppu_opcode_t op)
|
||||
|
||||
void PPUDisAsm::CMPLI(ppu_opcode_t op)
|
||||
{
|
||||
DisAsm_CR1_R1_IMM(op.l10 ? "cmpdi" : "cmpwi", op.crfd, op.ra, op.uimm16);
|
||||
DisAsm_CR1_R1_IMM(op.l10 ? "cmpldi" : "cmplwi", op.crfd, op.ra, op.uimm16);
|
||||
|
||||
// Try to obtain the true constant value we are comparing against, comment on success
|
||||
// Upper 16/48 bits of it
|
||||
if (auto [is_xor, value] = try_get_const_xor_gpr_value(op.ra); is_xor && !(value & 0xFFFF))
|
||||
{
|
||||
// Fixup value (merge the lower 16-bits of that value)
|
||||
value |= op.uimm16;
|
||||
|
||||
if (!op.l10)
|
||||
{
|
||||
value = static_cast<u32>(value);
|
||||
}
|
||||
|
||||
comment_constant(last_opcode, value);
|
||||
}
|
||||
}
|
||||
|
||||
void PPUDisAsm::CMPI(ppu_opcode_t op)
|
||||
{
|
||||
DisAsm_CR1_R1_IMM(op.l10 ? "cmpdi" : "cmpwi", op.crfd, op.ra, op.simm16);
|
||||
|
||||
// See CMPLI
|
||||
if (auto [is_xor, value] = try_get_const_xor_gpr_value(op.ra); is_xor && !(value & 0xFFFF))
|
||||
{
|
||||
// Signed fixup
|
||||
value ^= s64{op.simm16};
|
||||
|
||||
if (!op.l10)
|
||||
{
|
||||
value = static_cast<u32>(value);
|
||||
}
|
||||
|
||||
comment_constant(last_opcode, value);
|
||||
}
|
||||
}
|
||||
|
||||
void PPUDisAsm::ADDIC(ppu_opcode_t op)
|
||||
@@ -925,6 +1302,12 @@ void PPUDisAsm::ADDI(ppu_opcode_t op)
|
||||
else
|
||||
{
|
||||
DisAsm_R2_IMM("addi", op.rd, op.ra, op.simm16);
|
||||
|
||||
if (auto [is_const, value] = try_get_const_gpr_value(op.ra); is_const)
|
||||
{
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value + op.simm16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,6 +1320,12 @@ void PPUDisAsm::ADDIS(ppu_opcode_t op)
|
||||
else
|
||||
{
|
||||
DisAsm_R2_IMM("addis", op.rd, op.ra, op.simm16);
|
||||
|
||||
if (auto [is_const, value] = try_get_const_gpr_value(op.ra); is_const)
|
||||
{
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value + op.simm16 * 65536);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,7 +1339,7 @@ void PPUDisAsm::BC(ppu_opcode_t op)
|
||||
|
||||
if (m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk));
|
||||
fmt::append(last_opcode, "bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -958,7 +1347,7 @@ void PPUDisAsm::BC(ppu_opcode_t op)
|
||||
|
||||
if (!inst)
|
||||
{
|
||||
Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk));
|
||||
fmt::append(last_opcode, "bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -994,12 +1383,46 @@ void PPUDisAsm::BC(ppu_opcode_t op)
|
||||
|
||||
void PPUDisAsm::SC(ppu_opcode_t op)
|
||||
{
|
||||
if (op.opcode != ppu_instructions::SC(0))
|
||||
if (op.opcode != ppu_instructions::SC(0) && op.opcode != ppu_instructions::SC(1))
|
||||
{
|
||||
return UNK(op);
|
||||
}
|
||||
|
||||
Write("sc");
|
||||
fmt::append(last_opcode, "%-*s ", PadOp(), "sc");
|
||||
|
||||
if (op.lev)
|
||||
{
|
||||
fmt::append(last_opcode, "%u ", op.lev);
|
||||
}
|
||||
|
||||
// Try to get constant syscall index
|
||||
auto [is_const, index] = try_get_const_gpr_value(11);
|
||||
|
||||
if (!is_const)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (op.lev)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Lv2 syscall
|
||||
if (index < 1024u)
|
||||
{
|
||||
fmt::append(last_opcode, "#%s", ppu_syscall_code{index});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// Lv1 syscall
|
||||
fmt::append(last_opcode, "#lv1_syscall_%u", index);
|
||||
return;
|
||||
}
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
void PPUDisAsm::B(ppu_opcode_t op)
|
||||
@@ -1010,7 +1433,7 @@ void PPUDisAsm::B(ppu_opcode_t op)
|
||||
|
||||
if (m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("b 0x%x, %d, %d", li, aa, lk));
|
||||
fmt::append(last_opcode, "b 0x%x, %d, %d", li, aa, lk);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1048,7 +1471,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op)
|
||||
|
||||
if (bo == 0b10100)
|
||||
{
|
||||
Write(lk ? "blrl" : "blr");
|
||||
last_opcode += (lk ? "blrl" : "blr");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1056,7 +1479,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op)
|
||||
|
||||
if (!inst)
|
||||
{
|
||||
Write(fmt::format("bclr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk));
|
||||
fmt::append(last_opcode, "bclr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1091,7 +1514,7 @@ void PPUDisAsm::CRANDC(ppu_opcode_t op)
|
||||
|
||||
void PPUDisAsm::ISYNC(ppu_opcode_t)
|
||||
{
|
||||
Write("isync");
|
||||
last_opcode += "isync";
|
||||
}
|
||||
|
||||
void PPUDisAsm::CRXOR(ppu_opcode_t op)
|
||||
@@ -1151,7 +1574,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op)
|
||||
|
||||
if (bo == 0b10100)
|
||||
{
|
||||
Write(lk ? "bctrl" : "bctr");
|
||||
last_opcode += (lk ? "bctrl" : "bctr");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1160,7 +1583,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op)
|
||||
if (!inst || inst[1] == 'd')
|
||||
{
|
||||
// Invalid or unknown bcctr form
|
||||
Write(fmt::format("bcctr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk));
|
||||
fmt::append(last_opcode, "bcctr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1199,25 +1622,49 @@ void PPUDisAsm::RLWNM(ppu_opcode_t op)
|
||||
|
||||
void PPUDisAsm::ORI(ppu_opcode_t op)
|
||||
{
|
||||
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) return Write("nop");
|
||||
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) { last_opcode += "nop"; return; }
|
||||
if (op.uimm16 == 0) return DisAsm_R2("mr", op.ra, op.rs);
|
||||
DisAsm_R2_IMM("ori", op.ra, op.rs, op.uimm16);
|
||||
|
||||
if (auto [is_const, value] = try_get_const_gpr_value(op.rs); is_const)
|
||||
{
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value | op.uimm16);
|
||||
}
|
||||
}
|
||||
|
||||
void PPUDisAsm::ORIS(ppu_opcode_t op)
|
||||
{
|
||||
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) return Write("nop");
|
||||
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) { last_opcode += "nop"; return; }
|
||||
DisAsm_R2_IMM("oris", op.ra, op.rs, op.uimm16);
|
||||
|
||||
if (auto [is_const, value] = try_get_const_gpr_value(op.rs); is_const)
|
||||
{
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value | (op.uimm16 << 16));
|
||||
}
|
||||
}
|
||||
|
||||
void PPUDisAsm::XORI(ppu_opcode_t op)
|
||||
{
|
||||
DisAsm_R2_IMM("xori", op.ra, op.rs, op.uimm16);
|
||||
|
||||
if (auto [is_const, value] = try_get_const_gpr_value(op.rs); is_const)
|
||||
{
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value ^ op.uimm16);
|
||||
}
|
||||
}
|
||||
|
||||
void PPUDisAsm::XORIS(ppu_opcode_t op)
|
||||
{
|
||||
DisAsm_R2_IMM("xoris", op.ra, op.rs, op.uimm16);
|
||||
|
||||
if (auto [is_const, value] = try_get_const_gpr_value(op.rs); is_const)
|
||||
{
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value ^ (op.uimm16 << 16));
|
||||
}
|
||||
}
|
||||
|
||||
void PPUDisAsm::ANDI(ppu_opcode_t op)
|
||||
@@ -1674,7 +2121,7 @@ void PPUDisAsm::MFSPR(ppu_opcode_t op)
|
||||
case 0x001: DisAsm_R1("mfxer", op.rd); break;
|
||||
case 0x008: DisAsm_R1("mflr", op.rd); break;
|
||||
case 0x009: DisAsm_R1("mfctr", op.rd); break;
|
||||
default: DisAsm_R1_IMM("mfspr", op.rd, op.spr); break;
|
||||
default: DisAsm_R1_IMM("mfspr", op.rd, n); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1750,10 +2197,10 @@ void PPUDisAsm::OR(ppu_opcode_t op)
|
||||
{
|
||||
switch (op.opcode)
|
||||
{
|
||||
case 0x7f9ce378: return Write("db8cyc");
|
||||
case 0x7fbdeb78: return Write("db10cyc");
|
||||
case 0x7fdef378: return Write("db12cyc");
|
||||
case 0x7ffffb78: return Write("db16cyc");
|
||||
case 0x7f9ce378: last_opcode += "db8cyc"; return;
|
||||
case 0x7fbdeb78: last_opcode += "db10cyc"; return;
|
||||
case 0x7fdef378: last_opcode += "db12cyc"; return;
|
||||
case 0x7ffffb78: last_opcode += "db16cyc"; return;
|
||||
default: DisAsm_R2_RC("mr", op.ra, op.rb, op.rc);
|
||||
}
|
||||
}
|
||||
@@ -1782,7 +2229,7 @@ void PPUDisAsm::MTSPR(ppu_opcode_t op)
|
||||
case 0x001: DisAsm_R1("mtxer", op.rs); break;
|
||||
case 0x008: DisAsm_R1("mtlr", op.rs); break;
|
||||
case 0x009: DisAsm_R1("mtctr", op.rs); break;
|
||||
default: DisAsm_IMM_R1("mtspr", op.spr, op.rs); break;
|
||||
default: DisAsm_R1_IMM("mtspr", n, op.rs); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1863,7 +2310,7 @@ void PPUDisAsm::LFSUX(ppu_opcode_t op)
|
||||
|
||||
void PPUDisAsm::SYNC(ppu_opcode_t op)
|
||||
{
|
||||
Write(op.l10 ? "lwsync" : "sync");
|
||||
last_opcode += (op.l10 ? "lwsync" : "sync");
|
||||
}
|
||||
|
||||
void PPUDisAsm::LFDX(ppu_opcode_t op)
|
||||
@@ -1953,7 +2400,7 @@ void PPUDisAsm::LVRXL(ppu_opcode_t op)
|
||||
|
||||
void PPUDisAsm::DSS(ppu_opcode_t)
|
||||
{
|
||||
Write("dss()");
|
||||
last_opcode += "dss";
|
||||
}
|
||||
|
||||
void PPUDisAsm::SRAWI(ppu_opcode_t op)
|
||||
@@ -1968,7 +2415,7 @@ void PPUDisAsm::SRADI(ppu_opcode_t op)
|
||||
|
||||
void PPUDisAsm::EIEIO(ppu_opcode_t)
|
||||
{
|
||||
Write("eieio");
|
||||
last_opcode += "eieio";
|
||||
}
|
||||
|
||||
void PPUDisAsm::STVLXL(ppu_opcode_t op)
|
||||
@@ -2213,7 +2660,7 @@ void PPUDisAsm::FNMADDS(ppu_opcode_t op)
|
||||
|
||||
void PPUDisAsm::MTFSB1(ppu_opcode_t op)
|
||||
{
|
||||
Write(fmt::format("mtfsb1%s %d", op.rc ? "." : "", op.crbd));
|
||||
fmt::append(last_opcode, "%-*s %d", PadOp(), op.rc ? "mtfsb1." : "mtfsb1", op.crbd);
|
||||
}
|
||||
|
||||
void PPUDisAsm::MCRFS(ppu_opcode_t op)
|
||||
@@ -2223,12 +2670,12 @@ void PPUDisAsm::MCRFS(ppu_opcode_t op)
|
||||
|
||||
void PPUDisAsm::MTFSB0(ppu_opcode_t op)
|
||||
{
|
||||
Write(fmt::format("mtfsb0%s %d", op.rc ? "." : "", op.crbd));
|
||||
fmt::append(last_opcode, "%-*s %d", PadOp(), op.rc ? "mtfsb0." : "mtfsb0", op.crbd);
|
||||
}
|
||||
|
||||
void PPUDisAsm::MTFSFI(ppu_opcode_t op)
|
||||
{
|
||||
Write(fmt::format("mtfsfi%s cr%d,%d,%d", op.rc ? "." : "", op.crfd, op.i, op.l15));
|
||||
fmt::append(last_opcode, "%-*s cr%d,%d,%d", PadOp(), op.rc ? "mtfsfi." : "mtfsfi", op.crfd, op.i, op.l15);
|
||||
}
|
||||
|
||||
void PPUDisAsm::MFFS(ppu_opcode_t op)
|
||||
@@ -2238,7 +2685,7 @@ void PPUDisAsm::MFFS(ppu_opcode_t op)
|
||||
|
||||
void PPUDisAsm::MTFSF(ppu_opcode_t op)
|
||||
{
|
||||
Write(fmt::format("mtfsf%s %d,f%d,%d,%d", op.rc ? "." : "", op.rc, op.flm, op.frb, op.l6, op.l15));
|
||||
fmt::append(last_opcode, "%-*s %d,f%d,%d,%d", PadOp(), op.rc ? "mtfsf." : "mtfsf", op.rc, op.flm, op.frb, op.l6, op.l15);
|
||||
}
|
||||
|
||||
void PPUDisAsm::FCMPU(ppu_opcode_t op)
|
||||
@@ -2367,10 +2814,10 @@ void PPUDisAsm::UNK(ppu_opcode_t)
|
||||
|
||||
if (dump_pc % 8 == 4 && index < ppu_function_manager::get().size())
|
||||
{
|
||||
Write(fmt::format("Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index));
|
||||
fmt::append(last_opcode, "Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Write("?? ??");
|
||||
last_opcode += "?? ??";
|
||||
}
|
||||
|
||||
+147
-108
@@ -29,264 +29,303 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
void DisAsm_V4(const std::string& op, u32 v0, u32 v1, u32 v2, u32 v3)
|
||||
void DisAsm_V4(std::string_view op, u32 v0, u32 v1, u32 v2, u32 v3)
|
||||
{
|
||||
Write(fmt::format("%s v%d,v%d,v%d,v%d", FixOp(op), v0, v1, v2, v3));
|
||||
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,v%d", PadOp(), op, v0, v1, v2, v3);
|
||||
}
|
||||
void DisAsm_V3_UIMM(const std::string& op, u32 v0, u32 v1, u32 v2, u32 uimm)
|
||||
void DisAsm_V3_UIMM(std::string_view op, u32 v0, u32 v1, u32 v2, u32 uimm)
|
||||
{
|
||||
Write(fmt::format("%s v%d,v%d,v%d,%s", FixOp(op), v0, v1, v2, uimm));
|
||||
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,%s", PadOp(), op, v0, v1, v2, uimm);
|
||||
}
|
||||
void DisAsm_V3(const std::string& op, u32 v0, u32 v1, u32 v2)
|
||||
void DisAsm_V3(std::string_view op, u32 v0, u32 v1, u32 v2)
|
||||
{
|
||||
Write(fmt::format("%s v%d,v%d,v%d", FixOp(op), v0, v1, v2));
|
||||
fmt::append(last_opcode, "%-*s v%d,v%d,v%d", PadOp(), op, v0, v1, v2);
|
||||
}
|
||||
void DisAsm_V2_UIMM(const std::string& op, u32 v0, u32 v1, u32 uimm)
|
||||
void DisAsm_V2_UIMM(std::string_view op, u32 v0, u32 v1, u32 uimm)
|
||||
{
|
||||
Write(fmt::format("%s v%d,v%d,%s", FixOp(op), v0, v1, uimm));
|
||||
fmt::append(last_opcode, "%-*s v%d,v%d,%s", PadOp(), op, v0, v1, uimm);
|
||||
}
|
||||
void DisAsm_V2(const std::string& op, u32 v0, u32 v1)
|
||||
void DisAsm_V2(std::string_view op, u32 v0, u32 v1)
|
||||
{
|
||||
Write(fmt::format("%s v%d,v%d", FixOp(op), v0, v1));
|
||||
fmt::append(last_opcode, "%-*s v%d,v%d", PadOp(), op, v0, v1);
|
||||
}
|
||||
void DisAsm_V1_SIMM(const std::string& op, u32 v0, s32 simm)
|
||||
void DisAsm_V1_SIMM(std::string_view op, u32 v0, s32 simm)
|
||||
{
|
||||
Write(fmt::format("%s v%d,%s", FixOp(op), v0, SignedHex(simm)));
|
||||
fmt::append(last_opcode, "%-*s v%d,%s", PadOp(), op, v0, SignedHex(simm));
|
||||
}
|
||||
void DisAsm_V1(const std::string& op, u32 v0)
|
||||
void DisAsm_V1(std::string_view op, u32 v0)
|
||||
{
|
||||
Write(fmt::format("%s v%d", FixOp(op), v0));
|
||||
fmt::append(last_opcode, "%-*s v%d", PadOp(), op, v0);
|
||||
}
|
||||
void DisAsm_V1_R2(const std::string& op, u32 v0, u32 r1, u32 r2)
|
||||
void DisAsm_V1_R2(std::string_view op, u32 v0, u32 r1, u32 r2)
|
||||
{
|
||||
Write(fmt::format("%s v%d,r%d,r%d", FixOp(op), v0, r1, r2));
|
||||
fmt::append(last_opcode, "%-*s v%d,r%d,r%d", PadOp(), op, v0, r1, r2);
|
||||
}
|
||||
void DisAsm_CR1_F2_RC(const std::string& op, u32 cr0, u32 f0, u32 f1, u32 rc)
|
||||
void DisAsm_CR1_F2_RC(std::string_view op, u32 cr0, u32 f0, u32 f1, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s cr%d,f%d,f%d", FixOp(op + (rc ? "." : "")), cr0, f0, f1));
|
||||
fmt::append(last_opcode, "%-*s cr%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, cr0, f0, f1);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_CR1_F2(const std::string& op, u32 cr0, u32 f0, u32 f1)
|
||||
void DisAsm_CR1_F2(std::string_view op, u32 cr0, u32 f0, u32 f1)
|
||||
{
|
||||
DisAsm_CR1_F2_RC(op, cr0, f0, f1, false);
|
||||
}
|
||||
void DisAsm_INT1_R2(const std::string& op, u32 i0, u32 r0, u32 r1)
|
||||
void DisAsm_INT1_R2(std::string_view op, u32 i0, u32 r0, u32 r1)
|
||||
{
|
||||
Write(fmt::format("%s %d,r%d,r%d", FixOp(op), i0, r0, r1));
|
||||
fmt::append(last_opcode, "%-*s %d,r%d,r%d", PadOp(), op, i0, r0, r1);
|
||||
}
|
||||
void DisAsm_INT1_R1_IMM(const std::string& op, u32 i0, u32 r0, s32 imm0)
|
||||
void DisAsm_INT1_R1_IMM(std::string_view op, u32 i0, u32 r0, s32 imm0)
|
||||
{
|
||||
Write(fmt::format("%s %d,r%d,%s", FixOp(op), i0, r0, SignedHex(imm0)));
|
||||
fmt::append(last_opcode, "%-*s %d,r%d,%s", PadOp(), op, i0, r0, SignedHex(imm0));
|
||||
}
|
||||
void DisAsm_INT1_R1_RC(const std::string& op, u32 i0, u32 r0, u32 rc)
|
||||
void DisAsm_INT1_R1_RC(std::string_view op, u32 i0, u32 r0, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s %d,r%d", FixOp(op + (rc ? "." : "")), i0, r0));
|
||||
fmt::append(last_opcode, "%-*s %d,r%d", PadOp(op, rc ? 1 : 0), op, i0, r0);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_INT1_R1(const std::string& op, u32 i0, u32 r0)
|
||||
void DisAsm_INT1_R1(std::string_view op, u32 i0, u32 r0)
|
||||
{
|
||||
DisAsm_INT1_R1_RC(op, i0, r0, false);
|
||||
}
|
||||
void DisAsm_F4_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
|
||||
void DisAsm_F4_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s f%d,f%d,f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1, f2, f3));
|
||||
fmt::append(last_opcode, "%-*s f%d,f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2, f3);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_F3_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 rc)
|
||||
void DisAsm_F3_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s f%d,f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1, f2));
|
||||
fmt::append(last_opcode, "%-*s f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_F3(const std::string& op, u32 f0, u32 f1, u32 f2)
|
||||
void DisAsm_F3(std::string_view op, u32 f0, u32 f1, u32 f2)
|
||||
{
|
||||
DisAsm_F3_RC(op, f0, f1, f2, false);
|
||||
}
|
||||
void DisAsm_F2_RC(const std::string& op, u32 f0, u32 f1, u32 rc)
|
||||
void DisAsm_F2_RC(std::string_view op, u32 f0, u32 f1, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1));
|
||||
fmt::append(last_opcode, "%-*s f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_F2(const std::string& op, u32 f0, u32 f1)
|
||||
void DisAsm_F2(std::string_view op, u32 f0, u32 f1)
|
||||
{
|
||||
DisAsm_F2_RC(op, f0, f1, false);
|
||||
}
|
||||
void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1)
|
||||
void DisAsm_F1_R2(std::string_view op, u32 f0, u32 r0, u32 r1)
|
||||
{
|
||||
if (m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1));
|
||||
fmt::append(last_opcode, "%-*s f%d,r%d,r%d", PadOp(), op, f0, r0, r1);
|
||||
return;
|
||||
}
|
||||
|
||||
Write(fmt::format("%s f%d,r%d(r%d)", FixOp(op), f0, r0, r1));
|
||||
fmt::append(last_opcode, "%-*s f%d,r%d(r%d)", PadOp(), op, f0, r0, r1);
|
||||
}
|
||||
void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc)
|
||||
void DisAsm_F1_IMM_R1_RC(std::string_view op, u32 f0, s32 imm0, u32 r0, u32 rc)
|
||||
{
|
||||
if (m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s f%d,r%d,%s", FixOp(op + (rc ? "." : "")), f0, r0, SignedHex(imm0)));
|
||||
fmt::append(last_opcode, "%-*s f%d,r%d,%s", PadOp(op, rc ? 1 : 0), op, f0, r0, SignedHex(imm0));
|
||||
insert_char_if(op, !!rc);
|
||||
return;
|
||||
}
|
||||
|
||||
Write(fmt::format("%s f%d,%s(r%d)", FixOp(op + (rc ? "." : "")), f0, SignedHex(imm0), r0));
|
||||
fmt::append(last_opcode, "%-*s f%d,%s(r%d)", PadOp(op, rc ? 1 : 0), op, f0, SignedHex(imm0), r0);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_F1_IMM_R1(const std::string& op, u32 f0, s32 imm0, u32 r0)
|
||||
void DisAsm_F1_IMM_R1(std::string_view op, u32 f0, s32 imm0, u32 r0)
|
||||
{
|
||||
DisAsm_F1_IMM_R1_RC(op, f0, imm0, r0, false);
|
||||
}
|
||||
void DisAsm_F1_RC(const std::string& op, u32 f0, u32 rc)
|
||||
void DisAsm_F1_RC(std::string_view op, u32 f0, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s f%d", FixOp(op + (rc ? "." : "")), f0));
|
||||
fmt::append(last_opcode, "%-*s f%d", PadOp(op, rc ? 1 : 0), op, f0);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R1_RC(const std::string& op, u32 r0, u32 rc)
|
||||
void DisAsm_R1_RC(std::string_view op, u32 r0, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s r%d", FixOp(op + (rc ? "." : "")), r0));
|
||||
fmt::append(last_opcode, "%-*s r%d", PadOp(op, rc ? 1 : 0), op, r0);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R1(const std::string& op, u32 r0)
|
||||
void DisAsm_R1(std::string_view op, u32 r0)
|
||||
{
|
||||
DisAsm_R1_RC(op, r0, false);
|
||||
}
|
||||
void DisAsm_R2_OE_RC(const std::string& op, u32 r0, u32 r1, u32 _oe, u32 rc)
|
||||
void DisAsm_R2_OE_RC(std::string_view op, u32 r0, u32 r1, u32 _oe, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s r%d,r%d", FixOp(op + (_oe ? "o" : "") + (rc ? "." : "")), r0, r1));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d", PadOp(op, (rc ? 1 : 0) + (_oe ? 1 : 0)), op, r0, r1);
|
||||
insert_char_if(insert_char_if(op, !!_oe, 'o'), !!rc, '.');
|
||||
}
|
||||
void DisAsm_R2_RC(const std::string& op, u32 r0, u32 r1, u32 rc)
|
||||
void DisAsm_R2_RC(std::string_view op, u32 r0, u32 r1, u32 rc)
|
||||
{
|
||||
DisAsm_R2_OE_RC(op, r0, r1, false, rc);
|
||||
}
|
||||
void DisAsm_R2(const std::string& op, u32 r0, u32 r1)
|
||||
void DisAsm_R2(std::string_view op, u32 r0, u32 r1)
|
||||
{
|
||||
DisAsm_R2_RC(op, r0, r1, false);
|
||||
}
|
||||
void DisAsm_R3_OE_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 _oe, u32 rc)
|
||||
void DisAsm_R3_OE_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 _oe, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s r%d,r%d,r%d", FixOp(op + (rc ? "." : "") + (_oe ? "o" : "")), r0, r1, r2));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,r%d", PadOp(op, (rc ? 1 : 0) + (_oe ? 1 : 0)), op, r0, r1, r2);
|
||||
insert_char_if(insert_char_if(op, !!_oe, 'o'), !!rc, '.');
|
||||
}
|
||||
void DisAsm_R3_INT2_RC(const std::string& op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
|
||||
void DisAsm_R3_INT2_RC(std::string_view op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s r%d,r%d,r%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, r2, i0, i1));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, r2, i0, i1);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R3_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 rc)
|
||||
void DisAsm_R3_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 rc)
|
||||
{
|
||||
DisAsm_R3_OE_RC(op, r0, r1, r2, false, rc);
|
||||
}
|
||||
void DisAsm_R3(const std::string& op, u32 r0, u32 r1, u32 r2)
|
||||
void DisAsm_R3(std::string_view op, u32 r0, u32 r1, u32 r2)
|
||||
{
|
||||
DisAsm_R3_RC(op, r0, r1, r2, false);
|
||||
}
|
||||
void DisAsm_R2_INT3_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
|
||||
void DisAsm_R2_INT3_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s r%d,r%d,%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0, i1, i2));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1, i2);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R2_INT3(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
|
||||
void DisAsm_R2_INT3(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
|
||||
{
|
||||
DisAsm_R2_INT3_RC(op, r0, r1, i0, i1, i2, false);
|
||||
}
|
||||
void DisAsm_R2_INT2_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
|
||||
void DisAsm_R2_INT2_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s r%d,r%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0, i1));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R2_INT2(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1)
|
||||
void DisAsm_R2_INT2(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1)
|
||||
{
|
||||
DisAsm_R2_INT2_RC(op, r0, r1, i0, i1, false);
|
||||
}
|
||||
void DisAsm_R2_INT1_RC(const std::string& op, u32 r0, u32 r1, s32 i0, u32 rc)
|
||||
void DisAsm_R2_INT1_RC(std::string_view op, u32 r0, u32 r1, s32 i0, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s r%d,r%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_R2_INT1(const std::string& op, u32 r0, u32 r1, s32 i0)
|
||||
void DisAsm_R2_INT1(std::string_view op, u32 r0, u32 r1, s32 i0)
|
||||
{
|
||||
DisAsm_R2_INT1_RC(op, r0, r1, i0, false);
|
||||
}
|
||||
void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0)
|
||||
void DisAsm_R2_IMM(std::string_view op, u32 r0, u32 r1, s32 imm0)
|
||||
{
|
||||
if (m_mode == cpu_disasm_mode::compiler_elf)
|
||||
{
|
||||
Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0)));
|
||||
fmt::append(last_opcode, "%-*s r%d,r%d,%s", PadOp(), op, r0, r1, SignedHex(imm0));
|
||||
return;
|
||||
}
|
||||
|
||||
Write(fmt::format("%s r%d,%s(r%d)", FixOp(op), r0, SignedHex(imm0), r1));
|
||||
fmt::append(last_opcode, "%-*s r%d,%s(r%d)", PadOp(), op, r0, SignedHex(imm0), r1);
|
||||
}
|
||||
void DisAsm_R1_IMM(const std::string& op, u32 r0, s32 imm0)
|
||||
void DisAsm_R1_IMM(std::string_view op, u32 r0, s32 imm0)
|
||||
{
|
||||
Write(fmt::format("%s r%d,%s", FixOp(op), r0, SignedHex(imm0)));
|
||||
fmt::append(last_opcode, "%-*s r%d,%s", PadOp(), op, r0, SignedHex(imm0));
|
||||
}
|
||||
void DisAsm_IMM_R1(const std::string& op, s32 imm0, u32 r0)
|
||||
void DisAsm_IMM_R1(std::string_view op, s32 imm0, u32 r0)
|
||||
{
|
||||
Write(fmt::format("%s %d,r%d #%x", FixOp(op), imm0, r0, imm0));
|
||||
fmt::append(last_opcode, "%-*s %d,r%d #%x", PadOp(), op, imm0, r0, imm0);
|
||||
}
|
||||
void DisAsm_CR1_R1_IMM(const std::string& op, u32 cr0, u32 r0, s32 imm0)
|
||||
void DisAsm_CR1_R1_IMM(std::string_view op, u32 cr0, u32 r0, s32 imm0)
|
||||
{
|
||||
Write(fmt::format("%s cr%d,r%d,%s", FixOp(op), cr0, r0, SignedHex(imm0)));
|
||||
fmt::append(last_opcode, "%-*s cr%d,r%d,%s", PadOp(), op, cr0, r0, SignedHex(imm0));
|
||||
}
|
||||
void DisAsm_CR1_R2_RC(const std::string& op, u32 cr0, u32 r0, u32 r1, u32 rc)
|
||||
void DisAsm_CR1_R2_RC(std::string_view op, u32 cr0, u32 r0, u32 r1, u32 rc)
|
||||
{
|
||||
Write(fmt::format("%s%s cr%d,r%d,r%d", FixOp(op), (rc ? "." : ""), cr0, r0, r1));
|
||||
fmt::append(last_opcode, "%-*s cr%d,r%d,r%d", PadOp(op, rc ? 1 : 0), op, cr0, r0, r1);
|
||||
insert_char_if(op, !!rc);
|
||||
}
|
||||
void DisAsm_CR1_R1(const std::string& op, u32 cr0, u32 r0)
|
||||
void DisAsm_CR1_R1(std::string_view op, u32 cr0, u32 r0)
|
||||
{
|
||||
Write(fmt::format("%s cr%d,r%d", FixOp(op), cr0, r0));
|
||||
fmt::append(last_opcode, "%-*s cr%d,r%d", PadOp(), op, cr0, r0);
|
||||
}
|
||||
void DisAsm_R1_CR1(const std::string& op, u32 r0, u32 cr0)
|
||||
void DisAsm_R1_CR1(std::string_view op, u32 r0, u32 cr0)
|
||||
{
|
||||
Write(fmt::format("%s r%d,cr%d", FixOp(op), r0, cr0));
|
||||
fmt::append(last_opcode, "%-*s r%d,cr%d", PadOp(), op, r0, cr0);
|
||||
}
|
||||
void DisAsm_CR1_R2(const std::string& op, u32 cr0, u32 r0, u32 r1)
|
||||
void DisAsm_CR1_R2(std::string_view op, u32 cr0, u32 r0, u32 r1)
|
||||
{
|
||||
DisAsm_CR1_R2_RC(op, cr0, r0, r1, false);
|
||||
}
|
||||
void DisAsm_CR2(const std::string& op, u32 cr0, u32 cr1)
|
||||
void DisAsm_CR2(std::string_view op, u32 cr0, u32 cr1)
|
||||
{
|
||||
Write(fmt::format("%s cr%d,cr%d", FixOp(op), cr0, cr1));
|
||||
fmt::append(last_opcode, "%-*s cr%d,cr%d", PadOp(), op, cr0, cr1);
|
||||
}
|
||||
void DisAsm_BI1(const std::string& op, const int i0)
|
||||
void DisAsm_BI1(std::string_view op, const int i0)
|
||||
{
|
||||
Write(fmt::format("%s cr%d[%s]", FixOp(op), i0 / 4, get_partial_BI_field(i0)));
|
||||
fmt::append(last_opcode, "%-*s cr%d[%s]", PadOp(), op, i0 / 4, get_partial_BI_field(i0));
|
||||
}
|
||||
void DisAsm_BI2(const std::string& op, const int i0, const int i1)
|
||||
void DisAsm_BI2(std::string_view op, const int i0, const int i1)
|
||||
{
|
||||
Write(fmt::format("%s cr%d[%s],cr%d[%s]", FixOp(op), i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1)));
|
||||
fmt::append(last_opcode, "%-*s cr%d[%s],cr%d[%s]", PadOp(), op, i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1));
|
||||
}
|
||||
void DisAsm_BI3(const std::string& op, const int i0, const int i1, const int i2)
|
||||
void DisAsm_BI3(std::string_view op, const int i0, const int i1, const int i2)
|
||||
{
|
||||
Write(fmt::format("%s cr%d[%s],cr%d[%s],cr%d[%s]", FixOp(op),
|
||||
i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1), i2 / 4, get_partial_BI_field(i2)));
|
||||
fmt::append(last_opcode, "%-*s cr%d[%s],cr%d[%s],cr%d[%s]", PadOp(), op,
|
||||
i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1), i2 / 4, get_partial_BI_field(i2));
|
||||
}
|
||||
void DisAsm_INT3(const std::string& op, const int i0, const int i1, const int i2)
|
||||
void DisAsm_INT3(std::string_view op, const int i0, const int i1, const int i2)
|
||||
{
|
||||
Write(fmt::format("%s %d,%d,%d", FixOp(op), i0, i1, i2));
|
||||
fmt::append(last_opcode, "%-*s %d,%d,%d", PadOp(), op, i0, i1, i2);
|
||||
}
|
||||
void DisAsm_INT1(const std::string& op, const int i0)
|
||||
void DisAsm_INT1(std::string_view op, const int i0)
|
||||
{
|
||||
Write(fmt::format("%s %d", FixOp(op), i0));
|
||||
fmt::append(last_opcode, "%-*s %d", PadOp(), op, i0);
|
||||
}
|
||||
void DisAsm_BRANCH(const std::string& op, const int pc)
|
||||
void DisAsm_BRANCH(std::string_view op, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s 0x%x", FixOp(op), DisAsmBranchTarget(pc)));
|
||||
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, DisAsmBranchTarget(pc));
|
||||
}
|
||||
void DisAsm_BRANCH_A(const std::string& op, const int pc)
|
||||
void DisAsm_BRANCH_A(std::string_view op, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s 0x%x", FixOp(op), pc));
|
||||
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, pc);
|
||||
}
|
||||
void DisAsm_B2_BRANCH(const std::string& op, u32 b0, u32 b1, const int pc)
|
||||
void DisAsm_B2_BRANCH(std::string_view op, u32 b0, u32 b1, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s %d,%d,0x%x ", FixOp(op), b0, b1, DisAsmBranchTarget(pc)));
|
||||
fmt::append(last_opcode, "%-*s %d,%d,0x%x ", PadOp(), op, b0, b1, DisAsmBranchTarget(pc));
|
||||
}
|
||||
void DisAsm_CR_BRANCH(const std::string& op, u32 cr, const int pc)
|
||||
void DisAsm_CR_BRANCH(std::string_view op, u32 cr, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, DisAsmBranchTarget(pc)));
|
||||
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, DisAsmBranchTarget(pc));
|
||||
}
|
||||
void DisAsm_CR_BRANCH_A(const std::string& op, u32 cr, const int pc)
|
||||
void DisAsm_CR_BRANCH_A(std::string_view op, u32 cr, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, pc));
|
||||
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, pc);
|
||||
}
|
||||
void DisAsm_BI_BRANCH(const std::string& op, u32 bi, const int pc)
|
||||
void DisAsm_BI_BRANCH(std::string_view op, u32 bi, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s cr%d[%s],0x%x ", FixOp(op), bi / 4, get_partial_BI_field(bi), DisAsmBranchTarget(pc)));
|
||||
fmt::append(last_opcode, "%-*s cr%d[%s],0x%x ", PadOp(), op, bi / 4, get_partial_BI_field(bi), DisAsmBranchTarget(pc));
|
||||
}
|
||||
void DisAsm_BI_BRANCH_A(const std::string& op, u32 bi, const int pc)
|
||||
void DisAsm_BI_BRANCH_A(std::string_view op, u32 bi, const int pc)
|
||||
{
|
||||
Write(fmt::format("%s cr%d[%s],0x%x ", FixOp(op), bi / 4, get_partial_BI_field(bi), pc));
|
||||
fmt::append(last_opcode, "%-*s cr%d[%s],0x%x ", PadOp(), op, bi / 4, get_partial_BI_field(bi), pc);
|
||||
}
|
||||
|
||||
public:
|
||||
u32 disasm(u32 pc) override;
|
||||
std::pair<const void*, usz> get_memory_span() const override;
|
||||
std::unique_ptr<CPUDisAsm> copy_type_erased() const override;
|
||||
|
||||
enum class const_op
|
||||
{
|
||||
none,
|
||||
form, // Cosntant formation
|
||||
xor_mask, // Constant XOR mask applied (used with CMPI/CMPLI instructions, covers their limit of 16-bit immediates)
|
||||
};
|
||||
|
||||
std::pair<const_op, u64> try_get_const_op_gpr_value(u32 reg, u32 pc = -1, u32 TTL = 10) const;
|
||||
|
||||
std::pair<bool, u64> try_get_const_gpr_value(u32 reg, u32 pc = -1) const
|
||||
{
|
||||
auto [op, res] = try_get_const_op_gpr_value(reg, pc);
|
||||
return {op == const_op::form, res};
|
||||
}
|
||||
|
||||
std::pair<bool, u64> try_get_const_xor_gpr_value(u32 reg, u32 pc = -1) const
|
||||
{
|
||||
auto [op, res] = try_get_const_op_gpr_value(reg, pc);
|
||||
return {op == const_op::xor_mask, res};
|
||||
}
|
||||
|
||||
void MFVSCR(ppu_opcode_t op);
|
||||
void MTVSCR(ppu_opcode_t op);
|
||||
|
||||
@@ -37,21 +37,23 @@ extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32);
|
||||
// HLE function name cache
|
||||
std::vector<std::string> g_ppu_function_names;
|
||||
|
||||
extern u32 ppu_generate_id(const char* name)
|
||||
extern u32 ppu_generate_id(std::string_view name)
|
||||
{
|
||||
// Symbol name suffix
|
||||
const auto suffix = "\x67\x59\x65\x99\x04\x25\x04\x90\x56\x64\x27\x49\x94\x89\x74\x1A";
|
||||
constexpr auto suffix = "\x67\x59\x65\x99\x04\x25\x04\x90\x56\x64\x27\x49\x94\x89\x74\x1A"sv;
|
||||
|
||||
sha1_context ctx;
|
||||
u8 output[20];
|
||||
|
||||
// Compute SHA-1 hash
|
||||
sha1_starts(&ctx);
|
||||
sha1_update(&ctx, reinterpret_cast<const u8*>(name), std::strlen(name));
|
||||
sha1_update(&ctx, reinterpret_cast<const u8*>(suffix), std::strlen(suffix));
|
||||
sha1_update(&ctx, reinterpret_cast<const u8*>(name.data()), name.size());
|
||||
sha1_update(&ctx, reinterpret_cast<const u8*>(suffix.data()), suffix.size());
|
||||
sha1_finish(&ctx, output);
|
||||
|
||||
return reinterpret_cast<le_t<u32>&>(output[0]);
|
||||
le_t<u32> result = 0;
|
||||
std::memcpy(&result, output, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
ppu_static_module::ppu_static_module(const char* name)
|
||||
@@ -331,14 +333,11 @@ static void ppu_initialize_modules(ppu_linkage_info* link)
|
||||
g_ppu_function_names[function.second.index] = fmt::format("%s:%s", function.second.name, _module->name);
|
||||
}
|
||||
|
||||
if ((function.second.flags & MFF_HIDDEN) == 0)
|
||||
{
|
||||
auto& flink = linkage.functions[function.first];
|
||||
auto& flink = linkage.functions[function.first];
|
||||
|
||||
flink.static_func = &function.second;
|
||||
flink.export_addr = g_fxo->get<ppu_function_manager>().func_addr(function.second.index);
|
||||
function.second.export_addr = &flink.export_addr;
|
||||
}
|
||||
flink.static_func = &function.second;
|
||||
flink.export_addr = g_fxo->get<ppu_function_manager>().func_addr(function.second.index);
|
||||
function.second.export_addr = &flink.export_addr;
|
||||
}
|
||||
|
||||
for (auto& variable : _module->variables)
|
||||
@@ -392,8 +391,21 @@ static void ppu_initialize_modules(ppu_linkage_info* link)
|
||||
// For the debugger (g_ppu_function_names shouldn't change, string_view should suffice)
|
||||
extern const std::unordered_map<u32, std::string_view>& get_exported_function_names_as_addr_indexed_map()
|
||||
{
|
||||
static std::unordered_map<u32, std::string_view> res;
|
||||
static u64 update_time = 0;
|
||||
struct info_t
|
||||
{
|
||||
std::unordered_map<u32, std::string_view> res;
|
||||
u64 update_time = 0;
|
||||
};
|
||||
|
||||
static thread_local std::unique_ptr<info_t> info;
|
||||
|
||||
if (!info)
|
||||
{
|
||||
info = std::make_unique<info_t>();
|
||||
info->res.reserve(ppu_module_manager::get().size());
|
||||
}
|
||||
|
||||
auto& [res, update_time] = *info;
|
||||
|
||||
const auto link = g_fxo->try_get<ppu_linkage_info>();
|
||||
const auto hle_funcs = g_fxo->try_get<ppu_function_manager>();
|
||||
@@ -415,7 +427,6 @@ extern const std::unordered_map<u32, std::string_view>& get_exported_function_na
|
||||
update_time = current_time;
|
||||
|
||||
res.clear();
|
||||
res.reserve(ppu_module_manager::get().size());
|
||||
|
||||
for (auto& pair : ppu_module_manager::get())
|
||||
{
|
||||
@@ -528,6 +539,13 @@ struct ppu_prx_module_info
|
||||
be_t<u32> unk5;
|
||||
};
|
||||
|
||||
bool ppu_form_branch_to_code(u32 entry, u32 target);
|
||||
|
||||
extern u32 ppu_get_exported_func_addr(u32 fnid, const std::string& module_name)
|
||||
{
|
||||
return g_fxo->get<ppu_linkage_info>().modules[module_name].functions[fnid].export_addr;
|
||||
}
|
||||
|
||||
// Load and register exports; return special exports found (nameless module)
|
||||
static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 exports_end)
|
||||
{
|
||||
@@ -606,26 +624,12 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
|
||||
if (_sf && (_sf->flags & MFF_FORCED_HLE))
|
||||
{
|
||||
// Inject a branch to the HLE implementation
|
||||
const u32 _entry = vm::read32(faddr);
|
||||
const u32 target = g_fxo->get<ppu_function_manager>().func_addr(_sf->index) + 4;
|
||||
|
||||
// Set exported function
|
||||
flink.export_addr = target - 4;
|
||||
|
||||
if ((target <= _entry && _entry - target <= 0x2000000) || (target > _entry && target - _entry < 0x2000000))
|
||||
{
|
||||
// Use relative branch
|
||||
vm::write32(_entry, ppu_instructions::B(target - _entry));
|
||||
}
|
||||
else if (target < 0x2000000)
|
||||
{
|
||||
// Use absolute branch if possible
|
||||
vm::write32(_entry, ppu_instructions::B(target, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
ppu_loader.fatal("Failed to patch function at 0x%x (0x%x)", _entry, target);
|
||||
}
|
||||
ppu_form_branch_to_code(faddr, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1267,9 +1271,25 @@ void ppu_unload_prx(const lv2_prx& prx)
|
||||
// }
|
||||
//}
|
||||
|
||||
// Format patch name
|
||||
std::string hash = fmt::format("PRX-%s", fmt::base57(prx.sha1));
|
||||
|
||||
for (auto& seg : prx.segs)
|
||||
{
|
||||
if (!seg.size) continue;
|
||||
|
||||
vm::dealloc(seg.addr, vm::main);
|
||||
|
||||
const std::string hash_seg = fmt::format("%s-%u", hash, &seg - prx.segs.data());
|
||||
|
||||
// Deallocatte memory used for patches
|
||||
g_fxo->get<patch_engine>().unload(hash_seg);
|
||||
|
||||
if (!Emu.GetTitleID().empty())
|
||||
{
|
||||
// Alternative patch
|
||||
g_fxo->get<patch_engine>().unload(Emu.GetTitleID() + '-' + hash_seg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ constexpr const char* ppu_select_name(const char* /*name*/, const char* orig_nam
|
||||
}
|
||||
|
||||
// Generate FNID or VNID for given name
|
||||
extern u32 ppu_generate_id(const char* name);
|
||||
extern u32 ppu_generate_id(std::string_view name);
|
||||
|
||||
// Overload for REG_FNID, REG_VNID macro
|
||||
constexpr u32 ppu_generate_id(u32 id)
|
||||
@@ -31,7 +31,7 @@ enum ppu_static_module_flags : u32
|
||||
{
|
||||
MFF_FORCED_HLE = (1 << 0), // Always call HLE function
|
||||
MFF_PERFECT = (1 << 1), // Indicates complete implementation and LLE interchangeability
|
||||
MFF_HIDDEN = (1 << 2), // Invisible function for internal use (TODO)
|
||||
MFF_HIDDEN = (1 << 2), // Invisible variable for internal use (TODO)
|
||||
};
|
||||
|
||||
// HLE function information
|
||||
@@ -293,7 +293,9 @@ inline RT ppu_execute(ppu_thread& ppu, Args... args)
|
||||
return func(ppu, args...);
|
||||
}
|
||||
|
||||
#define REG_FNID(_module, nid, func) ppu_module_manager::register_static_function<&func>(#_module, ppu_select_name(#func, nid), BIND_FUNC(func, ppu.cia = static_cast<u32>(ppu.lr) & ~3), ppu_generate_id(nid))
|
||||
#define BIND_FUNC_WITH_BLR(func) BIND_FUNC(func, ppu.cia = static_cast<u32>(ppu.lr) & ~3)
|
||||
|
||||
#define REG_FNID(_module, nid, func) ppu_module_manager::register_static_function<&func>(#_module, ppu_select_name(#func, nid), BIND_FUNC_WITH_BLR(func), ppu_generate_id(nid))
|
||||
|
||||
#define REG_FUNC(_module, func) REG_FNID(_module, #func, func)
|
||||
|
||||
@@ -301,6 +303,10 @@ inline RT ppu_execute(ppu_thread& ppu, Args... args)
|
||||
|
||||
#define REG_VAR(_module, var) REG_VNID(_module, #var, var)
|
||||
|
||||
#define REG_HIDDEN_FUNC(func) ppu_function_manager::register_function<decltype(&func), &func>(BIND_FUNC_WITH_BLR(func))
|
||||
|
||||
#define REG_HIDDEN_FUNC_PURE(func) ppu_function_manager::register_function<decltype(&func), &func>(func)
|
||||
|
||||
#define REINIT_FUNC(func) (ppu_module_manager::find_static_function<&func>().flags = 0, ppu_module_manager::find_static_function<&func>())
|
||||
|
||||
#define UNIMPLEMENTED_FUNC(_module) _module.todo("%s()", __func__)
|
||||
|
||||
+256
-20
@@ -319,6 +319,12 @@ void ppu_recompiler_fallback(ppu_thread& ppu)
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (uptr func = ppu_ref(ppu.cia); (func << 17 >> 17) != reinterpret_cast<uptr>(ppu_recompiler_fallback_ghc))
|
||||
{
|
||||
// We found a recompiler function at cia, return
|
||||
break;
|
||||
}
|
||||
|
||||
// Run instructions in interpreter
|
||||
if (const u32 op = vm::read32(ppu.cia); ctr++, table[ppu_decode(op)](ppu, {op})) [[likely]]
|
||||
{
|
||||
@@ -326,12 +332,6 @@ void ppu_recompiler_fallback(ppu_thread& ppu)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uptr func = ppu_ref(ppu.cia); (func << 17 >> 17) != reinterpret_cast<uptr>(ppu_recompiler_fallback_ghc))
|
||||
{
|
||||
// We found a recompiler function at cia, return
|
||||
break;
|
||||
}
|
||||
|
||||
if (ppu.test_stopped())
|
||||
{
|
||||
break;
|
||||
@@ -411,7 +411,7 @@ extern void ppu_register_range(u32 addr, u32 size)
|
||||
|
||||
// Register executable range at
|
||||
utils::memory_commit(&ppu_ref(addr), u64{size} * 2, utils::protection::rw);
|
||||
vm::page_protect(addr, size, 0, vm::page_executable);
|
||||
ensure(vm::page_protect(addr, size, 0, vm::page_executable));
|
||||
|
||||
if (g_cfg.core.ppu_debug)
|
||||
{
|
||||
@@ -438,7 +438,9 @@ extern void ppu_register_range(u32 addr, u32 size)
|
||||
}
|
||||
}
|
||||
|
||||
extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr)
|
||||
static bool ppu_far_jump(ppu_thread& ppu);
|
||||
|
||||
extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr = nullptr)
|
||||
{
|
||||
// Initialize specific function
|
||||
if (ptr)
|
||||
@@ -464,10 +466,11 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr)
|
||||
|
||||
// Initialize interpreter cache
|
||||
const u64 _break = reinterpret_cast<uptr>(ppu_break);
|
||||
const u64 far_jump = reinterpret_cast<uptr>(ppu_far_jump);
|
||||
|
||||
while (size)
|
||||
{
|
||||
if (ppu_ref(addr) != _break)
|
||||
if (ppu_ref(addr) != _break && ppu_ref(addr) != far_jump)
|
||||
{
|
||||
ppu_ref(addr) = ppu_cache(addr);
|
||||
}
|
||||
@@ -477,6 +480,226 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr)
|
||||
}
|
||||
}
|
||||
|
||||
extern void ppu_register_function_at(u32 addr, u32 size, u64 ptr)
|
||||
{
|
||||
return ppu_register_function_at(addr, size, reinterpret_cast<ppu_function_t>(ptr));
|
||||
}
|
||||
|
||||
u32 ppu_get_exported_func_addr(u32 fnid, const std::string& module_name);
|
||||
|
||||
bool ppu_return_from_far_jump(ppu_thread& ppu)
|
||||
{
|
||||
auto& calls_info = ppu.hle_func_calls_with_toc_info;
|
||||
ensure(!calls_info.empty());
|
||||
|
||||
// Branch to next instruction after far jump call entry with restored R2 and LR
|
||||
const auto restore_info = &calls_info.back();
|
||||
ppu.cia = restore_info->cia + 4;
|
||||
ppu.lr = restore_info->saved_lr;
|
||||
ppu.gpr[2] = restore_info->saved_r2;
|
||||
|
||||
calls_info.pop_back();
|
||||
return false;
|
||||
}
|
||||
|
||||
static const bool s_init_return_far_jump_func = []
|
||||
{
|
||||
REG_HIDDEN_FUNC_PURE(ppu_return_from_far_jump);
|
||||
return true;
|
||||
}();
|
||||
|
||||
struct ppu_far_jumps_t
|
||||
{
|
||||
struct all_info_t
|
||||
{
|
||||
u32 target;
|
||||
bool link;
|
||||
bool with_toc;
|
||||
std::string module_name;
|
||||
};
|
||||
|
||||
std::unordered_map<u32, all_info_t> vals;
|
||||
|
||||
mutable shared_mutex mutex;
|
||||
|
||||
// Get target address, 'ppu' is used in ppu_far_jump in order to modify registers
|
||||
u32 get_target(const u32 pc, ppu_thread* ppu = nullptr)
|
||||
{
|
||||
reader_lock lock(mutex);
|
||||
|
||||
if (auto it = vals.find(pc); it != vals.end())
|
||||
{
|
||||
all_info_t& all_info = it->second;
|
||||
u32 target = all_info.target;
|
||||
|
||||
bool link = all_info.link;
|
||||
bool from_opd = all_info.with_toc;
|
||||
|
||||
if (!all_info.module_name.empty())
|
||||
{
|
||||
target = ppu_get_exported_func_addr(target, all_info.module_name);
|
||||
}
|
||||
|
||||
if (from_opd && !vm::check_addr<sizeof(ppu_func_opd_t)>(target))
|
||||
{
|
||||
// Avoid reading unmapped memory under mutex
|
||||
from_opd = false;
|
||||
}
|
||||
|
||||
if (from_opd)
|
||||
{
|
||||
auto& opd = vm::_ref<ppu_func_opd_t>(target);
|
||||
target = opd.addr;
|
||||
|
||||
// We modify LR to custom values here
|
||||
link = false;
|
||||
|
||||
if (ppu)
|
||||
{
|
||||
auto& calls_info = ppu->hle_func_calls_with_toc_info;
|
||||
|
||||
// Save LR and R2
|
||||
// Set LR to the this ppu_return_from_far_jump branch for restoration of registers
|
||||
// NOTE: In order to clean up this information all calls must return in order
|
||||
auto& saved_info = calls_info.emplace_back();
|
||||
saved_info.cia = pc;
|
||||
saved_info.saved_lr = std::exchange(ppu->lr, FIND_FUNC(ppu_return_from_far_jump));
|
||||
saved_info.saved_r2 = std::exchange(ppu->gpr[2], opd.rtoc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (link && ppu)
|
||||
{
|
||||
ppu->lr = pc + 4;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
u32 ppu_get_far_jump(u32 pc)
|
||||
{
|
||||
g_fxo->init<ppu_far_jumps_t>();
|
||||
return g_fxo->get<ppu_far_jumps_t>().get_target(pc);
|
||||
}
|
||||
|
||||
static bool ppu_far_jump(ppu_thread& ppu)
|
||||
{
|
||||
const u32 cia = g_fxo->get<ppu_far_jumps_t>().get_target(ppu.cia, &ppu);
|
||||
|
||||
if (!vm::check_addr(cia, vm::page_executable))
|
||||
{
|
||||
fmt::throw_exception("PPU far jump failed! (returned cia = 0x%08x)", cia);
|
||||
}
|
||||
|
||||
ppu.cia = cia;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ppu_form_branch_to_code(u32 entry, u32 target, bool link, bool with_toc, std::string module_name)
|
||||
{
|
||||
// Force align entry and target
|
||||
entry &= -4;
|
||||
|
||||
// Exported functions are using target as FNID, must not be changed
|
||||
if (module_name.empty())
|
||||
{
|
||||
target &= -4;
|
||||
|
||||
u32 cia_target = target;
|
||||
|
||||
if (with_toc)
|
||||
{
|
||||
ppu_func_opd_t opd{};
|
||||
if (!vm::try_access(target, &opd, sizeof(opd), false))
|
||||
{
|
||||
// Cannot access function descriptor
|
||||
return false;
|
||||
}
|
||||
|
||||
// For now allow situations where OPD is changed later by patches or by the program itself
|
||||
//cia_target = opd.addr;
|
||||
|
||||
// So force a valid target (executable, yet not equal to entry)
|
||||
cia_target = entry ^ 8;
|
||||
}
|
||||
|
||||
// Target CIA must be aligned, executable and not equal with
|
||||
if (cia_target % 4 || entry == cia_target || !vm::check_addr(cia_target, vm::page_executable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Entry must be executable
|
||||
if (!vm::check_addr(entry, vm::page_executable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
g_fxo->init<ppu_far_jumps_t>();
|
||||
|
||||
if (!module_name.empty())
|
||||
{
|
||||
// Always use function descriptor for exported functions
|
||||
with_toc = true;
|
||||
}
|
||||
|
||||
if (with_toc)
|
||||
{
|
||||
// Always link for calls with function descriptor
|
||||
link = true;
|
||||
}
|
||||
|
||||
// Register branch target in host memory, not guest memory
|
||||
auto& jumps = g_fxo->get<ppu_far_jumps_t>();
|
||||
|
||||
std::lock_guard lock(jumps.mutex);
|
||||
jumps.vals.insert_or_assign(entry, std::type_identity_t<typename ppu_far_jumps_t::all_info_t>{target, link, with_toc, std::move(module_name)});
|
||||
ppu_register_function_at(entry, 4, &ppu_far_jump);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ppu_form_branch_to_code(u32 entry, u32 target, bool link, bool with_toc)
|
||||
{
|
||||
return ppu_form_branch_to_code(entry, target, link, with_toc, std::string{});
|
||||
}
|
||||
|
||||
bool ppu_form_branch_to_code(u32 entry, u32 target, bool link)
|
||||
{
|
||||
return ppu_form_branch_to_code(entry, target, link, false);
|
||||
}
|
||||
|
||||
bool ppu_form_branch_to_code(u32 entry, u32 target)
|
||||
{
|
||||
return ppu_form_branch_to_code(entry, target, false);
|
||||
}
|
||||
|
||||
void ppu_remove_hle_instructions(u32 addr, u32 size)
|
||||
{
|
||||
g_fxo->init<ppu_far_jumps_t>();
|
||||
|
||||
auto& jumps = g_fxo->get<ppu_far_jumps_t>();
|
||||
|
||||
std::lock_guard lock(jumps.mutex);
|
||||
|
||||
for (auto it = jumps.vals.begin(); it != jumps.vals.end();)
|
||||
{
|
||||
if (it->first >= addr && it->first <= addr + size - 1 && size)
|
||||
{
|
||||
it = jumps.vals.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
atomic_t<bool> g_debugger_pause_all_threads_on_bp = true;
|
||||
|
||||
// Breakpoint entry point
|
||||
@@ -608,6 +831,14 @@ std::array<u32, 2> op_branch_targets(u32 pc, ppu_opcode_t op)
|
||||
{
|
||||
std::array<u32, 2> res{pc + 4, umax};
|
||||
|
||||
g_fxo->need<ppu_far_jumps_t>();
|
||||
|
||||
if (u32 target = g_fxo->get<ppu_far_jumps_t>().get_target(pc))
|
||||
{
|
||||
res[0] = target;
|
||||
return res;
|
||||
}
|
||||
|
||||
switch (const auto type = g_ppu_itype.decode(op.opcode))
|
||||
{
|
||||
case ppu_itype::B:
|
||||
@@ -633,6 +864,8 @@ std::string ppu_thread::dump_regs() const
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr);
|
||||
|
||||
for (uint i = 0; i < 32; ++i)
|
||||
{
|
||||
auto reg = gpr[i];
|
||||
@@ -683,7 +916,6 @@ std::string ppu_thread::dump_regs() const
|
||||
}
|
||||
else
|
||||
{
|
||||
PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr);
|
||||
dis_asm.disasm(reg);
|
||||
fmt::append(ret, " -> %s", dis_asm.last_opcode);
|
||||
}
|
||||
@@ -1402,17 +1634,17 @@ void ppu_trap(ppu_thread& ppu, u64 addr)
|
||||
// If stubbing is enabled, check current instruction and the following
|
||||
if (!add || !vm::check_addr(ppu.cia, vm::page_executable) || !vm::check_addr(ppu.cia + add, vm::page_executable))
|
||||
{
|
||||
fmt::throw_exception("PPU Trap!");
|
||||
fmt::throw_exception("PPU Trap! Sometimes tweaking the setting \"Stub PPU Traps\" can be a workaround to this crash.\nBest values depend on game code, if unsure try 1.");
|
||||
}
|
||||
|
||||
ppu_log.error("PPU Trap: Stubbing %d instructions %s.", std::abs(static_cast<s32>(add) / 4), add >> 31 ? "backwards" : "forwards");
|
||||
ppu.cia += add; // Skip instructions, hope for valid code (interprter may be invoked temporarily)
|
||||
}
|
||||
|
||||
[[noreturn]] static void ppu_error(ppu_thread& ppu, u64 addr, u32 op)
|
||||
static void ppu_error(ppu_thread& ppu, u64 addr, u32 /*op*/)
|
||||
{
|
||||
ppu.cia = ::narrow<u32>(addr);
|
||||
fmt::throw_exception("Unknown/Illegal opcode 0x08x (0x%llx)", op, addr);
|
||||
ppu_recompiler_fallback(ppu);
|
||||
}
|
||||
|
||||
static void ppu_check(ppu_thread& ppu, u64 addr)
|
||||
@@ -2597,7 +2829,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<lv2_
|
||||
|
||||
if (!had_ovl)
|
||||
{
|
||||
ensure(vm::unmap(0x3000'0000));
|
||||
ensure(vm::unmap(0x3000'0000).second);
|
||||
}
|
||||
|
||||
g_ps3_process_info.ppc_seg = ppc_seg;
|
||||
@@ -2707,7 +2939,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
|
||||
{
|
||||
for (auto& block : func.blocks)
|
||||
{
|
||||
ppu_register_function_at(block.first, block.second, nullptr);
|
||||
ppu_register_function_at(block.first, block.second);
|
||||
}
|
||||
|
||||
if (g_cfg.core.ppu_debug && func.size && func.toc != umax)
|
||||
@@ -3164,9 +3396,11 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
|
||||
if (!func.size) continue;
|
||||
|
||||
const auto name = fmt::format("__0x%x", func.addr - reloc);
|
||||
const u64 addr = ensure(jit->get(name));
|
||||
jit_mod.funcs.emplace_back(reinterpret_cast<ppu_function_t>(addr));
|
||||
ppu_ref(func.addr) = (addr & 0x7fff'ffff'ffffu) | (ppu_ref(func.addr) & ~0x7fff'ffff'ffffu);
|
||||
const auto addr = ensure(reinterpret_cast<ppu_function_t>(jit->get(name)));
|
||||
jit_mod.funcs.emplace_back(addr);
|
||||
|
||||
if (ppu_ref(func.addr) != reinterpret_cast<u64>(ppu_far_jump))
|
||||
ppu_register_function_at(func.addr, 4, addr);
|
||||
|
||||
if (g_cfg.core.ppu_debug)
|
||||
ppu_log.notice("Installing function %s at 0x%x: %p (reloc = 0x%x)", name, func.addr, ppu_ref(func.addr), reloc);
|
||||
@@ -3183,8 +3417,10 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
|
||||
{
|
||||
if (!func.size) continue;
|
||||
|
||||
const u64 addr = ensure(reinterpret_cast<uptr>(jit_mod.funcs[index++]));
|
||||
ppu_ref(func.addr) = (addr & 0x7fff'ffff'ffffu) | (ppu_ref(func.addr) & ~0x7fff'ffff'ffffu);
|
||||
const u64 addr = reinterpret_cast<uptr>(ensure(jit_mod.funcs[index++]));
|
||||
|
||||
if (ppu_ref(func.addr) != reinterpret_cast<u64>(ppu_far_jump))
|
||||
ppu_register_function_at(func.addr, 4, addr);
|
||||
|
||||
if (g_cfg.core.ppu_debug)
|
||||
ppu_log.notice("Reinstalling function at 0x%x: %p (reloc=0x%x)", func.addr, ppu_ref(func.addr), reloc);
|
||||
|
||||
@@ -307,6 +307,15 @@ public:
|
||||
|
||||
static constexpr u32 call_history_max_size = 4096;
|
||||
|
||||
struct hle_func_call_with_toc_info_t
|
||||
{
|
||||
u32 cia;
|
||||
u64 saved_lr;
|
||||
u64 saved_r2;
|
||||
};
|
||||
|
||||
std::vector<hle_func_call_with_toc_info_t> hle_func_calls_with_toc_info;
|
||||
|
||||
// For named_thread ctor
|
||||
const struct thread_name_t
|
||||
{
|
||||
|
||||
@@ -126,6 +126,8 @@ Type* PPUTranslator::GetContextType()
|
||||
return m_thread_type;
|
||||
}
|
||||
|
||||
u32 ppu_get_far_jump(u32 pc);
|
||||
|
||||
Function* PPUTranslator::Translate(const ppu_function& info)
|
||||
{
|
||||
m_function = m_module->getFunction(info.name);
|
||||
@@ -232,7 +234,16 @@ Function* PPUTranslator::Translate(const ppu_function& info)
|
||||
m_rel = nullptr;
|
||||
}
|
||||
|
||||
if (ppu_get_far_jump(m_addr + base))
|
||||
{
|
||||
// Branch into an HLEd instruction using the jump table
|
||||
FlushRegisters();
|
||||
CallFunction(0, m_reloc ? m_ir->CreateAdd(m_ir->getInt64(m_addr), m_seg0) : m_ir->getInt64(m_addr));
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 op = vm::read32(vm::cast(m_addr + base));
|
||||
|
||||
(this->*(s_ppu_decoder.decode(op)))({op});
|
||||
|
||||
if (m_rel)
|
||||
@@ -251,6 +262,7 @@ Function* PPUTranslator::Translate(const ppu_function& info)
|
||||
}
|
||||
}
|
||||
|
||||
replace_intrinsics(*m_function);
|
||||
return m_function;
|
||||
}
|
||||
|
||||
@@ -995,7 +1007,7 @@ void PPUTranslator::VMADDFP(ppu_opcode_t op)
|
||||
auto [a, b, c] = get_vrs<f32[4]>(op.va, op.vb, op.vc);
|
||||
|
||||
// Optimization: Emit only a floating multiply if the addend is zero
|
||||
if (auto [ok, data] = get_const_vector(b.value, m_addr, 2000); ok)
|
||||
if (auto [ok, data] = get_const_vector(b.value, m_addr); ok)
|
||||
{
|
||||
if (data == v128::from32p(1u << 31))
|
||||
{
|
||||
@@ -1306,7 +1318,7 @@ void PPUTranslator::VNMSUBFP(ppu_opcode_t op)
|
||||
auto [a, b, c] = get_vrs<f32[4]>(op.va, op.vb, op.vc);
|
||||
|
||||
// Optimization: Emit only a floating multiply if the addend is zero
|
||||
if (const auto [ok, data] = get_const_vector(b.value, m_addr, 2004); ok)
|
||||
if (const auto [ok, data] = get_const_vector(b.value, m_addr); ok)
|
||||
{
|
||||
if (data == v128{})
|
||||
{
|
||||
@@ -1362,7 +1374,7 @@ void PPUTranslator::VPERM(ppu_opcode_t op)
|
||||
if (m_use_avx512_icl && op.ra != op.rb)
|
||||
{
|
||||
const auto i = eval(~c);
|
||||
set_vr(op.vd, vperm2b(b, a, i));
|
||||
set_vr(op.vd, vperm2b256to128(b, a, i));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1504,7 +1516,7 @@ void PPUTranslator::VSEL(ppu_opcode_t op)
|
||||
const auto c = get_vr<u32[4]>(op.vc);
|
||||
|
||||
// Check if the constant mask doesn't require bit granularity
|
||||
if (auto [ok, mask] = get_const_vector(c.value, m_addr, 9000); ok)
|
||||
if (auto [ok, mask] = get_const_vector(c.value, m_addr); ok)
|
||||
{
|
||||
bool sel_32 = true;
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
|
||||
@@ -12,30 +12,58 @@ const spu_decoder<spu_iflag> s_spu_iflag;
|
||||
|
||||
u32 SPUDisAsm::disasm(u32 pc)
|
||||
{
|
||||
last_opcode.clear();
|
||||
|
||||
if (pc < m_start_pc || pc >= SPU_LS_SIZE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
dump_pc = pc;
|
||||
be_t<u32> op;
|
||||
std::memcpy(&op, m_offset + pc, 4);
|
||||
m_op = op;
|
||||
(this->*(s_spu_disasm.decode(m_op)))({ m_op });
|
||||
|
||||
format_by_mode();
|
||||
return 4;
|
||||
}
|
||||
|
||||
std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
|
||||
std::pair<const void*, usz> SPUDisAsm::get_memory_span() const
|
||||
{
|
||||
if (m_mode != cpu_disasm_mode::interpreter)
|
||||
return {m_offset + m_start_pc, SPU_LS_SIZE - m_start_pc};
|
||||
}
|
||||
|
||||
std::unique_ptr<CPUDisAsm> SPUDisAsm::copy_type_erased() const
|
||||
{
|
||||
return std::make_unique<SPUDisAsm>(*this);
|
||||
}
|
||||
|
||||
std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc, u32 TTL) const
|
||||
{
|
||||
if (!TTL)
|
||||
{
|
||||
// Recursion limit (Time To Live)
|
||||
return {};
|
||||
}
|
||||
|
||||
if (pc == umax)
|
||||
{
|
||||
pc = dump_pc;
|
||||
// Default arg: choose pc of previous instruction
|
||||
|
||||
if (dump_pc == 0)
|
||||
{
|
||||
// Do not underflow
|
||||
return {};
|
||||
}
|
||||
|
||||
pc = dump_pc - 4;
|
||||
}
|
||||
|
||||
// Scan LS backwards from this instruction (until PC=0)
|
||||
// Search for the first register modification or branch instruction
|
||||
|
||||
for (s32 i = pc - 4; i >= 0; i -= 4)
|
||||
for (s32 i = static_cast<s32>(pc); i >= static_cast<s32>(m_start_pc); i -= 4)
|
||||
{
|
||||
const u32 opcode = *reinterpret_cast<const be_t<u32>*>(m_offset + i);
|
||||
const spu_opcode_t op0{ opcode };
|
||||
@@ -58,6 +86,21 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get constant register value
|
||||
#define GET_CONST_REG(var, reg) \
|
||||
{\
|
||||
/* Search for the constant value of the register*/\
|
||||
const auto [is_const, value] = try_get_const_value(reg, i - 4, TTL - 1);\
|
||||
\
|
||||
if (!is_const)\
|
||||
{\
|
||||
/* Cannot compute constant value if register is not constant*/\
|
||||
return {};\
|
||||
}\
|
||||
\
|
||||
var = value;\
|
||||
} void() /*<- Require a semicolon*/
|
||||
|
||||
//const auto flag = s_spu_iflag.decode(opcode);
|
||||
|
||||
// TODO: It detects spurious register modifications
|
||||
@@ -78,6 +121,10 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
|
||||
{
|
||||
return { true, v128::from32p(op0.i16 << 16) };
|
||||
}
|
||||
case spu_itype::ILH:
|
||||
{
|
||||
return { true, v128::from16p(op0.i16) };
|
||||
}
|
||||
case spu_itype::CBD:
|
||||
case spu_itype::CHD:
|
||||
case spu_itype::CWD:
|
||||
@@ -127,25 +174,13 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
|
||||
}
|
||||
case spu_itype::IOHL:
|
||||
{
|
||||
// Avoid multi-recursion for now
|
||||
if (dump_pc != pc)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
v128 reg_val{};
|
||||
|
||||
if (i >= 4)
|
||||
{
|
||||
// Search for ILHU+IOHL pattern (common pattern for 32-bit constants formation)
|
||||
// But don't limit to it
|
||||
const auto [is_const, value] = try_get_const_value(reg, i);
|
||||
// Search for ILHU+IOHL pattern (common pattern for 32-bit constants formation)
|
||||
// But don't limit to it
|
||||
GET_CONST_REG(reg_val, op0.rt);
|
||||
|
||||
if (is_const)
|
||||
{
|
||||
return { true, value | v128::from32p(op0.i16) };
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
return { true, reg_val | v128::from32p(op0.i16) };
|
||||
}
|
||||
case spu_itype::STQA:
|
||||
case spu_itype::STQD:
|
||||
@@ -156,6 +191,27 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
|
||||
// Do not modify RT
|
||||
break;
|
||||
}
|
||||
case spu_itype::SHLQBYI:
|
||||
{
|
||||
if (op0.si7)
|
||||
{
|
||||
// Unimplemented, doubt needed
|
||||
return {};
|
||||
}
|
||||
|
||||
// Move register value operation
|
||||
v128 reg_val{};
|
||||
GET_CONST_REG(reg_val, op0.ra);
|
||||
|
||||
return { true, reg_val };
|
||||
}
|
||||
case spu_itype::ORI:
|
||||
{
|
||||
v128 reg_val{};
|
||||
GET_CONST_REG(reg_val, op0.ra);
|
||||
|
||||
return { true, reg_val | v128::from32p(op0.si10) };
|
||||
}
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
@@ -236,6 +292,8 @@ SPUDisAsm::insert_mask_info SPUDisAsm::try_get_insert_mask_info(const v128& mask
|
||||
|
||||
void SPUDisAsm::WRCH(spu_opcode_t op)
|
||||
{
|
||||
DisAsm("wrch", spu_ch_name[op.ra], spu_reg_name[op.rt]);
|
||||
|
||||
const auto [is_const, value] = try_get_const_value(op.rt);
|
||||
|
||||
if (is_const)
|
||||
@@ -244,7 +302,7 @@ void SPUDisAsm::WRCH(spu_opcode_t op)
|
||||
{
|
||||
case MFC_Cmd:
|
||||
{
|
||||
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], MFC(value._u8[12])).c_str());
|
||||
fmt::append(last_opcode, " #%s", MFC(value._u8[12]));
|
||||
return;
|
||||
}
|
||||
case MFC_WrListStallAck:
|
||||
@@ -252,62 +310,60 @@ void SPUDisAsm::WRCH(spu_opcode_t op)
|
||||
{
|
||||
const u32 v = value._u32[3];
|
||||
if (v && !(v & (v - 1)))
|
||||
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s (tag=%u)", spu_reg_name[op.rt], SignedHex(v), std::countr_zero(v)).c_str()); // Single-tag mask
|
||||
fmt::append(last_opcode, " #%s (tag=%u)", SignedHex(v), std::countr_zero(v)); // Single-tag mask
|
||||
else
|
||||
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(v)).c_str()); // Multi-tag mask (or zero)
|
||||
fmt::append(last_opcode, " #%s", SignedHex(v)); // Multi-tag mask (or zero)
|
||||
return;
|
||||
}
|
||||
case MFC_EAH:
|
||||
{
|
||||
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u32[3])).c_str());
|
||||
fmt::append(last_opcode, " #%s", SignedHex(value._u32[3]));
|
||||
return;
|
||||
}
|
||||
case MFC_Size:
|
||||
{
|
||||
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u16[6])).c_str());
|
||||
fmt::append(last_opcode, " #%s", SignedHex(value._u16[6]));
|
||||
return;
|
||||
}
|
||||
case MFC_TagID:
|
||||
{
|
||||
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%u", spu_reg_name[op.rt], value._u8[12]).c_str());
|
||||
fmt::append(last_opcode, " #%u", value._u8[12]);
|
||||
return;
|
||||
}
|
||||
case MFC_WrTagUpdate:
|
||||
{
|
||||
const auto upd = fmt::format("%s", mfc_tag_update(value._u32[3]));
|
||||
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], upd == "empty" ? "IMMEDIATE" : upd).c_str());
|
||||
fmt::append(last_opcode, " #%s", upd == "empty" ? "IMMEDIATE" : upd);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u32[3])).c_str());
|
||||
fmt::append(last_opcode, " #%s", SignedHex(value._u32[3]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisAsm("wrch", spu_ch_name[op.ra], spu_reg_name[op.rt]);
|
||||
}
|
||||
|
||||
enum CellError : u32;
|
||||
|
||||
void SPUDisAsm::IOHL(spu_opcode_t op)
|
||||
{
|
||||
const auto [is_const, value] = try_get_const_value(op.rt);
|
||||
DisAsm("iohl", spu_reg_name[op.rt], op.i16);
|
||||
|
||||
const auto [is_const, value] = try_get_const_equal_value_array<u32>(op.rt);
|
||||
|
||||
// Only print constant for a 4 equal 32-bit constants array
|
||||
if (is_const)
|
||||
{
|
||||
// Only print constant for a 4 equal 32-bit constants array
|
||||
if (value == v128::from32p(value._u32[0]))
|
||||
{
|
||||
DisAsm("iohl", spu_reg_name[op.rt], fmt::format("%s #%s", SignedHex(+op.i16), (value._u32[0] | op.i16)).c_str());
|
||||
return;
|
||||
}
|
||||
comment_constant(last_opcode, value | op.i16);
|
||||
}
|
||||
|
||||
DisAsm("iohl", spu_reg_name[op.rt], op.i16);
|
||||
}
|
||||
|
||||
void SPUDisAsm::SHUFB(spu_opcode_t op)
|
||||
{
|
||||
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], spu_reg_name[op.rc]);
|
||||
|
||||
const auto [is_const, value] = try_get_const_value(op.rc);
|
||||
|
||||
if (is_const)
|
||||
@@ -319,15 +375,13 @@ void SPUDisAsm::SHUFB(spu_opcode_t op)
|
||||
if ((size >= 4u && !src) || (size == 2u && src == 1u) || (size == 1u && src == 3u))
|
||||
{
|
||||
// Comment insertion pattern for CWD-alike instruction
|
||||
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], fmt::format("%s #i%u[%u]", spu_reg_name[op.rc], size * 8, dst).c_str());
|
||||
fmt::append(last_opcode, " #i%u[%u]", size * 8, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
// Comment insertion pattern for unknown instruction formations
|
||||
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], fmt::format("%s #i%u[%u] = [%u]", spu_reg_name[op.rc], size * 8, dst, src).c_str());
|
||||
fmt::append(last_opcode, " #i%u[%u] = [%u]", size * 8, dst, src);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], spu_reg_name[op.rc]);
|
||||
}
|
||||
|
||||
+118
-45
@@ -2,8 +2,9 @@
|
||||
|
||||
#include "PPCDisAsm.h"
|
||||
#include "SPUOpcodes.h"
|
||||
#include "util/v128.hpp"
|
||||
|
||||
union v128;
|
||||
enum spu_stop_syscall : u32;
|
||||
|
||||
static constexpr const char* spu_reg_name[128] =
|
||||
{
|
||||
@@ -68,15 +69,26 @@ static constexpr const char* spu_ch_name[128] =
|
||||
"ch121", "ch122", "ch123", "ch124", "ch125", "ch126", "ch127",
|
||||
};
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class shm;
|
||||
}
|
||||
|
||||
void comment_constant(std::string& last_opocde, u64 value, bool print_float = true);
|
||||
|
||||
class SPUDisAsm final : public PPCDisAsm
|
||||
{
|
||||
std::shared_ptr<utils::shm> m_shm;
|
||||
public:
|
||||
SPUDisAsm(cpu_disasm_mode mode, const u8* offset) : PPCDisAsm(mode, offset)
|
||||
SPUDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0) : PPCDisAsm(mode, offset, start_pc)
|
||||
{
|
||||
}
|
||||
|
||||
~SPUDisAsm()
|
||||
~SPUDisAsm() = default;
|
||||
|
||||
void set_shm(std::shared_ptr<utils::shm> shm)
|
||||
{
|
||||
m_shm = std::move(shm);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -85,84 +97,125 @@ private:
|
||||
return spu_branch_target(dump_pc, imm);
|
||||
}
|
||||
|
||||
static const char* BrIndirectSuffix(u32 de)
|
||||
static char BrIndirectSuffix(u32 de)
|
||||
{
|
||||
switch (de)
|
||||
{
|
||||
case 0b01: return "e";
|
||||
case 0b10: return "d";
|
||||
//case 0b11: return "(undef)";
|
||||
default: return "";
|
||||
case 0b01: return 'e';
|
||||
case 0b10: return 'd';
|
||||
case 0b11: return '!';
|
||||
default: return '\0';
|
||||
}
|
||||
}
|
||||
|
||||
std::string& FixOp(std::string& op) const
|
||||
{
|
||||
if (m_mode != cpu_disasm_mode::normal)
|
||||
{
|
||||
op.append(std::max<int>(10 - ::narrow<int>(op.size()), 0), ' ');
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
void DisAsm(const char* op)
|
||||
{
|
||||
Write(op);
|
||||
last_opcode += op;
|
||||
}
|
||||
void DisAsm(std::string op, u32 a1)
|
||||
void DisAsm(std::string_view op, u32 a1)
|
||||
{
|
||||
Write(fmt::format("%s 0x%x", FixOp(op), a1));
|
||||
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, a1);
|
||||
}
|
||||
void DisAsm(std::string op, const char* a1)
|
||||
void DisAsm(std::string_view op, const char* a1)
|
||||
{
|
||||
Write(fmt::format("%s %s", FixOp(op), a1));
|
||||
fmt::append(last_opcode, "%-*s %s", PadOp(), op, a1);
|
||||
}
|
||||
void DisAsm(std::string op, const char* a1, const char* a2)
|
||||
void DisAsm(std::string_view op, const char* a1, const char* a2)
|
||||
{
|
||||
Write(fmt::format("%s %s,%s", FixOp(op), a1, a2));
|
||||
fmt::append(last_opcode, "%-*s %s,%s", PadOp(), op, a1, a2);
|
||||
}
|
||||
void DisAsm(std::string op, int a1, const char* a2)
|
||||
void DisAsm(std::string_view op, int a1, const char* a2)
|
||||
{
|
||||
Write(fmt::format("%s 0x%x,%s", FixOp(op), a1, a2));
|
||||
fmt::append(last_opcode, "%-*s 0x%x,%s", PadOp(), op, a1, a2);
|
||||
}
|
||||
void DisAsm(std::string op, const char* a1, int a2)
|
||||
void DisAsm(std::string_view op, const char* a1, int a2)
|
||||
{
|
||||
Write(fmt::format("%s %s,%s", FixOp(op), a1, SignedHex(a2)));
|
||||
fmt::append(last_opcode, "%-*s %s,%s", PadOp(), op, a1, SignedHex(a2));
|
||||
}
|
||||
void DisAsm(std::string op, int a1, int a2)
|
||||
void DisAsm(std::string_view op, int a1, int a2)
|
||||
{
|
||||
Write(fmt::format("%s 0x%x,0x%x", FixOp(op), a1, a2));
|
||||
fmt::append(last_opcode, "%-*s 0x%x,0x%x", PadOp(), op, a1, a2);
|
||||
}
|
||||
void DisAsm(std::string op, const char* a1, const char* a2, const char* a3)
|
||||
void DisAsm(std::string_view op, const char* a1, const char* a2, const char* a3)
|
||||
{
|
||||
Write(fmt::format("%s %s,%s,%s", FixOp(op), a1, a2, a3));
|
||||
fmt::append(last_opcode, "%-*s %s,%s,%s", PadOp(), op, a1, a2, a3);
|
||||
}
|
||||
void DisAsm(std::string op, const char* a1, int a2, const char* a3)
|
||||
void DisAsm(std::string_view op, const char* a1, int a2, const char* a3)
|
||||
{
|
||||
Write(fmt::format("%s %s,%s(%s)", FixOp(op), a1, SignedHex(a2), a3));
|
||||
fmt::append(last_opcode, "%-*s %s,%s(%s)", PadOp(), op, a1, SignedHex(a2), a3);
|
||||
}
|
||||
void DisAsm(std::string op, const char* a1, const char* a2, int a3)
|
||||
void DisAsm(std::string_view op, const char* a1, const char* a2, int a3)
|
||||
{
|
||||
Write(fmt::format("%s %s,%s,%s", FixOp(op), a1, a2, SignedHex(a3)));
|
||||
fmt::append(last_opcode, "%-*s %s,%s,%s", PadOp(), op, a1, a2, SignedHex(a3));
|
||||
}
|
||||
void DisAsm(std::string op, const char* a1, const char* a2, const char* a3, const char* a4)
|
||||
void DisAsm(std::string_view op, const char* a1, const char* a2, const char* a3, const char* a4)
|
||||
{
|
||||
Write(fmt::format("%s %s,%s,%s,%s", FixOp(op), a1, a2, a3, a4));
|
||||
fmt::append(last_opcode, "%-*s %s,%s,%s,%s", PadOp(), op, a1, a2, a3, a4);
|
||||
}
|
||||
|
||||
using field_de_t = decltype(spu_opcode_t::de);
|
||||
void DisAsm(std::string op, field_de_t de, const char* a1)
|
||||
void DisAsm(std::string_view op, field_de_t de, const char* a1)
|
||||
{
|
||||
Write(fmt::format("%s %s", FixOp(op.append(BrIndirectSuffix(de))), a1));
|
||||
const char c = BrIndirectSuffix(de);
|
||||
|
||||
if (c == '!')
|
||||
{
|
||||
// Invalid
|
||||
fmt::append(last_opcode, "?? ?? (%s)", op);
|
||||
return;
|
||||
}
|
||||
|
||||
fmt::append(last_opcode, "%-*s %s", PadOp(op, c ? 1 : 0), op, a1);
|
||||
insert_char_if(op, !!c, c);
|
||||
}
|
||||
void DisAsm(std::string op, field_de_t de, const char* a1, const char* a2)
|
||||
void DisAsm(std::string_view op, field_de_t de, const char* a1, const char* a2)
|
||||
{
|
||||
Write(fmt::format("%s %s,%s", FixOp(op.append(BrIndirectSuffix(de))), a1, a2));
|
||||
const char c = BrIndirectSuffix(de);
|
||||
|
||||
if (c == '!')
|
||||
{
|
||||
fmt::append(last_opcode, "?? ?? (%s)", op);
|
||||
return;
|
||||
}
|
||||
|
||||
fmt::append(last_opcode, "%-*s %s,%s", PadOp(op, c ? 1 : 0), op, a1, a2);
|
||||
insert_char_if(op, !!c, c);
|
||||
}
|
||||
|
||||
public:
|
||||
u32 disasm(u32 pc) override;
|
||||
std::pair<bool, v128> try_get_const_value(u32 reg, u32 pc = -1) const;
|
||||
std::pair<const void*, usz> get_memory_span() const override;
|
||||
std::unique_ptr<CPUDisAsm> copy_type_erased() const override;
|
||||
std::pair<bool, v128> try_get_const_value(u32 reg, u32 pc = -1, u32 TTL = 10) const;
|
||||
|
||||
// Get constant value if the original array is made of only repetitions of the same value
|
||||
template <typename T> requires (sizeof(T) < sizeof(v128) && !(sizeof(v128) % sizeof(T)))
|
||||
std::pair<bool, T> try_get_const_equal_value_array(u32 reg, u32 pc = -1, u32 TTL = 10) const
|
||||
{
|
||||
auto [ok, res] = try_get_const_value(reg, pc, TTL);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
v128 test_value{};
|
||||
|
||||
T sample{};
|
||||
std::memcpy(&sample, res._bytes, sizeof(T));
|
||||
|
||||
for (u32 i = 0; i < sizeof(v128); i += sizeof(T))
|
||||
{
|
||||
std::memcpy(test_value._bytes + i, &sample, sizeof(T));
|
||||
}
|
||||
|
||||
if (test_value != res)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return {ok, sample};
|
||||
}
|
||||
|
||||
struct insert_mask_info
|
||||
{
|
||||
@@ -176,7 +229,7 @@ public:
|
||||
//0 - 10
|
||||
void STOP(spu_opcode_t op)
|
||||
{
|
||||
op.rb ? UNK(op) : DisAsm("stop", op.opcode & 0x3fff);
|
||||
op.rb ? UNK(op) : DisAsm("stop", fmt::format("0x%x #%s", op.opcode & 0x3fff, spu_stop_syscall{op.opcode & 0x3fff}).c_str());
|
||||
}
|
||||
void LNOP(spu_opcode_t /*op*/)
|
||||
{
|
||||
@@ -819,10 +872,12 @@ public:
|
||||
void ILHU(spu_opcode_t op)
|
||||
{
|
||||
DisAsm("ilhu", spu_reg_name[op.rt], op.i16);
|
||||
comment_constant(last_opcode, op.i16 << 16);
|
||||
}
|
||||
void ILH(spu_opcode_t op)
|
||||
{
|
||||
DisAsm("ilh", spu_reg_name[op.rt], op.i16);
|
||||
comment_constant(last_opcode, (op.i16 << 16) | op.i16);
|
||||
}
|
||||
|
||||
void IOHL(spu_opcode_t op);
|
||||
@@ -838,6 +893,12 @@ public:
|
||||
}
|
||||
|
||||
DisAsm("ori", spu_reg_name[op.rt], spu_reg_name[op.ra], op.si10);
|
||||
|
||||
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(+op.ra); is_const)
|
||||
{
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value | static_cast<u32>(op.si10));
|
||||
}
|
||||
}
|
||||
void ORHI(spu_opcode_t op)
|
||||
{
|
||||
@@ -870,6 +931,12 @@ public:
|
||||
void AI(spu_opcode_t op)
|
||||
{
|
||||
DisAsm("ai", spu_reg_name[op.rt], spu_reg_name[op.ra], op.si10);
|
||||
|
||||
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(op.ra); is_const)
|
||||
{
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value + static_cast<u32>(op.si10));
|
||||
}
|
||||
}
|
||||
void AHI(spu_opcode_t op)
|
||||
{
|
||||
@@ -886,6 +953,12 @@ public:
|
||||
void XORI(spu_opcode_t op)
|
||||
{
|
||||
DisAsm("xori", spu_reg_name[op.rt], spu_reg_name[op.ra], op.si10);
|
||||
|
||||
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(op.ra); is_const)
|
||||
{
|
||||
// Comment constant formation
|
||||
comment_constant(last_opcode, value ^ static_cast<u32>(op.si10));
|
||||
}
|
||||
}
|
||||
void XORHI(spu_opcode_t op)
|
||||
{
|
||||
@@ -991,6 +1064,6 @@ public:
|
||||
|
||||
void UNK(spu_opcode_t /*op*/)
|
||||
{
|
||||
Write("?? ??");
|
||||
DisAsm("?? ??");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -116,13 +116,22 @@ void spu_interpreter::set_interrupt_status(spu_thread& spu, spu_opcode_t op)
|
||||
spu.set_interrupt_status(false);
|
||||
}
|
||||
|
||||
spu.check_mfc_interrupts(spu.pc);
|
||||
if (spu.check_mfc_interrupts(spu.pc) && spu.state & cpu_flag::pending)
|
||||
{
|
||||
spu.do_mfc();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool spu_interpreter::STOP(spu_thread& spu, spu_opcode_t op)
|
||||
{
|
||||
if (!spu.stop_and_signal(op.opcode & 0x3fff))
|
||||
const bool allow = std::exchange(spu.allow_interrupts_in_cpu_work, false);
|
||||
|
||||
const bool advance_pc = spu.stop_and_signal(op.opcode & 0x3fff);
|
||||
|
||||
spu.allow_interrupts_in_cpu_work = allow;
|
||||
|
||||
if (!advance_pc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -163,8 +172,12 @@ bool spu_interpreter::MFSPR(spu_thread& spu, spu_opcode_t op)
|
||||
|
||||
bool spu_interpreter::RDCH(spu_thread& spu, spu_opcode_t op)
|
||||
{
|
||||
const bool allow = std::exchange(spu.allow_interrupts_in_cpu_work, false);
|
||||
|
||||
const s64 result = spu.get_ch_value(op.ra);
|
||||
|
||||
spu.allow_interrupts_in_cpu_work = allow;
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
return false;
|
||||
@@ -425,7 +438,13 @@ bool spu_interpreter::MTSPR(spu_thread&, spu_opcode_t)
|
||||
|
||||
bool spu_interpreter::WRCH(spu_thread& spu, spu_opcode_t op)
|
||||
{
|
||||
if (!spu.set_ch_value(op.ra, spu.gpr[op.rt]._u32[3]))
|
||||
const bool allow = std::exchange(spu.allow_interrupts_in_cpu_work, false);
|
||||
|
||||
const bool advance_pc = spu.set_ch_value(op.ra, spu.gpr[op.rt]._u32[3]);
|
||||
|
||||
spu.allow_interrupts_in_cpu_work = allow;
|
||||
|
||||
if (!advance_pc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
+790
-357
File diff suppressed because it is too large
Load Diff
+215
-33
@@ -1216,6 +1216,8 @@ std::string spu_thread::dump_regs() const
|
||||
|
||||
const bool floats_only = debugger_float_mode.load();
|
||||
|
||||
SPUDisAsm dis_asm(cpu_disasm_mode::normal, ls);
|
||||
|
||||
for (u32 i = 0; i < 128; i++, ret += '\n')
|
||||
{
|
||||
fmt::append(ret, "%s: ", spu_reg_name[i]);
|
||||
@@ -1269,7 +1271,6 @@ std::string spu_thread::dump_regs() const
|
||||
|
||||
if (i3 >= 0x80 && is_exec_code(i3))
|
||||
{
|
||||
SPUDisAsm dis_asm(cpu_disasm_mode::normal, ls);
|
||||
dis_asm.disasm(i3);
|
||||
fmt::append(ret, " -> %s", dis_asm.last_opcode);
|
||||
}
|
||||
@@ -1642,6 +1643,8 @@ void spu_thread::cpu_task()
|
||||
{
|
||||
ensure(spu_runtime::g_interpreter);
|
||||
|
||||
allow_interrupts_in_cpu_work = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (state) [[unlikely]]
|
||||
@@ -1652,6 +1655,68 @@ void spu_thread::cpu_task()
|
||||
|
||||
spu_runtime::g_interpreter(*this, _ptr<u8>(0), nullptr);
|
||||
}
|
||||
|
||||
allow_interrupts_in_cpu_work = false;
|
||||
}
|
||||
}
|
||||
|
||||
void spu_thread::cpu_work()
|
||||
{
|
||||
if (std::exchange(in_cpu_work, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 old_iter_count = cpu_work_iteration_count++;
|
||||
|
||||
const auto timeout = +g_cfg.core.mfc_transfers_timeout;
|
||||
|
||||
bool work_left = false;
|
||||
|
||||
if (u32 shuffle_count = g_cfg.core.mfc_transfers_shuffling)
|
||||
{
|
||||
// If either MFC size exceeds limit or timeout has been reached execute pending MFC commands
|
||||
if (mfc_size > shuffle_count || (timeout && get_system_time() - mfc_last_timestamp >= timeout))
|
||||
{
|
||||
work_left = do_mfc(false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
work_left = mfc_size != 0; // TODO: Optimize
|
||||
}
|
||||
}
|
||||
|
||||
bool gen_interrupt = false;
|
||||
|
||||
// Check interrupts every 16 iterations
|
||||
if (!(old_iter_count % 16) && allow_interrupts_in_cpu_work)
|
||||
{
|
||||
if (u32 mask = ch_events.load().mask & SPU_EVENT_INTR_BUSY_CHECK)
|
||||
{
|
||||
// LR check is expensive, do it once in a while
|
||||
if (old_iter_count /*% 256*/)
|
||||
{
|
||||
mask &= ~SPU_EVENT_LR;
|
||||
}
|
||||
|
||||
get_events(mask);
|
||||
}
|
||||
|
||||
gen_interrupt = check_mfc_interrupts(pc);
|
||||
work_left |= interrupts_enabled;
|
||||
}
|
||||
|
||||
in_cpu_work = false;
|
||||
|
||||
if (!work_left)
|
||||
{
|
||||
state -= cpu_flag::pending;
|
||||
}
|
||||
|
||||
if (gen_interrupt)
|
||||
{
|
||||
// Interrupt! escape everything and restart execution
|
||||
spu_runtime::g_escape(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1710,12 +1775,12 @@ spu_thread::spu_thread(lv2_spu_group* group, u32 index, std::string_view name, u
|
||||
{
|
||||
if (!group)
|
||||
{
|
||||
ensure(vm::get(vm::spu)->falloc(vm_offset(), SPU_LS_SIZE, &shm, 0x200));
|
||||
ensure(vm::get(vm::spu)->falloc(vm_offset(), SPU_LS_SIZE, &shm, vm::page_size_64k));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 0x1000 indicates falloc to allocate page with no access rights in base memory
|
||||
ensure(vm::get(vm::spu)->falloc(vm_offset(), SPU_LS_SIZE, &shm, 0x1200));
|
||||
// alloc_hidden indicates falloc to allocate page with no access rights in base memory
|
||||
ensure(vm::get(vm::spu)->falloc(vm_offset(), SPU_LS_SIZE, &shm, vm::page_size_64k | vm::alloc_hidden));
|
||||
}
|
||||
|
||||
// Try to guess free area
|
||||
@@ -2948,14 +3013,15 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args)
|
||||
vm::reservation_notifier(addr).notify_all(-128);
|
||||
}
|
||||
|
||||
void spu_thread::do_mfc(bool /*wait*/)
|
||||
bool spu_thread::do_mfc(bool can_escape, bool must_finish)
|
||||
{
|
||||
u32 removed = 0;
|
||||
u32 barrier = 0;
|
||||
u32 fence = 0;
|
||||
u16 exec_mask = 0;
|
||||
bool pending = false;
|
||||
|
||||
// Process enqueued commands
|
||||
static_cast<void>(std::remove_if(mfc_queue + 0, mfc_queue + mfc_size, [&](spu_mfc_cmd& args)
|
||||
auto process_command = [&](spu_mfc_cmd& args)
|
||||
{
|
||||
// Select tag bit in the tag mask or the stall mask
|
||||
const u32 mask = utils::rol32(1, args.tag);
|
||||
@@ -2992,6 +3058,20 @@ void spu_thread::do_mfc(bool /*wait*/)
|
||||
return false;
|
||||
}
|
||||
|
||||
// If command is not enabled in execution mask, execute it later
|
||||
if (!(exec_mask & (1u << (&args - mfc_queue))))
|
||||
{
|
||||
if (args.cmd & MFC_BARRIER_MASK)
|
||||
{
|
||||
barrier |= mask;
|
||||
}
|
||||
|
||||
// Fence is set for any command
|
||||
fence |= mask;
|
||||
pending = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.cmd & MFC_LIST_MASK)
|
||||
{
|
||||
if (!(args.tag & 0x80))
|
||||
@@ -3028,32 +3108,68 @@ void spu_thread::do_mfc(bool /*wait*/)
|
||||
|
||||
removed++;
|
||||
return true;
|
||||
}));
|
||||
};
|
||||
|
||||
mfc_size -= removed;
|
||||
mfc_barrier = barrier;
|
||||
mfc_fence = fence;
|
||||
|
||||
if (removed && ch_tag_upd)
|
||||
auto get_exec_mask = [&size = mfc_size]
|
||||
{
|
||||
const u32 completed = get_mfc_completed();
|
||||
// Get commands' execution mask
|
||||
// Mask bits are always set when mfc_transfers_shuffling is 0
|
||||
return static_cast<u16>((0 - (1u << std::min<u32>(g_cfg.core.mfc_transfers_shuffling, size))) | __rdtsc());
|
||||
};
|
||||
|
||||
if (completed && ch_tag_upd == MFC_TAG_UPDATE_ANY)
|
||||
// Process enqueued commands
|
||||
while (true)
|
||||
{
|
||||
removed = 0;
|
||||
barrier = 0;
|
||||
fence = 0;
|
||||
|
||||
// Shuffle commands execution (if enabled), explicit barriers are obeyed
|
||||
pending = false;
|
||||
exec_mask = get_exec_mask();
|
||||
|
||||
static_cast<void>(std::remove_if(mfc_queue + 0, mfc_queue + mfc_size, process_command));
|
||||
|
||||
mfc_size -= removed;
|
||||
mfc_barrier = barrier;
|
||||
mfc_fence = fence;
|
||||
|
||||
if (removed && ch_tag_upd)
|
||||
{
|
||||
ch_tag_stat.set_value(completed);
|
||||
ch_tag_upd = MFC_TAG_UPDATE_IMMEDIATE;
|
||||
const u32 completed = get_mfc_completed();
|
||||
|
||||
if (completed && ch_tag_upd == MFC_TAG_UPDATE_ANY)
|
||||
{
|
||||
ch_tag_stat.set_value(completed);
|
||||
ch_tag_upd = MFC_TAG_UPDATE_IMMEDIATE;
|
||||
}
|
||||
else if (completed == ch_tag_mask && ch_tag_upd == MFC_TAG_UPDATE_ALL)
|
||||
{
|
||||
ch_tag_stat.set_value(completed);
|
||||
ch_tag_upd = MFC_TAG_UPDATE_IMMEDIATE;
|
||||
}
|
||||
}
|
||||
else if (completed == ch_tag_mask && ch_tag_upd == MFC_TAG_UPDATE_ALL)
|
||||
|
||||
if (can_escape && check_mfc_interrupts(pc + 4))
|
||||
{
|
||||
ch_tag_stat.set_value(completed);
|
||||
ch_tag_upd = MFC_TAG_UPDATE_IMMEDIATE;
|
||||
spu_runtime::g_escape(this);
|
||||
}
|
||||
|
||||
if (!pending)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!must_finish && g_cfg.core.mfc_shuffling_in_steps)
|
||||
{
|
||||
// Exit early, not all pending commands have to be executed at a single iteration
|
||||
// Update last timestamp so the next MFC timeout check will use the current time
|
||||
mfc_last_timestamp = get_system_time();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (check_mfc_interrupts(pc + 4))
|
||||
{
|
||||
spu_runtime::g_escape(this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool spu_thread::check_mfc_interrupts(u32 next_pc)
|
||||
@@ -3109,6 +3225,22 @@ bool spu_thread::process_mfc_cmd()
|
||||
// Stall infinitely if MFC queue is full
|
||||
while (mfc_size >= 16) [[unlikely]]
|
||||
{
|
||||
// Reset MFC timestamp in the case of full queue
|
||||
mfc_last_timestamp = 0;
|
||||
|
||||
if (test_stopped())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Process MFC commands
|
||||
do_mfc();
|
||||
|
||||
if (mfc_size < 16)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto old = state.add_fetch(cpu_flag::wait);
|
||||
|
||||
if (is_stopped(old))
|
||||
@@ -3205,7 +3337,7 @@ bool spu_thread::process_mfc_cmd()
|
||||
{
|
||||
state += cpu_flag::wait + cpu_flag::temp;
|
||||
std::this_thread::yield();
|
||||
!check_state();
|
||||
static_cast<void>(check_state());
|
||||
}
|
||||
}())
|
||||
{
|
||||
@@ -3382,12 +3514,18 @@ bool spu_thread::process_mfc_cmd()
|
||||
{
|
||||
if (do_dma_check(ch_mfc_cmd)) [[likely]]
|
||||
{
|
||||
if (ch_mfc_cmd.size)
|
||||
if (!g_cfg.core.mfc_transfers_shuffling)
|
||||
{
|
||||
do_dma_transfer(this, ch_mfc_cmd, ls);
|
||||
if (ch_mfc_cmd.size)
|
||||
{
|
||||
do_dma_transfer(this, ch_mfc_cmd, ls);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (!state.test_and_set(cpu_flag::pending))
|
||||
mfc_last_timestamp = get_system_time();
|
||||
}
|
||||
|
||||
mfc_queue[mfc_size++] = ch_mfc_cmd;
|
||||
@@ -3429,9 +3567,17 @@ bool spu_thread::process_mfc_cmd()
|
||||
|
||||
if (do_dma_check(cmd)) [[likely]]
|
||||
{
|
||||
if (!cmd.size || do_list_transfer(cmd)) [[likely]]
|
||||
if (!g_cfg.core.mfc_transfers_shuffling)
|
||||
{
|
||||
return true;
|
||||
if (!cmd.size || do_list_transfer(cmd)) [[likely]]
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!state.test_and_set(cpu_flag::pending))
|
||||
mfc_last_timestamp = get_system_time();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3445,6 +3591,7 @@ bool spu_thread::process_mfc_cmd()
|
||||
|
||||
if (check_mfc_interrupts(pc + 4))
|
||||
{
|
||||
do_mfc(false);
|
||||
spu_runtime::g_escape(this);
|
||||
}
|
||||
|
||||
@@ -3647,9 +3794,20 @@ void spu_thread::set_interrupt_status(bool enable)
|
||||
if (enable)
|
||||
{
|
||||
// Detect enabling interrupts with events masked
|
||||
if (auto mask = ch_events.load().mask; mask & ~SPU_EVENT_INTR_IMPLEMENTED)
|
||||
if (auto mask = ch_events.load().mask; mask & SPU_EVENT_INTR_BUSY_CHECK)
|
||||
{
|
||||
fmt::throw_exception("SPU Interrupts not implemented (mask=0x%x)", mask);
|
||||
if (g_cfg.core.spu_decoder != spu_decoder_type::precise && g_cfg.core.spu_decoder != spu_decoder_type::fast)
|
||||
{
|
||||
fmt::throw_exception("SPU Interrupts not implemented (mask=0x%x): Use interpreterts", mask);
|
||||
}
|
||||
|
||||
spu_log.trace("SPU Interrupts (mask=0x%x) are using CPU busy checking mode", mask);
|
||||
|
||||
// Process interrupts in cpu_work()
|
||||
if (state.none_of(cpu_flag::pending))
|
||||
{
|
||||
state += cpu_flag::pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3714,6 +3872,11 @@ s64 spu_thread::get_ch_value(u32 ch)
|
||||
state += cpu_flag::wait + cpu_flag::temp;
|
||||
}
|
||||
|
||||
if (state & cpu_flag::pending)
|
||||
{
|
||||
do_mfc();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10 && channel.get_count() == 0; i++)
|
||||
{
|
||||
busy_wait();
|
||||
@@ -3739,6 +3902,11 @@ s64 spu_thread::get_ch_value(u32 ch)
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (state & cpu_flag::pending)
|
||||
{
|
||||
do_mfc();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10 && ch_in_mbox.get_count() == 0; i++)
|
||||
{
|
||||
busy_wait();
|
||||
@@ -3770,13 +3938,17 @@ s64 spu_thread::get_ch_value(u32 ch)
|
||||
|
||||
case MFC_RdTagStat:
|
||||
{
|
||||
if (state & cpu_flag::pending)
|
||||
{
|
||||
do_mfc();
|
||||
}
|
||||
|
||||
if (u32 out; ch_tag_stat.try_read(out))
|
||||
{
|
||||
ch_tag_stat.set_value(0, false);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Will stall infinitely
|
||||
return read_channel(ch_tag_stat);
|
||||
}
|
||||
|
||||
@@ -3929,6 +4101,11 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
|
||||
{
|
||||
if (get_type() >= spu_type::raw)
|
||||
{
|
||||
if (state & cpu_flag::pending)
|
||||
{
|
||||
do_mfc();
|
||||
}
|
||||
|
||||
if (ch_out_intr_mbox.get_count())
|
||||
{
|
||||
state += cpu_flag::wait;
|
||||
@@ -4060,6 +4237,11 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
|
||||
|
||||
case SPU_WrOutMbox:
|
||||
{
|
||||
if (state & cpu_flag::pending)
|
||||
{
|
||||
do_mfc();
|
||||
}
|
||||
|
||||
if (ch_out_mbox.get_count())
|
||||
{
|
||||
state += cpu_flag::wait;
|
||||
|
||||
@@ -82,8 +82,7 @@ enum : u32
|
||||
|
||||
SPU_EVENT_IMPLEMENTED = SPU_EVENT_LR | SPU_EVENT_TM | SPU_EVENT_SN | SPU_EVENT_S1 | SPU_EVENT_S2, // Mask of implemented events
|
||||
SPU_EVENT_INTR_IMPLEMENTED = SPU_EVENT_SN,
|
||||
|
||||
SPU_EVENT_INTR_TEST = SPU_EVENT_INTR_IMPLEMENTED,
|
||||
SPU_EVENT_INTR_BUSY_CHECK = SPU_EVENT_IMPLEMENTED & ~SPU_EVENT_INTR_IMPLEMENTED,
|
||||
};
|
||||
|
||||
// SPU Class 0 Interrupts
|
||||
@@ -631,6 +630,7 @@ public:
|
||||
virtual std::string dump_misc() const override;
|
||||
virtual void cpu_task() override final;
|
||||
virtual void cpu_return() override;
|
||||
virtual void cpu_work() override;
|
||||
virtual ~spu_thread() override;
|
||||
void cleanup();
|
||||
void cpu_init();
|
||||
@@ -668,6 +668,9 @@ public:
|
||||
u32 mfc_barrier = -1;
|
||||
u32 mfc_fence = -1;
|
||||
|
||||
// Timestamp of the first postponed command (transfers shuffling related)
|
||||
u64 mfc_last_timestamp = 0;
|
||||
|
||||
// MFC proxy command data
|
||||
spu_mfc_cmd mfc_prxy_cmd;
|
||||
shared_mutex mfc_prxy_mtx;
|
||||
@@ -774,6 +777,10 @@ public:
|
||||
u64 mfc_dump_idx = 0;
|
||||
static constexpr u32 max_mfc_dump_idx = 2048;
|
||||
|
||||
bool in_cpu_work = false;
|
||||
bool allow_interrupts_in_cpu_work = false;
|
||||
u8 cpu_work_iteration_count = 0;
|
||||
|
||||
std::array<v128, 0x4000> stack_mirror; // Return address information
|
||||
|
||||
const char* current_func{}; // Current STOP or RDCH blocking function
|
||||
@@ -787,7 +794,7 @@ public:
|
||||
bool do_list_transfer(spu_mfc_cmd& args);
|
||||
void do_putlluc(const spu_mfc_cmd& args);
|
||||
bool do_putllc(const spu_mfc_cmd& args);
|
||||
void do_mfc(bool wait = true);
|
||||
bool do_mfc(bool can_escape = true, bool must_finish = true);
|
||||
u32 get_mfc_completed() const;
|
||||
|
||||
bool process_mfc_cmd();
|
||||
|
||||
@@ -936,9 +936,81 @@ const std::array<std::pair<ppu_function_t, std::string_view>, 1024> g_ppu_syscal
|
||||
#undef BIND_SYSC
|
||||
#undef NULL_FUNC
|
||||
|
||||
// TODO: more enums
|
||||
enum CellAdecError : u32;
|
||||
enum CellAtracError : u32;
|
||||
enum CellAtracMultiError : u32;
|
||||
enum CellAudioError : u32;
|
||||
enum CellAudioOutError : u32;
|
||||
enum CellAudioInError : u32;
|
||||
|
||||
enum CellVideoOutError : u32;
|
||||
|
||||
enum CellSpursCoreError : u32;
|
||||
enum CellSpursPolicyModuleError : u32;
|
||||
enum CellSpursTaskError : u32;
|
||||
enum CellSpursJobError : u32;
|
||||
|
||||
enum CellGameError : u32;
|
||||
enum CellGameDataError : u32;
|
||||
enum CellDiscGameError : u32;
|
||||
enum CellHddGameError : u32;
|
||||
|
||||
enum SceNpTrophyError : u32;
|
||||
enum SceNpError : u32;
|
||||
|
||||
template <u64 EnumMin, typename E>
|
||||
constexpr auto formatter_of = std::make_pair(EnumMin, &fmt_class_string<E>::format);
|
||||
|
||||
const std::map<u64, void(*)(std::string&, u64)> s_error_codes_formatting_by_type
|
||||
{
|
||||
formatter_of<0x80610000, CellAdecError>,
|
||||
formatter_of<0x80612100, CellAdecError>,
|
||||
formatter_of<0x80610300, CellAtracError>,
|
||||
formatter_of<0x80610b00, CellAtracMultiError>,
|
||||
formatter_of<0x80310700, CellAudioError>,
|
||||
formatter_of<0x8002b240, CellAudioOutError>,
|
||||
formatter_of<0x8002b260, CellAudioInError>,
|
||||
formatter_of<0x8002b220, CellVideoOutError>,
|
||||
|
||||
formatter_of<0x80410700, CellSpursCoreError>,
|
||||
formatter_of<0x80410800, CellSpursPolicyModuleError>,
|
||||
formatter_of<0x80410900, CellSpursTaskError>,
|
||||
formatter_of<0x80410A00, CellSpursJobError>,
|
||||
|
||||
formatter_of<0x8002cb00, CellGameError>,
|
||||
formatter_of<0x8002b600, CellGameDataError>,
|
||||
formatter_of<0x8002bd00, CellDiscGameError>,
|
||||
formatter_of<0x8002ba00, CellHddGameError>,
|
||||
|
||||
formatter_of<0x80022900, SceNpTrophyError>,
|
||||
formatter_of<0x80029500, SceNpError>,
|
||||
};
|
||||
|
||||
template<>
|
||||
void fmt_class_string<CellError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
// Test if can be formatted by this formatter
|
||||
const bool lv2_cell_error = (arg >> 8) == 0x800100u;
|
||||
|
||||
if (!lv2_cell_error)
|
||||
{
|
||||
// Format by external enum formatters
|
||||
auto upper = s_error_codes_formatting_by_type.upper_bound(arg);
|
||||
|
||||
if (upper == s_error_codes_formatting_by_type.begin())
|
||||
{
|
||||
// Format as unknown by another enum formatter
|
||||
upper->second(out, arg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the formatter whose base is the highest that is not more than arg
|
||||
const auto found = std::prev(upper);
|
||||
found->second(out, arg);
|
||||
return;
|
||||
}
|
||||
|
||||
format_enum(out, arg, [](auto error)
|
||||
{
|
||||
switch (error)
|
||||
|
||||
@@ -23,19 +23,27 @@ lv2_fs_mount_point g_mp_sys_dev_flash{"", 512, 8192, lv2_mp_flag::read_only + lv
|
||||
lv2_fs_mount_point g_mp_sys_dev_flash2{ "", 512, 8192, lv2_mp_flag::no_uid_gid }; // TODO confirm
|
||||
lv2_fs_mount_point g_mp_sys_dev_flash3{ "", 512, 8192, lv2_mp_flag::read_only + lv2_mp_flag::no_uid_gid }; // TODO confirm
|
||||
|
||||
template<>
|
||||
void fmt_class_string<lv2_file_type>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](lv2_file_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case lv2_file_type::regular: return "Regular file";
|
||||
case lv2_file_type::sdata: return "SDATA";
|
||||
case lv2_file_type::edata: return "EDATA";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<lv2_file>::format(std::string& out, u64 arg)
|
||||
{
|
||||
const auto& file = get_object(arg);
|
||||
|
||||
std::string_view type_s;
|
||||
switch (file.type)
|
||||
{
|
||||
case lv2_file_type::regular: type_s = "Regular file"; break;
|
||||
case lv2_file_type::sdata: type_s = "SDATA"; break;
|
||||
case lv2_file_type::edata: type_s = "EDATA"; break;
|
||||
}
|
||||
|
||||
auto get_size = [](u64 size) -> std::string
|
||||
{
|
||||
if (size == umax)
|
||||
@@ -61,7 +69,7 @@ void fmt_class_string<lv2_file>::format(std::string& out, u64 arg)
|
||||
const usz pos = file.file ? file.file.pos() : umax;
|
||||
const usz size = file.file ? file.file.size() : umax;
|
||||
|
||||
fmt::append(out, u8"%s, “%s”, Mode: 0x%x, Flags: 0x%x, Pos: %s, Size: %s", type_s, file.name.data(), file.mode, file.flags, get_size(pos), get_size(size));
|
||||
fmt::append(out, u8"%s, “%s”, Mode: 0x%x, Flags: 0x%x, Pos: %s, Size: %s", file.type, file.name.data(), file.mode, file.flags, get_size(pos), get_size(size));
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -637,12 +645,25 @@ error_code sys_fs_read(ppu_thread& ppu, u32 fd, vm::ptr<void> buf, u64 nbytes, v
|
||||
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file || file->flags & CELL_FS_O_WRONLY)
|
||||
if (!file || (nbytes && file->flags & CELL_FS_O_WRONLY))
|
||||
{
|
||||
nread.try_write(0); // nread writing is allowed to fail, error code is unchanged
|
||||
return CELL_EBADF;
|
||||
}
|
||||
|
||||
if (!nbytes)
|
||||
{
|
||||
// Whole function is skipped, only EBADF and EBUSY are checked
|
||||
if (file->lock == 1)
|
||||
{
|
||||
nread.try_write(0);
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
*nread = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
std::lock_guard lock(file->mp->mutex);
|
||||
|
||||
if (!file->file)
|
||||
@@ -681,12 +702,30 @@ error_code sys_fs_write(ppu_thread& ppu, u32 fd, vm::cptr<void> buf, u64 nbytes,
|
||||
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file || !(file->flags & CELL_FS_O_ACCMODE))
|
||||
if (!file || (nbytes && !(file->flags & CELL_FS_O_ACCMODE)))
|
||||
{
|
||||
nwrite.try_write(0); // nwrite writing is allowed to fail, error code is unchanged
|
||||
return CELL_EBADF;
|
||||
}
|
||||
|
||||
if (!nbytes)
|
||||
{
|
||||
// Whole function is skipped, only EBADF and EBUSY are checked
|
||||
if (file->lock == 1)
|
||||
{
|
||||
nwrite.try_write(0);
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
*nwrite = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (file->type != lv2_file_type::regular)
|
||||
{
|
||||
sys_fs.error("%s type: Writing %u bytes to FD=%d (path=%s)", file->type, nbytes, file->name.data());
|
||||
}
|
||||
|
||||
if (file->mp->flags & lv2_mp_flag::read_only)
|
||||
{
|
||||
nwrite.try_write(0);
|
||||
@@ -727,13 +766,11 @@ error_code sys_fs_close(ppu_thread& ppu, u32 fd)
|
||||
ppu.state += cpu_flag::wait;
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
sys_fs.trace("sys_fs_close(fd=%d)", fd);
|
||||
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
return CELL_EBADF;
|
||||
return {CELL_EBADF, fd};
|
||||
}
|
||||
|
||||
{
|
||||
@@ -741,7 +778,7 @@ error_code sys_fs_close(ppu_thread& ppu, u32 fd)
|
||||
|
||||
if (!file->file)
|
||||
{
|
||||
return CELL_EBADF;
|
||||
return {CELL_EBADF, fd};
|
||||
}
|
||||
|
||||
if (!(file->mp->flags & (lv2_mp_flag::read_only + lv2_mp_flag::cache)) && file->flags & CELL_FS_O_ACCMODE)
|
||||
@@ -772,12 +809,14 @@ error_code sys_fs_close(ppu_thread& ppu, u32 fd)
|
||||
|
||||
if (!ret || ret.ret == CELL_EBADF)
|
||||
{
|
||||
return CELL_EBADF;
|
||||
return {CELL_EBADF, fd};
|
||||
}
|
||||
|
||||
sys_fs.warning("sys_fs_close(fd=%u): path='%s'", fd, file->name.data());
|
||||
|
||||
if (file->lock == 1)
|
||||
{
|
||||
return CELL_EBUSY;
|
||||
return {CELL_EBUSY, fd};
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
@@ -1396,6 +1435,11 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
||||
return CELL_EROFS;
|
||||
}
|
||||
|
||||
if (op == 0x8000000b && file->type != lv2_file_type::regular && arg->size)
|
||||
{
|
||||
sys_fs.error("%s type: Writing %u bytes to FD=%d (path=%s)", file->type, arg->size, file->name.data());
|
||||
}
|
||||
|
||||
std::lock_guard lock(file->mp->mutex);
|
||||
|
||||
if (!file->file)
|
||||
@@ -1467,7 +1511,7 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_shared<lv2_file>(file, std::move(stream), file.mode, file.flags, file.real_path, lv2_file_type::sdata);
|
||||
return std::make_shared<lv2_file>(file, std::move(stream), file.mode, CELL_FS_O_RDONLY, file.real_path, lv2_file_type::sdata);
|
||||
}))
|
||||
{
|
||||
arg->out_code = CELL_OK;
|
||||
|
||||
@@ -12,7 +12,7 @@ error_code _sys_lwmutex_create(ppu_thread& ppu, vm::ptr<u32> lwmutex_id, u32 pro
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_lwmutex.warning(u8"_sys_lwmutex_create(lwmutex_id=*0x%x, protocol=0x%x, control=*0x%x, has_name=0x%x, name=0x%llx (“%s”))", lwmutex_id, protocol, control, has_name, name, lv2_obj::name64(std::bit_cast<be_t<u64>>(name)));
|
||||
sys_lwmutex.trace(u8"_sys_lwmutex_create(lwmutex_id=*0x%x, protocol=0x%x, control=*0x%x, has_name=0x%x, name=0x%llx (“%s”))", lwmutex_id, protocol, control, has_name, name, lv2_obj::name64(std::bit_cast<be_t<u64>>(name)));
|
||||
|
||||
if (protocol != SYS_SYNC_FIFO && protocol != SYS_SYNC_RETRY && protocol != SYS_SYNC_PRIORITY)
|
||||
{
|
||||
@@ -38,7 +38,7 @@ error_code _sys_lwmutex_destroy(ppu_thread& ppu, u32 lwmutex_id)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_lwmutex.warning("_sys_lwmutex_destroy(lwmutex_id=0x%x)", lwmutex_id);
|
||||
sys_lwmutex.trace("_sys_lwmutex_destroy(lwmutex_id=0x%x)", lwmutex_id);
|
||||
|
||||
std::shared_ptr<lv2_lwmutex> _mutex;
|
||||
|
||||
|
||||
@@ -462,24 +462,30 @@ error_code sys_mmapper_free_address(ppu_thread& ppu, u32 addr)
|
||||
auto& pf_events = g_fxo->get<page_fault_event_entries>();
|
||||
std::lock_guard pf_lock(pf_events.pf_mutex);
|
||||
|
||||
const auto mem = vm::get(vm::any, addr);
|
||||
|
||||
if (!mem || mem->addr != addr)
|
||||
{
|
||||
return {CELL_EINVAL, addr};
|
||||
}
|
||||
|
||||
for (const auto& ev : pf_events.events)
|
||||
{
|
||||
auto mem = vm::get(vm::any, addr);
|
||||
if (mem && addr <= ev.second && ev.second <= addr + mem->size - 1)
|
||||
if (addr <= ev.second && ev.second <= addr + mem->size - 1)
|
||||
{
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to unmap area
|
||||
const auto area = vm::unmap(addr, true);
|
||||
const auto [area, success] = vm::unmap(addr, true, &mem);
|
||||
|
||||
if (!area)
|
||||
{
|
||||
return {CELL_EINVAL, addr};
|
||||
}
|
||||
|
||||
if (area.use_count() != 1)
|
||||
if (!success)
|
||||
{
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
@@ -586,6 +592,12 @@ error_code sys_mmapper_map_shared_memory(ppu_thread& ppu, u32 addr, u32 mem_id,
|
||||
if (!area->falloc(addr, mem->size, &mem->shm, mem->align == 0x10000 ? SYS_MEMORY_PAGE_SIZE_64K : SYS_MEMORY_PAGE_SIZE_1M))
|
||||
{
|
||||
mem->counter--;
|
||||
|
||||
if (!area->is_valid())
|
||||
{
|
||||
return {CELL_EINVAL, addr};
|
||||
}
|
||||
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
@@ -634,6 +646,12 @@ error_code sys_mmapper_search_and_map(ppu_thread& ppu, u32 start_addr, u32 mem_i
|
||||
if (!addr)
|
||||
{
|
||||
mem->counter--;
|
||||
|
||||
if (!area->is_valid())
|
||||
{
|
||||
return {CELL_EINVAL, start_addr};
|
||||
}
|
||||
|
||||
return CELL_ENOMEM;
|
||||
}
|
||||
|
||||
|
||||
@@ -978,7 +978,7 @@ struct network_thread
|
||||
WSADATA wsa_data;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||
#endif
|
||||
if (g_cfg.net.psn_status == np_psn_status::rpcn)
|
||||
if (g_cfg.net.psn_status == np_psn_status::psn_rpcn)
|
||||
list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(3658), std::forward_as_tuple(3658));
|
||||
}
|
||||
|
||||
@@ -1603,6 +1603,13 @@ error_code sys_net_bnet_connect(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sockaddr
|
||||
std::memcpy(addr_buf.buf, addr.get_ptr(), 16);
|
||||
name.sin_port = std::bit_cast<u16>(psa_in->sin_port);
|
||||
name.sin_addr.s_addr = std::bit_cast<u32>(psa_in->sin_addr);
|
||||
#ifdef _WIN32
|
||||
// Windows doesn't support sending packets to 0.0.0.0 but it works on unixes, send to 127.0.0.1 instead
|
||||
if (name.sin_addr.s_addr == 0x00000000)
|
||||
{
|
||||
name.sin_addr.s_addr = 0x0100007F;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1672,7 +1679,7 @@ error_code sys_net_bnet_connect(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sockaddr
|
||||
sock.p2ps.op_vport = dst_vport;
|
||||
sock.p2ps.cur_seq = send_hdr.seq + 1;
|
||||
sock.p2ps.data_beg_seq = 0;
|
||||
sock.p2ps.data_available = 0;
|
||||
sock.p2ps.data_available = 0u;
|
||||
sock.p2ps.received_data.clear();
|
||||
sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_handshaking;
|
||||
|
||||
@@ -1896,7 +1903,7 @@ error_code sys_net_bnet_getsockname(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sock
|
||||
return -SYS_NET_EINVAL;
|
||||
}
|
||||
|
||||
::sockaddr_storage native_addr;
|
||||
::sockaddr_storage native_addr{};
|
||||
::socklen_t native_addrlen = sizeof(native_addr);
|
||||
|
||||
lv2_socket_type type;
|
||||
@@ -1909,6 +1916,12 @@ error_code sys_net_bnet_getsockname(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sock
|
||||
type = sock.type;
|
||||
p2p_vport = sock.p2p.vport;
|
||||
|
||||
// Unbound P2P socket special case
|
||||
if ((sock.type == SYS_NET_SOCK_DGRAM_P2P || sock.type == SYS_NET_SOCK_STREAM_P2P) && sock.socket == 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (::getsockname(sock.socket, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen) == 0)
|
||||
{
|
||||
ensure(native_addr.ss_family == AF_INET);
|
||||
@@ -1921,8 +1934,6 @@ error_code sys_net_bnet_getsockname(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sock
|
||||
// windows doesn't support getsockname for sockets that are not bound
|
||||
if (get_native_error() == WSAEINVAL)
|
||||
{
|
||||
memset(&native_addr, 0, native_addrlen);
|
||||
native_addr.ss_family = AF_INET;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -2320,8 +2331,8 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32
|
||||
{
|
||||
const auto get_data = [&](unsigned char *dest_buf)
|
||||
{
|
||||
const u32 to_give = std::min(sock.p2ps.data_available, len);
|
||||
sys_net.trace("STREAM-P2P socket had %d available, given %d", sock.p2ps.data_available, to_give);
|
||||
const u32 to_give = std::min<u32>(sock.p2ps.data_available, len);
|
||||
sys_net.trace("STREAM-P2P socket had %u available, given %u", sock.p2ps.data_available, to_give);
|
||||
|
||||
u32 left_to_give = to_give;
|
||||
while (left_to_give)
|
||||
@@ -2608,6 +2619,14 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
|
||||
name.sin_port = std::bit_cast<u16>(psa_in->sin_port);
|
||||
name.sin_addr.s_addr = std::bit_cast<u32>(psa_in->sin_addr);
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows doesn't support sending packets to 0.0.0.0 but it works on unixes, send to 127.0.0.1 instead
|
||||
if (name.sin_addr.s_addr == 0x00000000)
|
||||
{
|
||||
name.sin_addr.s_addr = 0x0100007F;
|
||||
}
|
||||
#endif
|
||||
|
||||
char ip_str[16];
|
||||
inet_ntop(AF_INET, &name.sin_addr, ip_str, sizeof(ip_str));
|
||||
sys_net.trace("Sending to %s:%d", ip_str, psa_in->sin_port);
|
||||
@@ -2652,7 +2671,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
|
||||
}
|
||||
else if (type == SYS_NET_SOCK_STREAM_P2P)
|
||||
{
|
||||
constexpr s64 max_data_len = (65535 - (sizeof(u16) + sizeof(lv2_socket::p2ps_i::encapsulated_tcp)));
|
||||
constexpr u32 max_data_len = (65535 - (sizeof(u16) + sizeof(lv2_socket::p2ps_i::encapsulated_tcp)));
|
||||
|
||||
// Prepare address
|
||||
name.sin_family = AF_INET;
|
||||
@@ -2664,10 +2683,10 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
|
||||
tcp_header.dst_port = sock.p2ps.op_vport;
|
||||
// chop it up
|
||||
std::vector<std::vector<u8>> stream_packets;
|
||||
s64 cur_total_len = len;
|
||||
u32 cur_total_len = len;
|
||||
while(cur_total_len > 0)
|
||||
{
|
||||
s64 cur_data_len;
|
||||
u32 cur_data_len;
|
||||
if (cur_total_len >= max_data_len)
|
||||
cur_data_len = max_data_len;
|
||||
else
|
||||
@@ -2676,7 +2695,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
|
||||
tcp_header.length = cur_data_len;
|
||||
tcp_header.seq = sock.p2ps.cur_seq;
|
||||
|
||||
auto packet = nt_p2p_port::generate_u2s_packet(tcp_header, &_buf[len - cur_total_len], cur_data_len);
|
||||
auto packet = nt_p2p_port::generate_u2s_packet(tcp_header, &_buf[len - cur_total_len], cur_data_len);
|
||||
nt_p2p_port::send_u2s_packet(sock, s, std::move(packet), &name, tcp_header.seq);
|
||||
|
||||
cur_total_len -= cur_data_len;
|
||||
@@ -3273,7 +3292,7 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
|
||||
{
|
||||
if ((fds[i].events & SYS_NET_POLLIN) && sock->p2ps.data_available)
|
||||
{
|
||||
sys_net.trace("[P2PS] p2ps has %d bytes available", sock->p2ps.data_available);
|
||||
sys_net.trace("[P2PS] p2ps has %u bytes available", sock->p2ps.data_available);
|
||||
fds_buf[i].revents |= SYS_NET_POLLIN;
|
||||
}
|
||||
|
||||
@@ -3444,7 +3463,15 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
|
||||
|
||||
if (exceptfds)
|
||||
{
|
||||
sys_net.error("sys_net_bnet_select(): exceptfds not implemented");
|
||||
struct log_t
|
||||
{
|
||||
atomic_t<bool> logged = false;
|
||||
};
|
||||
|
||||
if (!g_fxo->get<log_t>().logged.exchange(true))
|
||||
{
|
||||
sys_net.error("sys_net_bnet_select(): exceptfds not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
sys_net_fd_set rread{}, _readfds{};
|
||||
|
||||
@@ -414,10 +414,10 @@ struct lv2_socket final
|
||||
u32 op_addr = 0;
|
||||
|
||||
u64 data_beg_seq = 0; // Seq of first byte of received_data
|
||||
u32 data_available = 0; // Amount of continuous data available(calculated on ACK send)
|
||||
u64 data_available = 0; // Amount of continuous data available(calculated on ACK send)
|
||||
std::map<u64, std::vector<u8>> received_data; // holds seq/data of data received
|
||||
|
||||
u32 cur_seq = 0; // SEQ of next packet to be sent
|
||||
u64 cur_seq = 0; // SEQ of next packet to be sent
|
||||
} p2ps;
|
||||
|
||||
// Value keepers
|
||||
|
||||
@@ -411,7 +411,8 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
|
||||
if (disc.empty() && !Emu.GetTitleID().empty())
|
||||
disc = vfs::get(Emu.GetDir());
|
||||
|
||||
Emu.CallAfter([path = std::move(path), argv = std::move(argv), envp = std::move(envp), data = std::move(data), disc = std::move(disc), hdd1 = std::move(hdd1), klic = g_fxo->get<loaded_npdrm_keys>().devKlic.load()]() mutable
|
||||
Emu.CallAfter([path = std::move(path), argv = std::move(argv), envp = std::move(envp), data = std::move(data), disc = std::move(disc)
|
||||
, hdd1 = std::move(hdd1), klic = g_fxo->get<loaded_npdrm_keys>().devKlic.load(), old_config = Emu.GetUsedConfig()]() mutable
|
||||
{
|
||||
sys_process.success("Process finished -> %s", argv[0]);
|
||||
Emu.SetForceBoot(true);
|
||||
@@ -429,7 +430,7 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
|
||||
|
||||
Emu.SetForceBoot(true);
|
||||
|
||||
auto res = Emu.BootGame(path, "", true);
|
||||
auto res = Emu.BootGame(path, "", true, false, cfg_mode::continuous, old_config);
|
||||
|
||||
if (res != game_boot_result::no_errors)
|
||||
{
|
||||
|
||||
@@ -473,7 +473,13 @@ error_code _sys_prx_start_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys
|
||||
}
|
||||
|
||||
pOpt->entry.set(prx->start ? prx->start.addr() : ~0ull);
|
||||
pOpt->entry2.set(prx->prologue ? prx->prologue.addr() : ~0ull);
|
||||
|
||||
// This check is probably for older fw
|
||||
if (pOpt->size != 0x20u)
|
||||
{
|
||||
pOpt->entry2.set(prx->prologue ? prx->prologue.addr() : ~0ull);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -495,6 +501,14 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys_
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
auto set_entry2 = [&](u64 addr)
|
||||
{
|
||||
if (pOpt->size != 0x20u)
|
||||
{
|
||||
pOpt->entry2.set(addr);
|
||||
}
|
||||
};
|
||||
|
||||
switch (pOpt->cmd & 0xf)
|
||||
{
|
||||
case 1:
|
||||
@@ -511,7 +525,7 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys_
|
||||
}
|
||||
|
||||
pOpt->entry.set(prx->stop ? prx->stop.addr() : ~0ull);
|
||||
pOpt->entry2.set(prx->epilogue ? prx->epilogue.addr() : ~0ull);
|
||||
set_entry2(prx->epilogue ? prx->epilogue.addr() : ~0ull);
|
||||
return CELL_OK;
|
||||
}
|
||||
case 2:
|
||||
@@ -550,7 +564,7 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys_
|
||||
if (pOpt->cmd == 4u)
|
||||
{
|
||||
pOpt->entry.set(prx->stop ? prx->stop.addr() : ~0ull);
|
||||
pOpt->entry2.set(prx->epilogue ? prx->epilogue.addr() : ~0ull);
|
||||
set_entry2(prx->epilogue ? prx->epilogue.addr() : ~0ull);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -787,7 +801,17 @@ error_code _sys_prx_get_module_info(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
if (pOpt->size != pOpt.size() || !pOpt->info)
|
||||
if (pOpt->size != pOpt.size())
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
if (!pOpt->info)
|
||||
{
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
if (pOpt->info->size != pOpt->info.size())
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
@@ -843,8 +867,33 @@ error_code _sys_prx_get_module_id_by_address(ppu_thread& ppu, u32 addr)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_prx.todo("_sys_prx_get_module_id_by_address(addr=0x%x)", addr);
|
||||
return CELL_OK;
|
||||
sys_prx.warning("_sys_prx_get_module_id_by_address(addr=0x%x)", addr);
|
||||
|
||||
if (!vm::check_addr(addr))
|
||||
{
|
||||
// Fast check for an invalid argument
|
||||
return {CELL_PRX_ERROR_UNKNOWN_MODULE, addr};
|
||||
}
|
||||
|
||||
const auto [prx, id] = idm::select<lv2_obj, lv2_prx>([&](u32 id, lv2_prx& prx) -> u32
|
||||
{
|
||||
for (const ppu_segment& seg : prx.segs)
|
||||
{
|
||||
if (seg.size && addr >= seg.addr && addr < seg.addr + seg.size)
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (!id)
|
||||
{
|
||||
return {CELL_PRX_ERROR_UNKNOWN_MODULE, addr};
|
||||
}
|
||||
|
||||
return not_an_error(id);
|
||||
}
|
||||
|
||||
error_code _sys_prx_start(ppu_thread& ppu)
|
||||
|
||||
@@ -48,6 +48,25 @@ void fmt_class_string<spu_group_status>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<spu_stop_syscall>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](spu_stop_syscall value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case SYS_SPU_THREAD_STOP_YIELD: return "sys_spu_thread_yield";
|
||||
case SYS_SPU_THREAD_STOP_GROUP_EXIT: return "sys_spu_thread_group_exit";
|
||||
case SYS_SPU_THREAD_STOP_THREAD_EXIT: return "sys_spu_thread_thread_exit";
|
||||
case SYS_SPU_THREAD_STOP_RECEIVE_EVENT: return "sys_spu_thread_receive_event";
|
||||
case SYS_SPU_THREAD_STOP_TRY_RECEIVE_EVENT: return "sys_spu_thread_tryreceive_event";
|
||||
case SYS_SPU_THREAD_STOP_SWITCH_SYSTEM_MODULE: return "sys_spu_thread_switch_system_module";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
void sys_spu_image::load(const fs::file& stream)
|
||||
{
|
||||
const spu_exec_object obj{stream, 0, elf_opt::no_sections + elf_opt::no_data};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user