Compare commits
254 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef8afa7873 | |||
| df4fc947ec | |||
| eecfbf3fa6 | |||
| cfbe7669b4 | |||
| ccaeb87b01 | |||
| fc98f35ea7 | |||
| 998f867c29 | |||
| eb8325afcb | |||
| 7674c29c78 | |||
| 573734fab8 | |||
| 2cbd983b4c | |||
| 18983f5476 | |||
| 75ef154c12 | |||
| 414a354511 | |||
| 2168511e5b | |||
| f64d6bc687 | |||
| 3e83729cb2 | |||
| 83256cdacb | |||
| f748fe688c | |||
| 188f5c7c3c | |||
| 838346e4f7 | |||
| 12dc03a903 | |||
| e17c2f2397 | |||
| 071baf63b0 | |||
| 1795d5bcc9 | |||
| 27125c6f72 | |||
| 12694dcf69 | |||
| e7d8ef924f | |||
| 0f682b7da1 | |||
| 2b385f28f0 | |||
| ac8e914a25 | |||
| 3067c86d65 | |||
| 96afc15fa2 | |||
| 66f694bb39 | |||
| 06025cd19d | |||
| fc698a4df2 | |||
| 2cd47c0415 | |||
| b032f2dd87 | |||
| 3acd442c2e | |||
| 6b43e5c80e | |||
| 183380fe57 | |||
| 7abc5f3ead | |||
| 1867f9aa1f | |||
| f47c7e0ef1 | |||
| 36ab866c2c | |||
| f943deb2c3 | |||
| 27ea25a999 | |||
| 989b353fbd | |||
| c1607d14dc | |||
| 85884d14cd | |||
| 08cf275bc8 | |||
| 88ba3c47c2 | |||
| 4aee44b1c0 | |||
| e3d4c067df | |||
| dfc626c99c | |||
| 8786516c58 | |||
| 34e945128f | |||
| 7db6165b46 | |||
| 883f2e92ae | |||
| 3e8b003515 | |||
| ed66e9e9b9 | |||
| f87e11d16a | |||
| 5a64556fbf | |||
| d71f4b33d7 | |||
| c91bc03532 | |||
| 348fab33a8 | |||
| 68f3df4975 | |||
| f6f8996aa9 | |||
| 8533f962c6 | |||
| af33c0c69f | |||
| 50b1402d66 | |||
| cfd5c62386 | |||
| 569f72c360 | |||
| df74d49f72 | |||
| eac31dfd03 | |||
| 0b1a7ee346 | |||
| 77ba2b40e7 | |||
| 94239f5526 | |||
| 148edc005a | |||
| ec59f4d37e | |||
| 2c03878c3b | |||
| c8c845a659 | |||
| a54f1970f1 | |||
| 7f602937a3 | |||
| c2631c914d | |||
| c17014a055 | |||
| c572226b0d | |||
| 6718b0683e | |||
| 3735df4f53 | |||
| 4746ef09cd | |||
| ed93bbd16d | |||
| 041daab068 | |||
| e5c831a800 | |||
| b8ed3eb824 | |||
| 95f9f04612 | |||
| 823c72bf27 | |||
| 9b387485b5 | |||
| 56e27bdd49 | |||
| 30e8c3e951 | |||
| 96b7e4c67f | |||
| 65d93c97ea | |||
| a92b8acba7 | |||
| 85f4c38b4e | |||
| 40c51a1f05 | |||
| a6c2e995af | |||
| 596e671973 | |||
| d38b2eb8ef | |||
| bb9444e19a | |||
| 26d406fec3 | |||
| dda0b8af45 | |||
| d5ce4e0f6b | |||
| e5cfe710fa | |||
| 2a0e2ce0a2 | |||
| ef56433684 | |||
| 8bc3a39586 | |||
| e13a671f86 | |||
| 48f82c75c1 | |||
| a5955657c5 | |||
| 319657e815 | |||
| c589001dff | |||
| c04cd2228e | |||
| cba51b4a5e | |||
| 8962ddca01 | |||
| 1808a4373b | |||
| bb22afb7f1 | |||
| ce571cb6ca | |||
| ac9afbfede | |||
| 4e8130bd99 | |||
| 00c9321b1d | |||
| 8e8b582747 | |||
| 6f2e843193 | |||
| 7d9460a560 | |||
| 32f2b14429 | |||
| 651f8512eb | |||
| 3dac7d16e7 | |||
| 240f80b467 | |||
| 17b5a36b59 | |||
| ea326a5f29 | |||
| c271415328 | |||
| f28c7d029c | |||
| d7f317fa58 | |||
| 3f2c34edd2 | |||
| 566a8ac5ac | |||
| 71b5bc4fe6 | |||
| a427783bac | |||
| 23dc6a44f6 | |||
| 9ef5a01de4 | |||
| cdfe3ee7c8 | |||
| c3c73c7e24 | |||
| af8d308a69 | |||
| a7da245656 | |||
| e9713f9635 | |||
| 3dbde04d18 | |||
| 1611588682 | |||
| 93c72822a3 | |||
| 6f5bcf12c6 | |||
| 07dba02897 | |||
| db685879af | |||
| 94e8d6fe13 | |||
| 0d1fbfb755 | |||
| 10c52cf569 | |||
| 7661df0847 | |||
| e8ad6ec673 | |||
| 3ef48cbdd5 | |||
| 5fe36872c5 | |||
| d33955c290 | |||
| 606a60e901 | |||
| 93f5154429 | |||
| a6e046c6c3 | |||
| 8eae8cb86c | |||
| abe5013f1d | |||
| ca7de4bcb1 | |||
| 9a9b5860df | |||
| 0971636c07 | |||
| 50fcec63e0 | |||
| 8a87dc63b1 | |||
| efea3aa385 | |||
| 86bab5b7c3 | |||
| 675ed82c0b | |||
| 58dd0198dd | |||
| 16f833a516 | |||
| 0392830950 | |||
| 04efc6b4a4 | |||
| 8edd7d54cf | |||
| f19744ae88 | |||
| 01ee119c59 | |||
| 4cd60aca22 | |||
| 81ba791ee7 | |||
| c7a94a80df | |||
| c509226bab | |||
| 312b35051d | |||
| e401d85af7 | |||
| e4c9af176b | |||
| 9c354ee269 | |||
| 96d880839a | |||
| e23db7efbd | |||
| fd540f1820 | |||
| 7d55a85fa6 | |||
| fa7ef870dd | |||
| ecacb874e4 | |||
| 0b2e0a1b28 | |||
| a08d60fbbe | |||
| ef6041c14f | |||
| 25954758d7 | |||
| 0ae1578dce | |||
| 2039b85be3 | |||
| 8eae73dd02 | |||
| fe9a24a1b8 | |||
| 3d974eed03 | |||
| 329b58c8cd | |||
| 2d82e3ef9a | |||
| faaf644331 | |||
| d7aedb439e | |||
| 9c11840e40 | |||
| 4c36b80300 | |||
| f7fa5aab70 | |||
| 17aeefe1b0 | |||
| 825ae5b8aa | |||
| 3513f6d83c | |||
| 4ad4badcfe | |||
| b67837c0ef | |||
| b5f3f910f6 | |||
| b0a7448394 | |||
| bed7763a3b | |||
| 572c075a4b | |||
| bad7756af2 | |||
| 45c89030ac | |||
| 7aad4f4253 | |||
| 9e9385a877 | |||
| 431d69ac87 | |||
| 3be24f1e6f | |||
| c45deb3f87 | |||
| 213674511e | |||
| 6548e3f7d0 | |||
| a269ae7e30 | |||
| 862295d9f3 | |||
| 61e130e68c | |||
| 236e32fb3d | |||
| 067ea68979 | |||
| 7504503d5f | |||
| 108d0d49ec | |||
| 2c424a0911 | |||
| ffe1b287a8 | |||
| 485fca34b0 | |||
| 0b984e9a23 | |||
| 74e084c893 | |||
| 47ae192cf8 | |||
| 80b7fd5f3f | |||
| 0227b16d06 | |||
| 2a1af17f67 | |||
| b6e9746198 | |||
| bc141831f7 | |||
| acd3fd0c3e | |||
| 2369266b8a |
@@ -43,6 +43,7 @@ cmake .. \
|
||||
-DUSE_SYSTEM_CURL=ON \
|
||||
-DUSE_SDL=ON \
|
||||
-DUSE_SYSTEM_FFMPEG=OFF \
|
||||
-DUSE_DISCORD_RPC=ON \
|
||||
-DOpenGL_GL_PREFERENCE=LEGACY \
|
||||
-DLLVM_DIR=/opt/llvm/lib/cmake/llvm \
|
||||
-DSTATIC_LINK_LLVM=ON \
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@ arch -x86_64 /usr/local/bin/brew reinstall -f --build-from-source gnutls freetyp
|
||||
arch -x86_64 /usr/local/bin/brew install llvm@16 glew cmake sdl2 vulkan-headers coreutils
|
||||
arch -x86_64 /usr/local/bin/brew link -f llvm@16 ffmpeg@5
|
||||
|
||||
# moltenvk based on commit for 1.2.6 release
|
||||
wget https://raw.githubusercontent.com/Homebrew/homebrew-core/4ac0cfaca4c2505abe2fcbcc0ce5816572103a6c/Formula/m/molten-vk.rb
|
||||
# moltenvk based on commit for 1.2.7 release
|
||||
wget https://raw.githubusercontent.com/Homebrew/homebrew-core/05a8770c483cfddf5b077667a392d846bc2ad719/Formula/m/molten-vk.rb
|
||||
arch -x86_64 /usr/local/bin/brew install -f --overwrite ./molten-vk.rb
|
||||
#export MACOSX_DEPLOYMENT_TARGET=12.0
|
||||
export CXX=clang++
|
||||
|
||||
@@ -19,7 +19,6 @@ QT_DECL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtdeclarative${QT_SUFFIX}"
|
||||
QT_TOOL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttools${QT_SUFFIX}"
|
||||
QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia${QT_SUFFIX}"
|
||||
QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}"
|
||||
QT_5CMP_URL="${QT_HOST}${QT_PREFIX}qt5compat.${QT_PREFIX_2}qt5compat${QT_SUFFIX}"
|
||||
LLVMLIBS_URL='https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z'
|
||||
GLSLANG_URL='https://github.com/RPCS3/glslang/releases/latest/download/glslanglibs_mt.7z'
|
||||
VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-1.3.268.0-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1"
|
||||
@@ -30,7 +29,6 @@ DEP_URLS=" \
|
||||
$QT_TOOL_URL \
|
||||
$QT_MM_URL \
|
||||
$QT_SVG_URL \
|
||||
$QT_5CMP_URL \
|
||||
$LLVMLIBS_URL \
|
||||
$GLSLANG_URL \
|
||||
$VULKAN_SDK_URL"
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@ env:
|
||||
BUILD_SOURCEBRANCHNAME: $CIRRUS_BRANCH
|
||||
RPCS3_TOKEN: ENCRYPTED[!a4c3850e29ab150692286a74bec29819d25971a7ec431b86de2a35f7ed90c5b2ab3c93469f9298e30924d843599110e9!]
|
||||
QT_VER_MAIN: '6'
|
||||
QT_VER: '6.6.1'
|
||||
QT_VER: '6.6.2'
|
||||
|
||||
# windows_task:
|
||||
# matrix:
|
||||
@@ -21,7 +21,7 @@ env:
|
||||
# COMPILER: msvc
|
||||
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\
|
||||
# QT_VER_MSVC: 'msvc2019'
|
||||
# QT_DATE: '202311210527'
|
||||
# QT_DATE: '202402121135'
|
||||
# QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64
|
||||
# VULKAN_VER: '1.3.268.0'
|
||||
# VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
@@ -56,7 +56,7 @@ env:
|
||||
|
||||
# linux_task:
|
||||
# container:
|
||||
# image: rpcs3/rpcs3-ci-focal:1.5
|
||||
# image: rpcs3/rpcs3-ci-focal:1.6
|
||||
# cpu: 4
|
||||
# memory: 16G
|
||||
# env:
|
||||
|
||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
||||
# 7z sdk
|
||||
if(WIN32)
|
||||
if(WIN32 OR APPLE)
|
||||
add_library(3rdparty_7z STATIC EXCLUDE_FROM_ALL
|
||||
src/7zAlloc.c
|
||||
src/7zArcIn.c
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/FAudio updated: 758c90af12...38e9da7264
Vendored
+1
-1
@@ -4,7 +4,7 @@ include(ExternalProject)
|
||||
|
||||
ExternalProject_Add(moltenvk
|
||||
GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git
|
||||
GIT_TAG 9e4ee9e
|
||||
GIT_TAG 66f6ff1
|
||||
BUILD_IN_SOURCE 1
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK
|
||||
CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos
|
||||
|
||||
-1276
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
Vendored
+1
-1
Submodule 3rdparty/curl/curl updated: 7161cb17c0...5ce164e0e9
Vendored
+2
@@ -1,6 +1,8 @@
|
||||
# DiscordRPC
|
||||
add_library(3rdparty_discordRPC INTERFACE)
|
||||
|
||||
check_cxx_compiler_flag("-msse -msse2 -mcx16" COMPILER_X86)
|
||||
|
||||
# We don't want Discord Rich Presence on the BSDs and other OSes
|
||||
if (USE_DISCORD_RPC AND (WIN32 OR CMAKE_SYSTEM MATCHES "Linux" OR APPLE) AND COMPILER_X86)
|
||||
if (WIN32 AND NOT MSVC)
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/flatbuffers updated: 06c5c7ed0b...0100f6a577
Vendored
-2
@@ -4,11 +4,9 @@ if (NOT USE_SYSTEM_LIBPNG)
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
set(PNG_SHARED OFF CACHE BOOL "Build shared lib")
|
||||
set(PNG_TESTS OFF CACHE BOOL "Build libpng tests")
|
||||
set(PNG_BUILD_ZLIB ON CACHE BOOL "ZLIB is already build or package is found")
|
||||
set(SKIP_INSTALL_ALL ON)
|
||||
add_subdirectory(libpng EXCLUDE_FROM_ALL)
|
||||
target_include_directories(png_static INTERFACE "${libpng_BINARY_DIR}" "${libpng_SOURCE_DIR}")
|
||||
target_link_libraries(png_static 3rdparty::zlib)
|
||||
|
||||
set(LIBPNG_TARGET png_static PARENT_SCOPE)
|
||||
else()
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/libpng/libpng updated: f135775ad4...35d9f5ea52
Vendored
+1
-1
Submodule 3rdparty/libsdl-org/SDL updated: 15ead9a40d...859844eae3
Vendored
+21
-1
@@ -11,6 +11,12 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_steamdeck.c" />
|
||||
<ClCompile Include="SDL\src\joystick\SDL_steam_virtual_gamepad.c" />
|
||||
<ClCompile Include="SDL\src\joystick\steam\SDL_steamcontroller.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_std.c" />
|
||||
<ClInclude Include="SDL\include\begin_code.h" />
|
||||
<ClInclude Include="SDL\include\close_code.h" />
|
||||
<ClInclude Include="SDL\include\SDL.h" />
|
||||
@@ -133,7 +139,9 @@
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_rumble.h" />
|
||||
<ClInclude Include="SDL\src\joystick\SDL_gamecontrollerdb.h" />
|
||||
<ClInclude Include="SDL\src\joystick\SDL_joystick_c.h" />
|
||||
<ClInclude Include="SDL\src\joystick\SDL_steam_virtual_gamepad.h" />
|
||||
<ClInclude Include="SDL\src\joystick\SDL_sysjoystick.h" />
|
||||
<ClInclude Include="SDL\src\joystick\steam\SDL_steamcontroller.h" />
|
||||
<ClInclude Include="SDL\src\joystick\usb_ids.h" />
|
||||
<ClInclude Include="SDL\src\joystick\virtual\SDL_virtualjoystick_c.h" />
|
||||
<ClInclude Include="SDL\src\joystick\windows\SDL_dinputjoystick_c.h" />
|
||||
@@ -188,6 +196,13 @@
|
||||
<ClInclude Include="SDL\src\video\dummy\SDL_nullevents_c.h" />
|
||||
<ClInclude Include="SDL\src\video\dummy\SDL_nullframebuffer_c.h" />
|
||||
<ClInclude Include="SDL\src\video\dummy\SDL_nullvideo.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codecs_common.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std_decode.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std_encode.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std_decode.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std_encode.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vulkan\vk_icd.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vulkan\vk_layer.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vulkan\vk_platform.h" />
|
||||
@@ -238,7 +253,13 @@
|
||||
<ClInclude Include="SDL\src\video\windows\SDL_windowswindow.h" />
|
||||
<ClInclude Include="SDL\src\video\windows\wmmsg.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_common.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_internal.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx_func.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_sse_func.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std_func.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -418,7 +439,6 @@
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsvideo.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsvulkan.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowswindow.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb.c" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA}</ProjectGuid>
|
||||
|
||||
+21
-1
@@ -178,10 +178,15 @@
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsvideo.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowsvulkan.c" />
|
||||
<ClCompile Include="SDL\src\video\windows\SDL_windowswindow.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_ps3.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_steam.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_wii.c" />
|
||||
<ClCompile Include="SDL\src\joystick\steam\SDL_steamcontroller.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.c" />
|
||||
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_std.c" />
|
||||
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_steamdeck.c" />
|
||||
<ClCompile Include="SDL\src\joystick\SDL_steam_virtual_gamepad.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="SDL\include\begin_code.h" />
|
||||
@@ -409,5 +414,20 @@
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_sse_func.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std_func.h" />
|
||||
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_nintendo.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_common.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_internal.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx_func.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.h" />
|
||||
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std.h" />
|
||||
<ClInclude Include="SDL\src\joystick\steam\SDL_steamcontroller.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std_decode.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std_encode.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std_decode.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std_encode.h" />
|
||||
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codecs_common.h" />
|
||||
<ClInclude Include="SDL\src\joystick\SDL_steam_virtual_gamepad.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Vendored
+1
-1
Submodule 3rdparty/libusb/libusb updated: 4239bc3a50...d52e355daa
Vendored
+1
-1
Submodule 3rdparty/miniupnp/miniupnp updated: e439318cf7...f22a96b469
Vendored
-2
@@ -27,10 +27,8 @@
|
||||
#include <sys/un.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <poll.h>
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr SOCKET invalid_socket = INVALID_SOCKET;
|
||||
|
||||
Vendored
+4
-3
@@ -1,10 +1,9 @@
|
||||
if (USE_SYSTEM_ZLIB)
|
||||
message(STATUS "RPCS3: Using system ZLIB")
|
||||
find_package(ZLIB QUIET)
|
||||
find_package(ZLIB QUIET REQUIRED)
|
||||
|
||||
add_library(3rdparty_zlib INTERFACE)
|
||||
target_link_libraries(3rdparty_zlib INTERFACE ${ZLIB_LIBRARIES})
|
||||
target_include_directories(3rdparty_zlib INTERFACE ${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_zlib INTERFACE ZLIB::ZLIB)
|
||||
target_compile_definitions(3rdparty_zlib INTERFACE -DZLIB_CONST=1)
|
||||
else()
|
||||
message(STATUS "RPCS3: Using builtin ZLIB")
|
||||
@@ -15,4 +14,6 @@ else()
|
||||
target_link_libraries(3rdparty_zlib INTERFACE zlibstatic)
|
||||
target_include_directories(3rdparty_zlib INTERFACE zlib ${CMAKE_CURRENT_BINARY_DIR}/zlib)
|
||||
target_compile_definitions(3rdparty_zlib INTERFACE -DZLIB_CONST=1)
|
||||
|
||||
set(ZLIB_ROOT ${CMAKE_CURRENT_BINARY_DIR}/zlib PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/zlib/zlib updated: 09155eaa2f...51b7f2abda
+6
-6
@@ -7,21 +7,21 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu
|
||||
|
||||
### Windows 7 or later
|
||||
|
||||
* [CMake 3.16.9+](https://www.cmake.org/download/) (add to PATH)
|
||||
* [CMake 3.28.0+](https://www.cmake.org/download/) (add to PATH)
|
||||
* [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
|
||||
* [Qt 6.6.1](https://www.qt.io/download-qt-installer)
|
||||
* [Qt 6.6.2](https://www.qt.io/download-qt-installer)
|
||||
* [Visual Studio 2022](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) (or at least Visual Studio 2019 16.11.xx+ as C++20 is not included in previous versions)
|
||||
* [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
|
||||
|
||||
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\6.6.1\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)**
|
||||
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\6.6.2\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)**
|
||||
|
||||
### Linux
|
||||
|
||||
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager.
|
||||
|
||||
* Clang 12+ or GCC 11+
|
||||
* [CMake 3.16.9+](https://www.cmake.org/download/)
|
||||
* [Qt 6.6.1](https://www.qt.io/download-qt-installer)
|
||||
* [CMake 3.28.0+](https://www.cmake.org/download/)
|
||||
* [Qt 6.6.2](https://www.qt.io/download-qt-installer)
|
||||
* [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
|
||||
* [SDL2](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
|
||||
|
||||
@@ -96,7 +96,7 @@ git submodule update --init
|
||||
#### Configuring the Qt plugin (if used)
|
||||
|
||||
1) Go to `Extensions->Qt VS Tools->Qt Versions`.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.6.1\msvc2019_64`, version will fill in automatically.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.6.2\msvc2019_64`, version will fill in automatically.
|
||||
3) Go to `Extensions->Qt VS Tools->Options->Legacy Project Format`.
|
||||
4) Set `Build: Run pre-build setup` to `true`.
|
||||
|
||||
|
||||
+186
-36
@@ -1,12 +1,14 @@
|
||||
#include "File.h"
|
||||
#include "mutex.h"
|
||||
#include "StrFmt.h"
|
||||
#include "StrUtil.h"
|
||||
#include "Crypto/sha1.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
#include "util/asm.hpp"
|
||||
#include "util/coro.hpp"
|
||||
@@ -49,23 +51,6 @@ static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void to_utf8(std::string& out, const wchar_t* source)
|
||||
{
|
||||
// String size
|
||||
const usz length = std::wcslen(source);
|
||||
|
||||
// Safe buffer size for max possible output length (including null terminator)
|
||||
const int buf_size = narrow<int>(length * 3 + 1);
|
||||
|
||||
// Resize buffer
|
||||
out.resize(buf_size - 1);
|
||||
|
||||
const int result = WideCharToMultiByte(CP_UTF8, 0, source, static_cast<int>(length) + 1, &out.front(), buf_size, nullptr, nullptr);
|
||||
|
||||
// Fix the size
|
||||
out.resize(ensure(result) - 1);
|
||||
}
|
||||
|
||||
static time_t to_time(const ULARGE_INTEGER& ft)
|
||||
{
|
||||
return ft.QuadPart / 10000000ULL - 11644473600ULL;
|
||||
@@ -153,6 +138,7 @@ static fs::error to_error(DWORD e)
|
||||
#if defined(__APPLE__)
|
||||
#include <copyfile.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <limits.h>
|
||||
#elif defined(__linux__) || defined(__sun)
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/syscall.h>
|
||||
@@ -371,6 +357,12 @@ namespace fs
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_base::create_symlink(const std::string&)
|
||||
{
|
||||
g_tls_error = error::readonly;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_base::rename(const std::string&, const std::string&)
|
||||
{
|
||||
g_tls_error = error::readonly;
|
||||
@@ -531,7 +523,7 @@ bool fs::get_stat(const std::string& path, stat_t& info)
|
||||
// Handle drives specially
|
||||
if (epath.find_first_of(delim) == umax && epath.ends_with(':'))
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA attrs;
|
||||
WIN32_FILE_ATTRIBUTE_DATA attrs{};
|
||||
|
||||
// Must end with a delimiter
|
||||
if (!GetFileAttributesExW(to_wchar(std::string(epath) + '/').get(), GetFileExInfoStandard, &attrs))
|
||||
@@ -553,8 +545,6 @@ bool fs::get_stat(const std::string& path, stat_t& info)
|
||||
return true;
|
||||
}
|
||||
|
||||
WIN32_FIND_DATA attrs;
|
||||
|
||||
// Allowed by FindFirstFileExW but we should not allow it
|
||||
if (epath.ends_with("*"))
|
||||
{
|
||||
@@ -562,6 +552,8 @@ bool fs::get_stat(const std::string& path, stat_t& info)
|
||||
return false;
|
||||
}
|
||||
|
||||
WIN32_FIND_DATA attrs{};
|
||||
|
||||
const auto wchar_ptr = to_wchar(std::string(epath));
|
||||
const std::wstring_view wpath_view = wchar_ptr.get();
|
||||
|
||||
@@ -595,6 +587,7 @@ bool fs::get_stat(const std::string& path, stat_t& info)
|
||||
}
|
||||
|
||||
info.is_directory = (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
info.is_symlink = (attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
|
||||
info.is_writable = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
|
||||
info.size = attrs.nFileSizeLow | (u64{attrs.nFileSizeHigh} << 32);
|
||||
info.atime = to_time(attrs.ftLastAccessTime);
|
||||
@@ -609,6 +602,7 @@ bool fs::get_stat(const std::string& path, stat_t& info)
|
||||
}
|
||||
|
||||
info.is_directory = S_ISDIR(file_info.st_mode);
|
||||
info.is_symlink = S_ISLNK(file_info.st_mode);
|
||||
info.is_writable = file_info.st_mode & 0200; // HACK: approximation
|
||||
info.size = file_info.st_size;
|
||||
info.atime = file_info.st_atime;
|
||||
@@ -662,6 +656,23 @@ bool fs::is_dir(const std::string& path)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fs::is_symlink(const std::string& path)
|
||||
{
|
||||
fs::stat_t info{};
|
||||
if (!fs::get_stat(path, info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!info.is_symlink)
|
||||
{
|
||||
g_tls_error = error::exist;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fs::statfs(const std::string& path, fs::device_stat& info)
|
||||
{
|
||||
if (auto device = get_virtual_device(path))
|
||||
@@ -761,7 +772,7 @@ bool fs::create_path(const std::string& path)
|
||||
|
||||
#ifdef _WIN32
|
||||
// Workaround: don't call is_dir with naked drive letter
|
||||
if (parent.size() < path.size() && parent.back() != ':' && !is_dir(parent) && !create_path(parent))
|
||||
if (parent.size() < path.size() && (parent.empty() || (parent.back() != ':' && !is_dir(parent) && !create_path(parent))))
|
||||
#else
|
||||
if (parent.size() < path.size() && !is_dir(parent) && !create_path(parent))
|
||||
#endif
|
||||
@@ -797,10 +808,35 @@ bool fs::remove_dir(const std::string& path)
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (::rmdir(path.c_str()) != 0)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fs::create_symlink(const std::string& path, const std::string& target)
|
||||
{
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
return device->create_symlink(path);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const DWORD flags = is_dir(target) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
|
||||
if (!CreateSymbolicLinkW(to_wchar(path).get(), to_wchar(target).get(), flags))
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
if (::rmdir(path.c_str()) != 0)
|
||||
if (::symlink(target.c_str(), path.c_str()) != 0)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
@@ -1789,7 +1825,7 @@ bool fs::dir::open(const std::string& path)
|
||||
{
|
||||
dir_entry info;
|
||||
|
||||
to_utf8(info.name, found.cFileName);
|
||||
info.name = wchar_to_utf8(found.cFileName);
|
||||
info.is_directory = (found.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
info.is_writable = (found.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
|
||||
info.size = (static_cast<u64>(found.nFileSizeHigh) << 32) | static_cast<u64>(found.nFileSizeLow);
|
||||
@@ -1905,9 +1941,9 @@ bool fs::dir::open(const std::string& path)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fs::file::strict_read_check(u64 _size, u64 type_size) const
|
||||
bool fs::file::strict_read_check(u64 offset, u64 _size, u64 type_size) const
|
||||
{
|
||||
if (usz pos0 = pos(), size0 = size(); (pos0 >= size0 ? 0 : (size0 - pos0)) / type_size < _size)
|
||||
if (usz pos0 = offset, size0 = size(); (pos0 >= size0 ? 0 : (size0 - pos0)) / type_size < _size)
|
||||
{
|
||||
fs::g_tls_error = fs::error::inval;
|
||||
return false;
|
||||
@@ -1916,6 +1952,72 @@ bool fs::file::strict_read_check(u64 _size, u64 type_size) const
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string fs::get_executable_path()
|
||||
{
|
||||
// Use magic static
|
||||
static const std::string s_exe_path = []
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
constexpr DWORD size = 32767;
|
||||
std::vector<wchar_t> buffer(size);
|
||||
GetModuleFileNameW(nullptr, buffer.data(), size);
|
||||
return wchar_to_utf8(buffer.data());
|
||||
#elif defined(__APPLE__)
|
||||
char bin_path[PATH_MAX];
|
||||
uint32_t bin_path_size = sizeof(bin_path);
|
||||
if (_NSGetExecutablePath(bin_path, &bin_path_size) != 0)
|
||||
{
|
||||
std::cerr << "Failed to find app binary path" << std::endl;
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
// App bundle directory is three levels up from the binary.
|
||||
return get_parent_dir(bin_path, 3);
|
||||
#else
|
||||
if (const char* appimage_path = ::getenv("APPIMAGE"))
|
||||
{
|
||||
std::cout << "Found AppImage path: " << appimage_path << std::endl;
|
||||
return std::string(appimage_path);
|
||||
}
|
||||
|
||||
std::cout << "No AppImage path found, checking for executable" << std::endl;
|
||||
|
||||
char exe_path[PATH_MAX];
|
||||
const ssize_t len = ::readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
std::cerr << "Failed to find executable path" << std::endl;
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
exe_path[len] = '\0';
|
||||
std::cout << "Found exec path: " << exe_path << std::endl;
|
||||
|
||||
return std::string(exe_path);
|
||||
#endif
|
||||
}();
|
||||
|
||||
return s_exe_path;
|
||||
}
|
||||
|
||||
std::string fs::get_executable_dir()
|
||||
{
|
||||
// Use magic static
|
||||
static const std::string s_exe_dir = []
|
||||
{
|
||||
std::string exe_path = get_executable_path();
|
||||
if (exe_path.empty())
|
||||
{
|
||||
return exe_path;
|
||||
}
|
||||
|
||||
return get_parent_dir(exe_path) + "/";
|
||||
}();
|
||||
|
||||
return s_exe_dir;
|
||||
}
|
||||
|
||||
const std::string& fs::get_config_dir()
|
||||
{
|
||||
// Use magic static
|
||||
@@ -1923,17 +2025,52 @@ const std::string& fs::get_config_dir()
|
||||
{
|
||||
std::string dir;
|
||||
|
||||
#ifdef _WIN32
|
||||
wchar_t buf[32768];
|
||||
constexpr DWORD size = static_cast<DWORD>(std::size(buf));
|
||||
if (GetEnvironmentVariable(L"RPCS3_CONFIG_DIR", buf, size) - 1 >= size - 1 &&
|
||||
GetModuleFileName(nullptr, buf, size) - 1 >= size - 1)
|
||||
// Check if a portable directory exists.
|
||||
std::string portable_dir = get_executable_dir() + "/portable/";
|
||||
if (is_dir(portable_dir))
|
||||
{
|
||||
MessageBoxA(nullptr, fmt::format("GetModuleFileName() failed: error: %s", fmt::win_error{GetLastError(), nullptr}).c_str(), "fs::get_config_dir()", MB_ICONERROR);
|
||||
return dir; // empty
|
||||
return portable_dir;
|
||||
}
|
||||
|
||||
to_utf8(dir, buf); // Convert to UTF-8
|
||||
#ifdef _WIN32
|
||||
std::vector<wchar_t> buf;
|
||||
|
||||
// Check if RPCS3_CONFIG_DIR is set and get the required buffer size
|
||||
DWORD size = GetEnvironmentVariable(L"RPCS3_CONFIG_DIR", nullptr, 0);
|
||||
if (size > 0)
|
||||
{
|
||||
// Resize buffer and fetch RPCS3_CONFIG_DIR
|
||||
buf.resize(size);
|
||||
|
||||
if (GetEnvironmentVariable(L"RPCS3_CONFIG_DIR", buf.data(), size) != (size - 1))
|
||||
{
|
||||
// Clear buffer on failure and notify user
|
||||
MessageBoxA(nullptr, fmt::format("GetEnvironmentVariable(RPCS3_CONFIG_DIR) failed: error: %s", fmt::win_error{GetLastError(), nullptr}).c_str(), "fs::get_config_dir()", MB_ICONERROR);
|
||||
buf.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to executable path if needed
|
||||
for (DWORD buf_size = MAX_PATH; size == 0; buf_size += MAX_PATH)
|
||||
{
|
||||
buf.resize(buf_size);
|
||||
size = GetModuleFileName(nullptr, buf.data(), buf_size);
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
MessageBoxA(nullptr, fmt::format("GetModuleFileName() failed: error: %s", fmt::win_error{GetLastError(), nullptr}).c_str(), "fs::get_config_dir()", MB_ICONERROR);
|
||||
return dir; // empty
|
||||
}
|
||||
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
// Try again with increased buffer size
|
||||
size = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
dir = wchar_to_utf8(buf.data());
|
||||
|
||||
std::replace(dir.begin(), dir.end(), '\\', '/');
|
||||
|
||||
@@ -2018,10 +2155,23 @@ const std::string& fs::get_temp_dir()
|
||||
return dir; // empty
|
||||
}
|
||||
|
||||
to_utf8(dir, buf);
|
||||
dir = wchar_to_utf8(buf);
|
||||
#else
|
||||
// TODO
|
||||
dir = get_cache_dir();
|
||||
const char* tmp_dir = getenv("TMPDIR");
|
||||
if (tmp_dir == nullptr || tmp_dir[0] == '\0')
|
||||
{
|
||||
// Fall back to cache directory
|
||||
dir = get_cache_dir();
|
||||
}
|
||||
else
|
||||
{
|
||||
dir = tmp_dir;
|
||||
if (!dir.ends_with("/"))
|
||||
{
|
||||
// Ensure path ends with a separator
|
||||
dir += "/";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return dir;
|
||||
|
||||
+24
-6
@@ -66,6 +66,7 @@ namespace fs
|
||||
struct stat_t
|
||||
{
|
||||
bool is_directory;
|
||||
bool is_symlink;
|
||||
bool is_writable;
|
||||
u64 size;
|
||||
s64 atime;
|
||||
@@ -155,6 +156,7 @@ namespace fs
|
||||
virtual bool statfs(const std::string& path, device_stat& info) = 0;
|
||||
virtual bool remove_dir(const std::string& path);
|
||||
virtual bool create_dir(const std::string& path);
|
||||
virtual bool create_symlink(const std::string& path);
|
||||
virtual bool rename(const std::string& from, const std::string& to);
|
||||
virtual bool remove(const std::string& path);
|
||||
virtual bool trunc(const std::string& path, u64 length);
|
||||
@@ -197,6 +199,9 @@ namespace fs
|
||||
// Check whether the directory exists and is NOT a file
|
||||
bool is_dir(const std::string& path);
|
||||
|
||||
// Check whether the path points to an existing symlink
|
||||
bool is_symlink(const std::string& path);
|
||||
|
||||
// Get filesystem information
|
||||
bool statfs(const std::string& path, device_stat& info);
|
||||
|
||||
@@ -209,6 +214,9 @@ namespace fs
|
||||
// Create directories
|
||||
bool create_path(const std::string& path);
|
||||
|
||||
// Create symbolic link
|
||||
bool create_symlink(const std::string& path, const std::string& target);
|
||||
|
||||
// Rename (move) file or directory
|
||||
bool rename(const std::string& from, const std::string& to, bool overwrite);
|
||||
|
||||
@@ -306,7 +314,7 @@ namespace fs
|
||||
}
|
||||
|
||||
// Check if the handle is capable of reading (size * type_size) of bytes at the time of calling
|
||||
bool strict_read_check(u64 size, u64 type_size = 1) const;
|
||||
bool strict_read_check(u64 offset, u64 size, u64 type_size) const;
|
||||
|
||||
// Read the data from the file and return the amount of data written in buffer
|
||||
u64 read(void* buffer, u64 count,
|
||||
@@ -424,7 +432,7 @@ namespace fs
|
||||
if (_size != umax)
|
||||
{
|
||||
// If _size arg is too high std::bad_alloc may happen during resize and then we cannot error check
|
||||
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(_size, sizeof(T)))
|
||||
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(pos(), _size, sizeof(T)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -449,7 +457,7 @@ namespace fs
|
||||
|
||||
// Read POD std::vector
|
||||
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
|
||||
bool read(std::vector<T>& vec, usz _size = umax,
|
||||
bool read(std::vector<T>& vec, usz _size = umax, bool use_offs = false, usz offset = umax,
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION(),
|
||||
u32 line = __builtin_LINE(),
|
||||
@@ -460,7 +468,7 @@ namespace fs
|
||||
if (_size != umax)
|
||||
{
|
||||
// If _size arg is too high std::bad_alloc may happen during resize and then we cannot error check
|
||||
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(_size, sizeof(T)))
|
||||
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(use_offs ? offset : pos(), _size, sizeof(T)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -468,6 +476,11 @@ namespace fs
|
||||
vec.resize(_size);
|
||||
}
|
||||
|
||||
if (use_offs)
|
||||
{
|
||||
return read_at(offset, vec.data(), sizeof(T) * vec.size(), line, col, file, func) == sizeof(T) * vec.size();
|
||||
}
|
||||
|
||||
return read(vec.data(), sizeof(T) * vec.size(), line, col, file, func) == sizeof(T) * vec.size();
|
||||
}
|
||||
|
||||
@@ -510,8 +523,7 @@ namespace fs
|
||||
{
|
||||
std::vector<T> result;
|
||||
result.resize(size() / sizeof(T));
|
||||
seek(0);
|
||||
if (!read(result, result.size(), file, func, line, col)) xfail({line, col, file, func});
|
||||
if (!read(result, result.size(), true, 0, file, func, line, col)) xfail({line, col, file, func});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -660,6 +672,12 @@ namespace fs
|
||||
}
|
||||
};
|
||||
|
||||
// Get executable path
|
||||
std::string get_executable_path();
|
||||
|
||||
// Get executable containing directory
|
||||
std::string get_executable_dir();
|
||||
|
||||
// Get configuration directory
|
||||
const std::string& get_config_dir();
|
||||
|
||||
|
||||
+1
-1
@@ -554,4 +554,4 @@ public:
|
||||
static std::string triple2();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // LLVM_AVAILABLE
|
||||
|
||||
@@ -4,16 +4,13 @@
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "util/logs.hpp"
|
||||
#include "mutex.h"
|
||||
#include "util/vm.hpp"
|
||||
#include "util/asm.hpp"
|
||||
#include "util/v128.hpp"
|
||||
#include "util/simd.hpp"
|
||||
#include "Crypto/unzip.h"
|
||||
|
||||
#include <charconv>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#define CAN_OVERCOMMIT
|
||||
#endif
|
||||
|
||||
@@ -845,648 +842,3 @@ void asmjit::simd_builder::vec_extract_gpr(u32 esize, const x86::Gp& dst, const
|
||||
}
|
||||
|
||||
#endif /* X86 */
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <deque>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wall"
|
||||
#pragma GCC diagnostic ignored "-Wextra"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#pragma GCC diagnostic ignored "-Wredundant-decls"
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
|
||||
#endif
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/FormattedStream.h"
|
||||
#include "llvm/TargetParser/Host.h"
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
|
||||
#include "llvm/ExecutionEngine/ObjectCache.h"
|
||||
#include "llvm/ExecutionEngine/JITEventListener.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/Object/SymbolSize.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
const bool jit_initialize = []() -> bool
|
||||
{
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
llvm::InitializeNativeTargetAsmParser();
|
||||
LLVMLinkInMCJIT();
|
||||
return true;
|
||||
}();
|
||||
|
||||
[[noreturn]] static void null(const char* name)
|
||||
{
|
||||
fmt::throw_exception("Null function: %s", name);
|
||||
}
|
||||
|
||||
namespace vm
|
||||
{
|
||||
extern u8* const g_sudo_addr;
|
||||
}
|
||||
|
||||
static shared_mutex null_mtx;
|
||||
|
||||
static std::unordered_map<std::string, u64> null_funcs;
|
||||
|
||||
static u64 make_null_function(const std::string& name)
|
||||
{
|
||||
if (name.starts_with("__0x"))
|
||||
{
|
||||
u32 addr = -1;
|
||||
auto res = std::from_chars(name.c_str() + 4, name.c_str() + name.size(), addr, 16);
|
||||
|
||||
if (res.ec == std::errc() && res.ptr == name.c_str() + name.size() && addr < 0x8000'0000)
|
||||
{
|
||||
// Point the garbage to reserved, non-executable memory
|
||||
return reinterpret_cast<u64>(vm::g_sudo_addr + addr);
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard lock(null_mtx);
|
||||
|
||||
if (u64& func_ptr = null_funcs[name]) [[likely]]
|
||||
{
|
||||
// Already exists
|
||||
return func_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
using namespace asmjit;
|
||||
|
||||
// Build a "null" function that contains its name
|
||||
const auto func = build_function_asm<void (*)()>("NULL", [&](native_asm& c, auto& args)
|
||||
{
|
||||
#if defined(ARCH_X64)
|
||||
Label data = c.newLabel();
|
||||
c.lea(args[0], x86::qword_ptr(data, 0));
|
||||
c.jmp(Imm(&null));
|
||||
c.align(AlignMode::kCode, 16);
|
||||
c.bind(data);
|
||||
|
||||
// Copy function name bytes
|
||||
for (char ch : name)
|
||||
c.db(ch);
|
||||
c.db(0);
|
||||
c.align(AlignMode::kData, 16);
|
||||
#else
|
||||
// AArch64 implementation
|
||||
Label jmp_address = c.newLabel();
|
||||
Label data = c.newLabel();
|
||||
// Force absolute jump to prevent out of bounds PC-rel jmp
|
||||
c.ldr(args[0], arm::ptr(jmp_address));
|
||||
c.br(args[0]);
|
||||
c.align(AlignMode::kCode, 16);
|
||||
|
||||
c.bind(data);
|
||||
c.embed(name.c_str(), name.size());
|
||||
c.embedUInt8(0U);
|
||||
c.bind(jmp_address);
|
||||
c.embedUInt64(reinterpret_cast<u64>(&null));
|
||||
c.align(AlignMode::kData, 16);
|
||||
#endif
|
||||
});
|
||||
|
||||
func_ptr = reinterpret_cast<u64>(func);
|
||||
return func_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
struct JITAnnouncer : llvm::JITEventListener
|
||||
{
|
||||
void notifyObjectLoaded(u64, const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& info) override
|
||||
{
|
||||
using namespace llvm;
|
||||
|
||||
object::OwningBinary<object::ObjectFile> debug_obj_ = info.getObjectForDebug(obj);
|
||||
if (!debug_obj_.getBinary())
|
||||
{
|
||||
#ifdef __linux__
|
||||
jit_log.error("LLVM: Failed to announce JIT events (no debug object)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
const object::ObjectFile& debug_obj = *debug_obj_.getBinary();
|
||||
|
||||
for (const auto& [sym, size] : computeSymbolSizes(debug_obj))
|
||||
{
|
||||
Expected<object::SymbolRef::Type> type_ = sym.getType();
|
||||
if (!type_ || *type_ != object::SymbolRef::ST_Function)
|
||||
continue;
|
||||
|
||||
Expected<StringRef> name = sym.getName();
|
||||
if (!name)
|
||||
continue;
|
||||
|
||||
Expected<u64> addr = sym.getAddress();
|
||||
if (!addr)
|
||||
continue;
|
||||
|
||||
jit_announce(*addr, size, {name->data(), name->size()});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Simple memory manager
|
||||
struct MemoryManager1 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
// 256 MiB for code or data
|
||||
static constexpr u64 c_max_size = 0x20000000 / 2;
|
||||
|
||||
// Allocation unit (2M)
|
||||
static constexpr u64 c_page_size = 2 * 1024 * 1024;
|
||||
|
||||
// Reserve 512 MiB
|
||||
u8* const ptr = static_cast<u8*>(utils::memory_reserve(c_max_size * 2));
|
||||
|
||||
u64 code_ptr = 0;
|
||||
u64 data_ptr = c_max_size;
|
||||
|
||||
MemoryManager1() = default;
|
||||
|
||||
MemoryManager1(const MemoryManager1&) = delete;
|
||||
|
||||
MemoryManager1& operator=(const MemoryManager1&) = delete;
|
||||
|
||||
~MemoryManager1() override
|
||||
{
|
||||
// Hack: don't release to prevent reuse of address space, see jit_announce
|
||||
utils::memory_decommit(ptr, c_max_size * 2);
|
||||
}
|
||||
|
||||
llvm::JITSymbol findSymbol(const std::string& name) override
|
||||
{
|
||||
u64 addr = RTDyldMemoryManager::getSymbolAddress(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
addr = make_null_function(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
fmt::throw_exception("Failed to link '%s'", name);
|
||||
}
|
||||
}
|
||||
|
||||
return {addr, llvm::JITSymbolFlags::Exported};
|
||||
}
|
||||
|
||||
u8* allocate(u64& oldp, uptr size, uint align, utils::protection prot)
|
||||
{
|
||||
if (align > c_page_size)
|
||||
{
|
||||
jit_log.fatal("Unsupported alignment (size=0x%x, align=0x%x)", size, align);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const u64 olda = utils::align(oldp, align);
|
||||
const u64 newp = utils::align(olda + size, align);
|
||||
|
||||
if ((newp - 1) / c_max_size != oldp / c_max_size)
|
||||
{
|
||||
jit_log.fatal("Out of memory (size=0x%x, align=0x%x)", size, align);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((oldp - 1) / c_page_size != (newp - 1) / c_page_size)
|
||||
{
|
||||
// Allocate pages on demand
|
||||
const u64 pagea = utils::align(oldp, c_page_size);
|
||||
const u64 psize = utils::align(newp - pagea, c_page_size);
|
||||
utils::memory_commit(this->ptr + pagea, psize, prot);
|
||||
}
|
||||
|
||||
// Update allocation counter
|
||||
oldp = newp;
|
||||
|
||||
return this->ptr + olda;
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/) override
|
||||
{
|
||||
return allocate(code_ptr, size, align, utils::protection::wx);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/, bool /*is_ro*/) override
|
||||
{
|
||||
return allocate(data_ptr, size, align, utils::protection::rw);
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8*, u64, usz) override
|
||||
{
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Simple memory manager
|
||||
struct MemoryManager2 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
MemoryManager2() = default;
|
||||
|
||||
~MemoryManager2() override
|
||||
{
|
||||
}
|
||||
|
||||
llvm::JITSymbol findSymbol(const std::string& name) override
|
||||
{
|
||||
u64 addr = RTDyldMemoryManager::getSymbolAddress(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
addr = make_null_function(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
fmt::throw_exception("Failed to link '%s' (MM2)", name);
|
||||
}
|
||||
}
|
||||
|
||||
return {addr, llvm::JITSymbolFlags::Exported};
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, true);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/, bool /*is_ro*/) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, false);
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8*, u64, usz) override
|
||||
{
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Helper class
|
||||
class ObjectCache final : public llvm::ObjectCache
|
||||
{
|
||||
const std::string& m_path;
|
||||
|
||||
public:
|
||||
ObjectCache(const std::string& path)
|
||||
: m_path(path)
|
||||
{
|
||||
}
|
||||
|
||||
~ObjectCache() override = default;
|
||||
|
||||
void notifyObjectCompiled(const llvm::Module* _module, llvm::MemoryBufferRef obj) override
|
||||
{
|
||||
std::string name = m_path;
|
||||
name.append(_module->getName().data());
|
||||
//fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
|
||||
name.append(".gz");
|
||||
|
||||
fs::file module_file(name, fs::rewrite);
|
||||
|
||||
if (!module_file)
|
||||
{
|
||||
jit_log.error("LLVM: Failed to create module file: %s (%s)", name, fs::g_tls_error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!zip(obj.getBufferStart(), obj.getBufferSize(), module_file))
|
||||
{
|
||||
jit_log.error("LLVM: Failed to compress module: %s", _module->getName().data());
|
||||
module_file.close();
|
||||
fs::remove_file(name);
|
||||
return;
|
||||
}
|
||||
|
||||
jit_log.notice("LLVM: Created module: %s", _module->getName().data());
|
||||
}
|
||||
|
||||
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
|
||||
{
|
||||
if (fs::file cached{path + ".gz", fs::read})
|
||||
{
|
||||
const std::vector<u8> cached_data = cached.to_vector<u8>();
|
||||
|
||||
if (cached_data.empty()) [[unlikely]]
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<u8> out = unzip(cached_data);
|
||||
|
||||
if (out.empty())
|
||||
{
|
||||
jit_log.error("LLVM: Failed to unzip module: '%s'", path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(out.size());
|
||||
std::memcpy(buf->getBufferStart(), out.data(), out.size());
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (fs::file cached{path, fs::read})
|
||||
{
|
||||
if (cached.size() == 0) [[unlikely]]
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(buf->getBufferStart(), buf->getBufferSize());
|
||||
return buf;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* _module) override
|
||||
{
|
||||
std::string path = m_path;
|
||||
path.append(_module->getName().data());
|
||||
|
||||
if (auto buf = load(path))
|
||||
{
|
||||
jit_log.notice("LLVM: Loaded module: %s", _module->getName().data());
|
||||
return buf;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
std::string jit_compiler::cpu(const std::string& _cpu)
|
||||
{
|
||||
std::string m_cpu = _cpu;
|
||||
|
||||
if (m_cpu.empty())
|
||||
{
|
||||
m_cpu = llvm::sys::getHostCPUName().str();
|
||||
|
||||
if (m_cpu == "sandybridge" ||
|
||||
m_cpu == "ivybridge" ||
|
||||
m_cpu == "haswell" ||
|
||||
m_cpu == "broadwell" ||
|
||||
m_cpu == "skylake" ||
|
||||
m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cascadelake" ||
|
||||
m_cpu == "cooperlake" ||
|
||||
m_cpu == "cannonlake" ||
|
||||
m_cpu == "icelake" ||
|
||||
m_cpu == "icelake-client" ||
|
||||
m_cpu == "icelake-server" ||
|
||||
m_cpu == "tigerlake" ||
|
||||
m_cpu == "rocketlake" ||
|
||||
m_cpu == "alderlake" ||
|
||||
m_cpu == "raptorlake" ||
|
||||
m_cpu == "meteorlake")
|
||||
{
|
||||
// Downgrade if AVX is not supported by some chips
|
||||
if (!utils::has_avx())
|
||||
{
|
||||
m_cpu = "nehalem";
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cascadelake" ||
|
||||
m_cpu == "cooperlake" ||
|
||||
m_cpu == "cannonlake" ||
|
||||
m_cpu == "icelake" ||
|
||||
m_cpu == "icelake-client" ||
|
||||
m_cpu == "icelake-server" ||
|
||||
m_cpu == "tigerlake" ||
|
||||
m_cpu == "rocketlake")
|
||||
{
|
||||
// Downgrade if AVX-512 is disabled or not supported
|
||||
if (!utils::has_avx512())
|
||||
{
|
||||
m_cpu = "skylake";
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cpu == "znver1" && utils::has_clwb())
|
||||
{
|
||||
// Upgrade
|
||||
m_cpu = "znver2";
|
||||
}
|
||||
|
||||
if ((m_cpu == "znver3" || m_cpu == "goldmont" || m_cpu == "alderlake" || m_cpu == "raptorlake" || m_cpu == "meteorlake") && utils::has_avx512_icl())
|
||||
{
|
||||
// Upgrade
|
||||
m_cpu = "icelake-client";
|
||||
}
|
||||
|
||||
if (m_cpu == "goldmont" && utils::has_avx2())
|
||||
{
|
||||
// Upgrade
|
||||
m_cpu = "alderlake";
|
||||
}
|
||||
}
|
||||
|
||||
return m_cpu;
|
||||
}
|
||||
|
||||
std::string jit_compiler::triple1()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
|
||||
#elif defined(__APPLE__) && defined(ARCH_X64)
|
||||
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
|
||||
#elif defined(__APPLE__) && defined(ARCH_ARM64)
|
||||
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
|
||||
#else
|
||||
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string jit_compiler::triple2()
|
||||
{
|
||||
#if defined(_WIN32) && defined(ARCH_X64)
|
||||
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
|
||||
#elif defined(_WIN32) && defined(ARCH_ARM64)
|
||||
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
|
||||
#elif defined(__APPLE__) && defined(ARCH_X64)
|
||||
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
|
||||
#elif defined(__APPLE__) && defined(ARCH_ARM64)
|
||||
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
|
||||
#else
|
||||
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
|
||||
#endif
|
||||
}
|
||||
|
||||
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags)
|
||||
: m_context(new llvm::LLVMContext)
|
||||
, m_cpu(cpu(_cpu))
|
||||
{
|
||||
std::string result;
|
||||
|
||||
auto null_mod = std::make_unique<llvm::Module> ("null_", *m_context);
|
||||
null_mod->setTargetTriple(jit_compiler::triple1());
|
||||
|
||||
std::unique_ptr<llvm::RTDyldMemoryManager> mem;
|
||||
|
||||
if (_link.empty())
|
||||
{
|
||||
// Auxiliary JIT (does not use custom memory manager, only writes the objects)
|
||||
if (flags & 0x1)
|
||||
{
|
||||
mem = std::make_unique<MemoryManager1>();
|
||||
}
|
||||
else
|
||||
{
|
||||
mem = std::make_unique<MemoryManager2>();
|
||||
null_mod->setTargetTriple(jit_compiler::triple2());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mem = std::make_unique<MemoryManager1>();
|
||||
}
|
||||
|
||||
{
|
||||
m_engine.reset(llvm::EngineBuilder(std::move(null_mod))
|
||||
.setErrorStr(&result)
|
||||
.setEngineKind(llvm::EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::move(mem))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
|
||||
#ifdef __APPLE__
|
||||
//.setCodeModel(llvm::CodeModel::Large)
|
||||
#endif
|
||||
.setRelocationModel(llvm::Reloc::Model::PIC_)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
}
|
||||
|
||||
if (!_link.empty())
|
||||
{
|
||||
for (auto&& [name, addr] : _link)
|
||||
{
|
||||
m_engine->updateGlobalMapping(name, addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_link.empty() || !(flags & 0x1))
|
||||
{
|
||||
m_engine->RegisterJITEventListener(llvm::JITEventListener::createIntelJITEventListener());
|
||||
m_engine->RegisterJITEventListener(new JITAnnouncer);
|
||||
}
|
||||
|
||||
if (!m_engine)
|
||||
{
|
||||
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
|
||||
}
|
||||
}
|
||||
|
||||
jit_compiler::~jit_compiler()
|
||||
{
|
||||
}
|
||||
|
||||
void jit_compiler::add(std::unique_ptr<llvm::Module> _module, const std::string& path)
|
||||
{
|
||||
ObjectCache cache{path};
|
||||
m_engine->setObjectCache(&cache);
|
||||
|
||||
const auto ptr = _module.get();
|
||||
m_engine->addModule(std::move(_module));
|
||||
m_engine->generateCodeForModule(ptr);
|
||||
m_engine->setObjectCache(nullptr);
|
||||
|
||||
for (auto& func : ptr->functions())
|
||||
{
|
||||
// Delete IR to lower memory consumption
|
||||
func.deleteBody();
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(std::unique_ptr<llvm::Module> _module)
|
||||
{
|
||||
const auto ptr = _module.get();
|
||||
m_engine->addModule(std::move(_module));
|
||||
m_engine->generateCodeForModule(ptr);
|
||||
|
||||
for (auto& func : ptr->functions())
|
||||
{
|
||||
// Delete IR to lower memory consumption
|
||||
func.deleteBody();
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(const std::string& path)
|
||||
{
|
||||
auto cache = ObjectCache::load(path);
|
||||
|
||||
if (auto object_file = llvm::object::ObjectFile::createObjectFile(*cache))
|
||||
{
|
||||
m_engine->addObjectFile(llvm::object::OwningBinary<llvm::object::ObjectFile>(std::move(*object_file), std::move(cache)));
|
||||
}
|
||||
else
|
||||
{
|
||||
jit_log.error("ObjectCache: Adding failed: %s", path);
|
||||
}
|
||||
}
|
||||
|
||||
bool jit_compiler::check(const std::string& path)
|
||||
{
|
||||
if (auto cache = ObjectCache::load(path))
|
||||
{
|
||||
if (auto object_file = llvm::object::ObjectFile::createObjectFile(*cache))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fs::remove_file(path))
|
||||
{
|
||||
jit_log.error("ObjectCache: Removed damaged file: %s", path);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void jit_compiler::update_global_mapping(const std::string& name, u64 addr)
|
||||
{
|
||||
m_engine->updateGlobalMapping(name, addr);
|
||||
}
|
||||
|
||||
void jit_compiler::fin()
|
||||
{
|
||||
m_engine->finalizeObject();
|
||||
}
|
||||
|
||||
u64 jit_compiler::get(const std::string& name)
|
||||
{
|
||||
return m_engine->getGlobalValueAddress(name);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,656 @@
|
||||
#include "util/types.hpp"
|
||||
#include "util/sysinfo.hpp"
|
||||
#include "JIT.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "util/logs.hpp"
|
||||
#include "mutex.h"
|
||||
#include "util/vm.hpp"
|
||||
#include "util/asm.hpp"
|
||||
#include "Crypto/unzip.h"
|
||||
|
||||
#include <charconv>
|
||||
|
||||
LOG_CHANNEL(jit_log, "JIT");
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wall"
|
||||
#pragma GCC diagnostic ignored "-Wextra"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#pragma GCC diagnostic ignored "-Wredundant-decls"
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
|
||||
#endif
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/TargetParser/Host.h"
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
|
||||
#include "llvm/ExecutionEngine/ObjectCache.h"
|
||||
#include "llvm/ExecutionEngine/JITEventListener.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/Object/SymbolSize.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
const bool jit_initialize = []() -> bool
|
||||
{
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
llvm::InitializeNativeTargetAsmParser();
|
||||
LLVMLinkInMCJIT();
|
||||
return true;
|
||||
}();
|
||||
|
||||
[[noreturn]] static void null(const char* name)
|
||||
{
|
||||
fmt::throw_exception("Null function: %s", name);
|
||||
}
|
||||
|
||||
namespace vm
|
||||
{
|
||||
extern u8* const g_sudo_addr;
|
||||
}
|
||||
|
||||
static shared_mutex null_mtx;
|
||||
|
||||
static std::unordered_map<std::string, u64> null_funcs;
|
||||
|
||||
static u64 make_null_function(const std::string& name)
|
||||
{
|
||||
if (name.starts_with("__0x"))
|
||||
{
|
||||
u32 addr = -1;
|
||||
auto res = std::from_chars(name.c_str() + 4, name.c_str() + name.size(), addr, 16);
|
||||
|
||||
if (res.ec == std::errc() && res.ptr == name.c_str() + name.size() && addr < 0x8000'0000)
|
||||
{
|
||||
// Point the garbage to reserved, non-executable memory
|
||||
return reinterpret_cast<u64>(vm::g_sudo_addr + addr);
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard lock(null_mtx);
|
||||
|
||||
if (u64& func_ptr = null_funcs[name]) [[likely]]
|
||||
{
|
||||
// Already exists
|
||||
return func_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
using namespace asmjit;
|
||||
|
||||
// Build a "null" function that contains its name
|
||||
const auto func = build_function_asm<void (*)()>("NULL", [&](native_asm& c, auto& args)
|
||||
{
|
||||
#if defined(ARCH_X64)
|
||||
Label data = c.newLabel();
|
||||
c.lea(args[0], x86::qword_ptr(data, 0));
|
||||
c.jmp(Imm(&null));
|
||||
c.align(AlignMode::kCode, 16);
|
||||
c.bind(data);
|
||||
|
||||
// Copy function name bytes
|
||||
for (char ch : name)
|
||||
c.db(ch);
|
||||
c.db(0);
|
||||
c.align(AlignMode::kData, 16);
|
||||
#else
|
||||
// AArch64 implementation
|
||||
Label jmp_address = c.newLabel();
|
||||
Label data = c.newLabel();
|
||||
// Force absolute jump to prevent out of bounds PC-rel jmp
|
||||
c.ldr(args[0], arm::ptr(jmp_address));
|
||||
c.br(args[0]);
|
||||
c.align(AlignMode::kCode, 16);
|
||||
|
||||
c.bind(data);
|
||||
c.embed(name.c_str(), name.size());
|
||||
c.embedUInt8(0U);
|
||||
c.bind(jmp_address);
|
||||
c.embedUInt64(reinterpret_cast<u64>(&null));
|
||||
c.align(AlignMode::kData, 16);
|
||||
#endif
|
||||
});
|
||||
|
||||
func_ptr = reinterpret_cast<u64>(func);
|
||||
return func_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
struct JITAnnouncer : llvm::JITEventListener
|
||||
{
|
||||
void notifyObjectLoaded(u64, const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& info) override
|
||||
{
|
||||
using namespace llvm;
|
||||
|
||||
object::OwningBinary<object::ObjectFile> debug_obj_ = info.getObjectForDebug(obj);
|
||||
if (!debug_obj_.getBinary())
|
||||
{
|
||||
#ifdef __linux__
|
||||
jit_log.error("LLVM: Failed to announce JIT events (no debug object)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
const object::ObjectFile& debug_obj = *debug_obj_.getBinary();
|
||||
|
||||
for (const auto& [sym, size] : computeSymbolSizes(debug_obj))
|
||||
{
|
||||
Expected<object::SymbolRef::Type> type_ = sym.getType();
|
||||
if (!type_ || *type_ != object::SymbolRef::ST_Function)
|
||||
continue;
|
||||
|
||||
Expected<StringRef> name = sym.getName();
|
||||
if (!name)
|
||||
continue;
|
||||
|
||||
Expected<u64> addr = sym.getAddress();
|
||||
if (!addr)
|
||||
continue;
|
||||
|
||||
jit_announce(*addr, size, {name->data(), name->size()});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Simple memory manager
|
||||
struct MemoryManager1 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
// 256 MiB for code or data
|
||||
static constexpr u64 c_max_size = 0x20000000 / 2;
|
||||
|
||||
// Allocation unit (2M)
|
||||
static constexpr u64 c_page_size = 2 * 1024 * 1024;
|
||||
|
||||
// Reserve 512 MiB
|
||||
u8* const ptr = static_cast<u8*>(utils::memory_reserve(c_max_size * 2));
|
||||
|
||||
u64 code_ptr = 0;
|
||||
u64 data_ptr = c_max_size;
|
||||
|
||||
MemoryManager1() = default;
|
||||
|
||||
MemoryManager1(const MemoryManager1&) = delete;
|
||||
|
||||
MemoryManager1& operator=(const MemoryManager1&) = delete;
|
||||
|
||||
~MemoryManager1() override
|
||||
{
|
||||
// Hack: don't release to prevent reuse of address space, see jit_announce
|
||||
utils::memory_decommit(ptr, c_max_size * 2);
|
||||
}
|
||||
|
||||
llvm::JITSymbol findSymbol(const std::string& name) override
|
||||
{
|
||||
u64 addr = RTDyldMemoryManager::getSymbolAddress(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
addr = make_null_function(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
fmt::throw_exception("Failed to link '%s'", name);
|
||||
}
|
||||
}
|
||||
|
||||
return {addr, llvm::JITSymbolFlags::Exported};
|
||||
}
|
||||
|
||||
u8* allocate(u64& oldp, uptr size, uint align, utils::protection prot)
|
||||
{
|
||||
if (align > c_page_size)
|
||||
{
|
||||
jit_log.fatal("Unsupported alignment (size=0x%x, align=0x%x)", size, align);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const u64 olda = utils::align(oldp, align);
|
||||
const u64 newp = utils::align(olda + size, align);
|
||||
|
||||
if ((newp - 1) / c_max_size != oldp / c_max_size)
|
||||
{
|
||||
jit_log.fatal("Out of memory (size=0x%x, align=0x%x)", size, align);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((oldp - 1) / c_page_size != (newp - 1) / c_page_size)
|
||||
{
|
||||
// Allocate pages on demand
|
||||
const u64 pagea = utils::align(oldp, c_page_size);
|
||||
const u64 psize = utils::align(newp - pagea, c_page_size);
|
||||
utils::memory_commit(this->ptr + pagea, psize, prot);
|
||||
}
|
||||
|
||||
// Update allocation counter
|
||||
oldp = newp;
|
||||
|
||||
return this->ptr + olda;
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/) override
|
||||
{
|
||||
return allocate(code_ptr, size, align, utils::protection::wx);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/, bool /*is_ro*/) override
|
||||
{
|
||||
return allocate(data_ptr, size, align, utils::protection::rw);
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8*, u64, usz) override
|
||||
{
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Simple memory manager
|
||||
struct MemoryManager2 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
MemoryManager2() = default;
|
||||
|
||||
~MemoryManager2() override
|
||||
{
|
||||
}
|
||||
|
||||
llvm::JITSymbol findSymbol(const std::string& name) override
|
||||
{
|
||||
u64 addr = RTDyldMemoryManager::getSymbolAddress(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
addr = make_null_function(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
fmt::throw_exception("Failed to link '%s' (MM2)", name);
|
||||
}
|
||||
}
|
||||
|
||||
return {addr, llvm::JITSymbolFlags::Exported};
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, true);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/, bool /*is_ro*/) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, false);
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8*, u64, usz) override
|
||||
{
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Helper class
|
||||
class ObjectCache final : public llvm::ObjectCache
|
||||
{
|
||||
const std::string& m_path;
|
||||
|
||||
public:
|
||||
ObjectCache(const std::string& path)
|
||||
: m_path(path)
|
||||
{
|
||||
}
|
||||
|
||||
~ObjectCache() override = default;
|
||||
|
||||
void notifyObjectCompiled(const llvm::Module* _module, llvm::MemoryBufferRef obj) override
|
||||
{
|
||||
std::string name = m_path;
|
||||
name.append(_module->getName().data());
|
||||
//fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
|
||||
name.append(".gz");
|
||||
|
||||
fs::file module_file(name, fs::rewrite);
|
||||
|
||||
if (!module_file)
|
||||
{
|
||||
jit_log.error("LLVM: Failed to create module file: %s (%s)", name, fs::g_tls_error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!zip(obj.getBufferStart(), obj.getBufferSize(), module_file))
|
||||
{
|
||||
jit_log.error("LLVM: Failed to compress module: %s", _module->getName().data());
|
||||
module_file.close();
|
||||
fs::remove_file(name);
|
||||
return;
|
||||
}
|
||||
|
||||
jit_log.notice("LLVM: Created module: %s", _module->getName().data());
|
||||
}
|
||||
|
||||
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
|
||||
{
|
||||
if (fs::file cached{path + ".gz", fs::read})
|
||||
{
|
||||
const std::vector<u8> cached_data = cached.to_vector<u8>();
|
||||
|
||||
if (cached_data.empty()) [[unlikely]]
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<u8> out = unzip(cached_data);
|
||||
|
||||
if (out.empty())
|
||||
{
|
||||
jit_log.error("LLVM: Failed to unzip module: '%s'", path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(out.size());
|
||||
std::memcpy(buf->getBufferStart(), out.data(), out.size());
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (fs::file cached{path, fs::read})
|
||||
{
|
||||
if (cached.size() == 0) [[unlikely]]
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(buf->getBufferStart(), buf->getBufferSize());
|
||||
return buf;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* _module) override
|
||||
{
|
||||
std::string path = m_path;
|
||||
path.append(_module->getName().data());
|
||||
|
||||
if (auto buf = load(path))
|
||||
{
|
||||
jit_log.notice("LLVM: Loaded module: %s", _module->getName().data());
|
||||
return buf;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
std::string jit_compiler::cpu(const std::string& _cpu)
|
||||
{
|
||||
std::string m_cpu = _cpu;
|
||||
|
||||
if (m_cpu.empty())
|
||||
{
|
||||
m_cpu = llvm::sys::getHostCPUName().str();
|
||||
|
||||
if (m_cpu == "sandybridge" ||
|
||||
m_cpu == "ivybridge" ||
|
||||
m_cpu == "haswell" ||
|
||||
m_cpu == "broadwell" ||
|
||||
m_cpu == "skylake" ||
|
||||
m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cascadelake" ||
|
||||
m_cpu == "cooperlake" ||
|
||||
m_cpu == "cannonlake" ||
|
||||
m_cpu == "icelake" ||
|
||||
m_cpu == "icelake-client" ||
|
||||
m_cpu == "icelake-server" ||
|
||||
m_cpu == "tigerlake" ||
|
||||
m_cpu == "rocketlake" ||
|
||||
m_cpu == "alderlake" ||
|
||||
m_cpu == "raptorlake" ||
|
||||
m_cpu == "meteorlake")
|
||||
{
|
||||
// Downgrade if AVX is not supported by some chips
|
||||
if (!utils::has_avx())
|
||||
{
|
||||
m_cpu = "nehalem";
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cascadelake" ||
|
||||
m_cpu == "cooperlake" ||
|
||||
m_cpu == "cannonlake" ||
|
||||
m_cpu == "icelake" ||
|
||||
m_cpu == "icelake-client" ||
|
||||
m_cpu == "icelake-server" ||
|
||||
m_cpu == "tigerlake" ||
|
||||
m_cpu == "rocketlake")
|
||||
{
|
||||
// Downgrade if AVX-512 is disabled or not supported
|
||||
if (!utils::has_avx512())
|
||||
{
|
||||
m_cpu = "skylake";
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cpu == "znver1" && utils::has_clwb())
|
||||
{
|
||||
// Upgrade
|
||||
m_cpu = "znver2";
|
||||
}
|
||||
|
||||
if ((m_cpu == "znver3" || m_cpu == "goldmont" || m_cpu == "alderlake" || m_cpu == "raptorlake" || m_cpu == "meteorlake") && utils::has_avx512_icl())
|
||||
{
|
||||
// Upgrade
|
||||
m_cpu = "icelake-client";
|
||||
}
|
||||
|
||||
if (m_cpu == "goldmont" && utils::has_avx2())
|
||||
{
|
||||
// Upgrade
|
||||
m_cpu = "alderlake";
|
||||
}
|
||||
}
|
||||
|
||||
return m_cpu;
|
||||
}
|
||||
|
||||
std::string jit_compiler::triple1()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
|
||||
#elif defined(__APPLE__) && defined(ARCH_X64)
|
||||
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
|
||||
#elif defined(__APPLE__) && defined(ARCH_ARM64)
|
||||
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
|
||||
#else
|
||||
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string jit_compiler::triple2()
|
||||
{
|
||||
#if defined(_WIN32) && defined(ARCH_X64)
|
||||
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
|
||||
#elif defined(_WIN32) && defined(ARCH_ARM64)
|
||||
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
|
||||
#elif defined(__APPLE__) && defined(ARCH_X64)
|
||||
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
|
||||
#elif defined(__APPLE__) && defined(ARCH_ARM64)
|
||||
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
|
||||
#else
|
||||
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
|
||||
#endif
|
||||
}
|
||||
|
||||
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags)
|
||||
: m_context(new llvm::LLVMContext)
|
||||
, m_cpu(cpu(_cpu))
|
||||
{
|
||||
std::string result;
|
||||
|
||||
auto null_mod = std::make_unique<llvm::Module> ("null_", *m_context);
|
||||
null_mod->setTargetTriple(jit_compiler::triple1());
|
||||
|
||||
std::unique_ptr<llvm::RTDyldMemoryManager> mem;
|
||||
|
||||
if (_link.empty())
|
||||
{
|
||||
// Auxiliary JIT (does not use custom memory manager, only writes the objects)
|
||||
if (flags & 0x1)
|
||||
{
|
||||
mem = std::make_unique<MemoryManager1>();
|
||||
}
|
||||
else
|
||||
{
|
||||
mem = std::make_unique<MemoryManager2>();
|
||||
null_mod->setTargetTriple(jit_compiler::triple2());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mem = std::make_unique<MemoryManager1>();
|
||||
}
|
||||
|
||||
{
|
||||
m_engine.reset(llvm::EngineBuilder(std::move(null_mod))
|
||||
.setErrorStr(&result)
|
||||
.setEngineKind(llvm::EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::move(mem))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
|
||||
#ifdef __APPLE__
|
||||
//.setCodeModel(llvm::CodeModel::Large)
|
||||
#endif
|
||||
.setRelocationModel(llvm::Reloc::Model::PIC_)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
}
|
||||
|
||||
if (!_link.empty())
|
||||
{
|
||||
for (auto&& [name, addr] : _link)
|
||||
{
|
||||
m_engine->updateGlobalMapping(name, addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_link.empty() || !(flags & 0x1))
|
||||
{
|
||||
m_engine->RegisterJITEventListener(llvm::JITEventListener::createIntelJITEventListener());
|
||||
m_engine->RegisterJITEventListener(new JITAnnouncer);
|
||||
}
|
||||
|
||||
if (!m_engine)
|
||||
{
|
||||
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
|
||||
}
|
||||
}
|
||||
|
||||
jit_compiler::~jit_compiler()
|
||||
{
|
||||
}
|
||||
|
||||
void jit_compiler::add(std::unique_ptr<llvm::Module> _module, const std::string& path)
|
||||
{
|
||||
ObjectCache cache{path};
|
||||
m_engine->setObjectCache(&cache);
|
||||
|
||||
const auto ptr = _module.get();
|
||||
m_engine->addModule(std::move(_module));
|
||||
m_engine->generateCodeForModule(ptr);
|
||||
m_engine->setObjectCache(nullptr);
|
||||
|
||||
for (auto& func : ptr->functions())
|
||||
{
|
||||
// Delete IR to lower memory consumption
|
||||
func.deleteBody();
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(std::unique_ptr<llvm::Module> _module)
|
||||
{
|
||||
const auto ptr = _module.get();
|
||||
m_engine->addModule(std::move(_module));
|
||||
m_engine->generateCodeForModule(ptr);
|
||||
|
||||
for (auto& func : ptr->functions())
|
||||
{
|
||||
// Delete IR to lower memory consumption
|
||||
func.deleteBody();
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(const std::string& path)
|
||||
{
|
||||
auto cache = ObjectCache::load(path);
|
||||
|
||||
if (auto object_file = llvm::object::ObjectFile::createObjectFile(*cache))
|
||||
{
|
||||
m_engine->addObjectFile(llvm::object::OwningBinary<llvm::object::ObjectFile>(std::move(*object_file), std::move(cache)));
|
||||
}
|
||||
else
|
||||
{
|
||||
jit_log.error("ObjectCache: Adding failed: %s", path);
|
||||
}
|
||||
}
|
||||
|
||||
bool jit_compiler::check(const std::string& path)
|
||||
{
|
||||
if (auto cache = ObjectCache::load(path))
|
||||
{
|
||||
if (auto object_file = llvm::object::ObjectFile::createObjectFile(*cache))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fs::remove_file(path))
|
||||
{
|
||||
jit_log.error("ObjectCache: Removed damaged file: %s", path);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void jit_compiler::update_global_mapping(const std::string& name, u64 addr)
|
||||
{
|
||||
m_engine->updateGlobalMapping(name, addr);
|
||||
}
|
||||
|
||||
void jit_compiler::fin()
|
||||
{
|
||||
m_engine->finalizeObject();
|
||||
}
|
||||
|
||||
u64 jit_compiler::get(const std::string& name)
|
||||
{
|
||||
return m_engine->getGlobalValueAddress(name);
|
||||
}
|
||||
|
||||
#endif // LLVM_AVAILABLE
|
||||
@@ -35,6 +35,9 @@ std::string wchar_to_utf8(std::wstring_view src)
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996)
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
@@ -52,6 +55,8 @@ std::u16string utf8_to_utf16(std::string_view src)
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
@@ -652,6 +657,11 @@ std::string fmt::to_lower(std::string_view string)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fmt::truncate(std::string_view src, usz length)
|
||||
{
|
||||
return std::string(src.begin(), src.begin() + std::min(src.size(), length));
|
||||
}
|
||||
|
||||
bool fmt::match(const std::string& source, const std::string& mask)
|
||||
{
|
||||
usz source_position = 0, mask_position = 0;
|
||||
|
||||
@@ -178,6 +178,8 @@ namespace fmt
|
||||
std::string to_upper(std::string_view string);
|
||||
std::string to_lower(std::string_view string);
|
||||
|
||||
std::string truncate(std::string_view src, usz length);
|
||||
|
||||
bool match(const std::string& source, const std::string& mask);
|
||||
|
||||
struct buf_to_hexstring
|
||||
|
||||
@@ -46,6 +46,7 @@ DYNAMIC_IMPORT_RENAME("Kernel32.dll", SetThreadDescriptionImport, "SetThreadDesc
|
||||
#include <time.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -2426,7 +2427,7 @@ void thread_ctrl::wait_for_accurate(u64 usec)
|
||||
break;
|
||||
}
|
||||
|
||||
usec = (until - current).count();
|
||||
usec = std::chrono::duration_cast<std::chrono::microseconds>(until - current).count();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+3
-3
@@ -130,6 +130,9 @@ public:
|
||||
|
||||
const native_entry entry_point;
|
||||
|
||||
// Set name for debugger
|
||||
static void set_name(std::string);
|
||||
|
||||
private:
|
||||
// Thread handle (platform-specific)
|
||||
atomic_t<u64> m_thread{0};
|
||||
@@ -160,9 +163,6 @@ private:
|
||||
// Cleanup after possibly deleting the thread instance
|
||||
static native_entry finalize(u64 _self) noexcept;
|
||||
|
||||
// Set name for debugger
|
||||
static void set_name(std::string);
|
||||
|
||||
// Make entry point
|
||||
static native_entry make_trampoline(u64(*entry)(thread_base* _base));
|
||||
|
||||
|
||||
+13
-3
@@ -379,8 +379,8 @@ public:
|
||||
return m_head != 0;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void push(Args&&... args)
|
||||
template <bool Notify = true, typename... Args>
|
||||
bool push(Args&&... args)
|
||||
{
|
||||
auto oldv = m_head.load();
|
||||
auto item = new lf_queue_item<T>(load(oldv), std::forward<Args>(args)...);
|
||||
@@ -390,9 +390,19 @@ public:
|
||||
item->m_link = load(oldv);
|
||||
}
|
||||
|
||||
if (!oldv)
|
||||
if (!oldv && Notify)
|
||||
{
|
||||
// Notify only if queue was empty
|
||||
notify(true);
|
||||
}
|
||||
|
||||
return !oldv;
|
||||
}
|
||||
|
||||
void notify(bool force = false)
|
||||
{
|
||||
if (force || operator bool())
|
||||
{
|
||||
utils::bless<atomic_t<u32>>(&m_head)[1].notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -38,13 +38,13 @@ jobs:
|
||||
displayName: ccache
|
||||
|
||||
- bash: |
|
||||
docker pull --quiet rpcs3/rpcs3-ci-focal:1.5
|
||||
docker pull --quiet rpcs3/rpcs3-ci-focal:1.6
|
||||
docker run \
|
||||
-v $(pwd):/rpcs3 \
|
||||
--env-file .ci/docker.env \
|
||||
-v $CCACHE_DIR:/root/.ccache \
|
||||
-v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \
|
||||
rpcs3/rpcs3-ci-focal:1.5 \
|
||||
rpcs3/rpcs3-ci-focal:1.6 \
|
||||
/rpcs3/.ci/build-linux.sh
|
||||
displayName: Docker setup and build
|
||||
|
||||
@@ -69,9 +69,9 @@ jobs:
|
||||
variables:
|
||||
COMPILER: msvc
|
||||
QT_VER_MAIN: '6'
|
||||
QT_VER: '6.6.1'
|
||||
QT_VER: '6.6.2'
|
||||
QT_VER_MSVC: 'msvc2019'
|
||||
QT_DATE: '202311210527'
|
||||
QT_DATE: '202402121135'
|
||||
QTDIR: C:\Qt\$(QT_VER)\$(QT_VER_MSVC)_64
|
||||
VULKAN_VER: '1.3.268.0'
|
||||
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
@@ -132,7 +132,7 @@ jobs:
|
||||
UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-mac"
|
||||
RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
|
||||
ARTDIR: $(Build.ArtifactStagingDirectory)
|
||||
QT_VER: '6.6.1'
|
||||
QT_VER: '6.6.2'
|
||||
QT_VER_MAIN: '6'
|
||||
|
||||
pool:
|
||||
|
||||
Binary file not shown.
@@ -33,16 +33,17 @@ else()
|
||||
add_compile_options(-fno-exceptions)
|
||||
add_compile_options(-fstack-protector)
|
||||
|
||||
if (COMPILER_X86)
|
||||
add_compile_options(-msse -msse2 -mcx16)
|
||||
endif()
|
||||
|
||||
if (COMPILER_ARM)
|
||||
# This section needs a review. Apple claims armv8.5-a on M-series but doesn't support SVE.
|
||||
# Note that compared to the rest of the 8.x family, 8.1 is very restrictive and we'll have to bump the requirement in future to get anything meaningful.
|
||||
if (APPLE)
|
||||
add_compile_options(-march=armv8.4-a)
|
||||
else()
|
||||
add_compile_options(-march=armv8.1-a)
|
||||
endif()
|
||||
elseif(COMPILER_X86)
|
||||
# Some compilers will set both X86 and ARM, so check explicitly for ARM first
|
||||
add_compile_options(-msse -msse2 -mcx16)
|
||||
endif()
|
||||
|
||||
add_compile_options(-Werror=old-style-cast)
|
||||
@@ -51,6 +52,7 @@ else()
|
||||
add_compile_options(-Werror=return-type)
|
||||
add_compile_options(-Werror=overloaded-virtual)
|
||||
add_compile_options(-Werror=missing-noreturn)
|
||||
add_compile_options(-Werror=implicit-fallthrough)
|
||||
add_compile_options(-Wunused-parameter)
|
||||
add_compile_options(-Wignored-qualifiers)
|
||||
add_compile_options(-Wredundant-move)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#import <Foundation/Foundation.h>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace Darwin_Version
|
||||
{
|
||||
|
||||
@@ -45,7 +45,8 @@ if(WIN32)
|
||||
target_compile_definitions(rpcs3 PRIVATE UNICODE _UNICODE)
|
||||
elseif(APPLE)
|
||||
add_executable(rpcs3 MACOSX_BUNDLE)
|
||||
target_sources(rpcs3 PRIVATE rpcs3.icns)
|
||||
target_sources(rpcs3 PRIVATE rpcs3.icns update_helper.sh)
|
||||
set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set_target_properties(rpcs3
|
||||
PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in")
|
||||
@@ -70,10 +71,12 @@ target_sources(rpcs3
|
||||
Input/dualsense_pad_handler.cpp
|
||||
Input/evdev_joystick_handler.cpp
|
||||
Input/evdev_gun_handler.cpp
|
||||
Input/gui_pad_thread.cpp
|
||||
Input/hid_pad_handler.cpp
|
||||
Input/keyboard_pad_handler.cpp
|
||||
Input/mm_joystick_handler.cpp
|
||||
Input/pad_thread.cpp
|
||||
Input/raw_mouse_handler.cpp
|
||||
Input/sdl_pad_handler.cpp
|
||||
Input/skateboard_pad_handler.cpp
|
||||
Input/xinput_pad_handler.cpp
|
||||
|
||||
@@ -106,7 +106,7 @@ usz decrypt_binaries_t::decrypt(std::string klic_input)
|
||||
case "NPD\0"_u32:
|
||||
{
|
||||
// EDAT / SDAT
|
||||
elf_file = DecryptEDAT(elf_file, old_path, key_it != 0 ? 8 : 1, reinterpret_cast<u8*>(&m_klics[key_it]), true);
|
||||
elf_file = DecryptEDAT(elf_file, old_path, key_it != 0 ? 8 : 1, reinterpret_cast<u8*>(&m_klics[key_it]));
|
||||
|
||||
if (!elf_file)
|
||||
{
|
||||
@@ -139,7 +139,16 @@ usz decrypt_binaries_t::decrypt(std::string klic_input)
|
||||
|
||||
if (fs::file new_file{new_path, fs::rewrite})
|
||||
{
|
||||
new_file.write(elf_file.to_string());
|
||||
// 16MB buffer
|
||||
std::vector<u8> buffer(std::min<usz>(elf_file.size(), 1u << 24));
|
||||
|
||||
elf_file.seek(0);
|
||||
|
||||
while (usz read_size = elf_file.read(buffer.data(), buffer.size()))
|
||||
{
|
||||
new_file.write(buffer.data(), read_size);
|
||||
}
|
||||
|
||||
dec_log.success("Decrypted %s -> %s", old_path, new_path);
|
||||
std::cout << "Decrypted " << old_path << " -> " << new_path << std::endl; // For CLI
|
||||
m_index++;
|
||||
|
||||
+181
-249
@@ -157,37 +157,30 @@ u128 get_block_key(int block, NPD_HEADER *npd)
|
||||
// for out data, allocate a buffer the size of 'edat->block_size'
|
||||
// Also, set 'in file' to the beginning of the encrypted data, which may be offset if inside another file, but normally just reset to beginning of file
|
||||
// returns number of bytes written, -1 for error
|
||||
s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *npd, u8* crypt_key, u32 block_num, u32 total_blocks, u64 size_left)
|
||||
s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *npd, u8* crypt_key, u32 block_num, u32 total_blocks, u64 size_left, bool is_out_buffer_aligned = false)
|
||||
{
|
||||
// Get metadata info and setup buffers.
|
||||
const int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
|
||||
const int metadata_offset = 0x100;
|
||||
|
||||
std::unique_ptr<u8[]> enc_data;
|
||||
std::unique_ptr<u8[]> dec_data;
|
||||
u8 hash[0x10] = { 0 };
|
||||
u8 key_result[0x10] = { 0 };
|
||||
u8 hash_result[0x14] = { 0 };
|
||||
|
||||
u64 offset = 0;
|
||||
u64 metadata_sec_offset = 0;
|
||||
s32 length = 0;
|
||||
u64 length = 0;
|
||||
s32 compression_end = 0;
|
||||
unsigned char empty_iv[0x10] = {};
|
||||
|
||||
const u64 file_offset = in->pos();
|
||||
memset(hash_result, 0, 0x14);
|
||||
|
||||
// Decrypt the metadata.
|
||||
if ((edat->flags & EDAT_COMPRESSED_FLAG) != 0)
|
||||
{
|
||||
metadata_sec_offset = metadata_offset + u64{block_num} * metadata_section_size;
|
||||
|
||||
in->seek(file_offset + metadata_sec_offset);
|
||||
u8 metadata[0x20]{};
|
||||
|
||||
unsigned char metadata[0x20];
|
||||
memset(metadata, 0, 0x20);
|
||||
in->read(metadata, 0x20);
|
||||
in->read_at(metadata_sec_offset, metadata, 0x20);
|
||||
|
||||
// If the data is compressed, decrypt the metadata.
|
||||
// NOTE: For NPD version 1 the metadata is not encrypted.
|
||||
@@ -202,18 +195,17 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
|
||||
std::tie(offset, length, compression_end) = dec_section(metadata);
|
||||
}
|
||||
|
||||
memcpy(hash_result, metadata, 0x10);
|
||||
std::memcpy(hash_result, metadata, 0x10);
|
||||
}
|
||||
else if ((edat->flags & EDAT_FLAG_0x20) != 0)
|
||||
{
|
||||
// If FLAG 0x20, the metadata precedes each data block.
|
||||
metadata_sec_offset = metadata_offset + u64{block_num} * (metadata_section_size + edat->block_size);
|
||||
in->seek(file_offset + metadata_sec_offset);
|
||||
|
||||
unsigned char metadata[0x20];
|
||||
memset(metadata, 0, 0x20);
|
||||
in->read(metadata, 0x20);
|
||||
memcpy(hash_result, metadata, 0x14);
|
||||
u8 metadata[0x20]{};
|
||||
in->read_at(metadata_sec_offset, metadata, 0x20);
|
||||
|
||||
std::memcpy(hash_result, metadata, 0x14);
|
||||
|
||||
// If FLAG 0x20 is set, apply custom xor.
|
||||
for (int j = 0; j < 0x10; j++)
|
||||
@@ -228,9 +220,9 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
|
||||
else
|
||||
{
|
||||
metadata_sec_offset = metadata_offset + u64{block_num} * metadata_section_size;
|
||||
in->seek(file_offset + metadata_sec_offset);
|
||||
|
||||
in->read(hash_result, 0x10);
|
||||
in->read_at(metadata_sec_offset, hash_result, 0x10);
|
||||
|
||||
offset = metadata_offset + u64{block_num} * edat->block_size + total_blocks * metadata_section_size;
|
||||
length = edat->block_size;
|
||||
|
||||
@@ -239,59 +231,82 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
|
||||
}
|
||||
|
||||
// Locate the real data.
|
||||
const int pad_length = length;
|
||||
length = (pad_length + 0xF) & 0xFFFFFFF0;
|
||||
const usz pad_length = length;
|
||||
length = utils::align<usz>(pad_length, 0x10);
|
||||
|
||||
// Setup buffers for decryption and read the data.
|
||||
enc_data.reset(new u8[length]{ 0 });
|
||||
dec_data.reset(new u8[length]{ 0 });
|
||||
memset(hash, 0, 0x10);
|
||||
memset(key_result, 0, 0x10);
|
||||
std::vector<u8> enc_data_buf(is_out_buffer_aligned || length == pad_length ? 0 : length);
|
||||
std::vector<u8> dec_data_buf(length);
|
||||
|
||||
in->seek(file_offset + offset);
|
||||
in->read(enc_data.get(), length);
|
||||
// Try to use out buffer for file reads if no padding is needed instead of a new buffer
|
||||
u8* enc_data = enc_data_buf.empty() ? out : enc_data_buf.data();
|
||||
|
||||
// Variable to avoid copies when possible
|
||||
u8* dec_data = dec_data_buf.data();
|
||||
|
||||
std::memset(hash, 0, 0x10);
|
||||
std::memset(key_result, 0, 0x10);
|
||||
|
||||
in->read_at(offset, enc_data, length);
|
||||
|
||||
// Generate a key for the current block.
|
||||
auto b_key = get_block_key(block_num, npd);
|
||||
|
||||
// Encrypt the block key with the crypto key.
|
||||
aesecb128_encrypt(crypt_key, reinterpret_cast<uchar*>(&b_key), key_result);
|
||||
|
||||
if ((edat->flags & EDAT_FLAG_0x10) != 0)
|
||||
{
|
||||
aesecb128_encrypt(crypt_key, key_result, hash); // If FLAG 0x10 is set, encrypt again to get the final hash.
|
||||
}
|
||||
else
|
||||
memcpy(hash, key_result, 0x10);
|
||||
{
|
||||
std::memcpy(hash, key_result, 0x10);
|
||||
}
|
||||
|
||||
// Setup the crypto and hashing mode based on the extra flags.
|
||||
int crypto_mode = ((edat->flags & EDAT_FLAG_0x02) == 0) ? 0x2 : 0x1;
|
||||
int hash_mode;
|
||||
|
||||
if ((edat->flags & EDAT_FLAG_0x10) == 0)
|
||||
if ((edat->flags & EDAT_FLAG_0x10) == 0)
|
||||
hash_mode = 0x02;
|
||||
else if ((edat->flags & EDAT_FLAG_0x20) == 0)
|
||||
hash_mode = 0x04;
|
||||
else
|
||||
hash_mode = 0x01;
|
||||
|
||||
if ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) != 0)
|
||||
if ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) != 0)
|
||||
{
|
||||
crypto_mode |= 0x10000000;
|
||||
hash_mode |= 0x10000000;
|
||||
}
|
||||
|
||||
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
|
||||
const bool should_decompress = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0) && compression_end;
|
||||
|
||||
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
|
||||
{
|
||||
// Reset the flags.
|
||||
crypto_mode |= 0x01000000;
|
||||
hash_mode |= 0x01000000;
|
||||
|
||||
// Simply copy the data without the header or the footer.
|
||||
memcpy(dec_data.get(), enc_data.get(), length);
|
||||
if (should_decompress)
|
||||
{
|
||||
std::memcpy(dec_data, enc_data, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Optimize when decompression is not needed by avoiding 2 copies
|
||||
dec_data = enc_data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// IV is null if NPD version is 1 or 0.
|
||||
u8* iv = (npd->version <= 1) ? empty_iv : npd->digest;
|
||||
|
||||
// Call main crypto routine on this data block.
|
||||
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), enc_data.get(), dec_data.get(), length, key_result, iv, hash, hash_result))
|
||||
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), enc_data, dec_data, length, key_result, iv, hash, hash_result))
|
||||
{
|
||||
edat_log.error("Block at offset 0x%llx has invalid hash!", offset);
|
||||
return -1;
|
||||
@@ -299,9 +314,9 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
|
||||
}
|
||||
|
||||
// Apply additional de-compression if needed and write the decrypted data.
|
||||
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0) && compression_end)
|
||||
if (should_decompress)
|
||||
{
|
||||
const int res = decompress(out, dec_data.get(), edat->block_size);
|
||||
const int res = decompress(out, dec_data, edat->block_size);
|
||||
|
||||
size_left -= res;
|
||||
|
||||
@@ -313,42 +328,20 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(out, dec_data.get(), pad_length);
|
||||
return pad_length;
|
||||
}
|
||||
}
|
||||
|
||||
// EDAT/SDAT decryption.
|
||||
// reset file to beginning of data before calling
|
||||
int decrypt_data(const fs::file* in, const fs::file* out, EDAT_HEADER *edat, NPD_HEADER *npd, unsigned char* crypt_key, bool /*verbose*/)
|
||||
{
|
||||
const int total_blocks = static_cast<int>((edat->file_size + edat->block_size - 1) / edat->block_size);
|
||||
u64 size_left = edat->file_size;
|
||||
std::unique_ptr<u8[]> data(new u8[edat->block_size]);
|
||||
|
||||
for (int i = 0; i < total_blocks; i++)
|
||||
if (dec_data != out)
|
||||
{
|
||||
in->seek(0);
|
||||
memset(data.get(), 0, edat->block_size);
|
||||
u64 res = decrypt_block(in, data.get(), edat, npd, crypt_key, i, total_blocks, size_left);
|
||||
if (res == umax)
|
||||
{
|
||||
edat_log.error("Decrypt Block failed!");
|
||||
return 1;
|
||||
}
|
||||
size_left -= res;
|
||||
out->write(data.get(), res);
|
||||
std::memcpy(out, dec_data, pad_length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return pad_length;
|
||||
}
|
||||
|
||||
// set file offset to beginning before calling
|
||||
int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs::file* f, bool verbose)
|
||||
bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f, bool verbose)
|
||||
{
|
||||
u8 header[0xA0] = { 0 };
|
||||
u8 empty_header[0xA0] = { 0 };
|
||||
@@ -363,7 +356,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
if (edat->flags & 0x7EFFFFFE)
|
||||
{
|
||||
edat_log.error("Bad header flags!");
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (npd->version == 2)
|
||||
@@ -371,7 +364,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
if (edat->flags & 0x7EFFFFE0)
|
||||
{
|
||||
edat_log.error("Bad header flags!");
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((npd->version == 3) || (npd->version == 4))
|
||||
@@ -379,13 +372,13 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
if (edat->flags & 0x7EFFFFC0)
|
||||
{
|
||||
edat_log.error("Bad header flags!");
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
edat_log.error("Unknown version!");
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read in the file header.
|
||||
@@ -421,7 +414,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
|
||||
{
|
||||
edat_log.error("RAP/RIF/KLIC key is invalid!");
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,7 +428,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
|
||||
if (!edat->block_size)
|
||||
{
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
const usz block_num = utils::aligned_div<u64>(edat->file_size, edat->block_size);
|
||||
@@ -445,7 +438,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
|
||||
if (utils::add_saturate<u64>(utils::add_saturate<u64>(file_offset, metadata_section_offset), metadata_size) > f->size())
|
||||
{
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 bytes_read = 0;
|
||||
@@ -455,10 +448,10 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
while (bytes_read < metadata_size)
|
||||
{
|
||||
// Locate the metadata blocks.
|
||||
f->seek(file_offset + metadata_section_offset);
|
||||
const usz offset = file_offset + metadata_section_offset;
|
||||
|
||||
// Read in the metadata.
|
||||
f->read(metadata.get() + bytes_read, metadata_section_size);
|
||||
f->read_at(offset, metadata.get() + bytes_read, metadata_section_size);
|
||||
|
||||
// Adjust sizes.
|
||||
bytes_read += metadata_section_size;
|
||||
@@ -492,15 +485,17 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
ecdsa_set_pub(VSH_PUB);
|
||||
|
||||
// Read in the metadata and header signatures.
|
||||
f->seek(file_offset + 0xB0);
|
||||
f->seek(0xB0);
|
||||
f->read(metadata_signature, 0x28);
|
||||
f->read(header_signature, 0x28);
|
||||
|
||||
// Checking metadata signature.
|
||||
// Setup signature r and s.
|
||||
memcpy(signature_r + 01, metadata_signature, 0x14);
|
||||
memcpy(signature_s + 01, metadata_signature + 0x14, 0x14);
|
||||
if ((!memcmp(signature_r, zero_buf, 0x15)) || (!memcmp(signature_s, zero_buf, 0x15)))
|
||||
signature_r[0] = 0;
|
||||
signature_s[0] = 0;
|
||||
std::memcpy(signature_r + 1, metadata_signature, 0x14);
|
||||
std::memcpy(signature_s + 1, metadata_signature + 0x14, 0x14);
|
||||
if ((!std::memcmp(signature_r, zero_buf, 0x15)) || (!std::memcmp(signature_s, zero_buf, 0x15)))
|
||||
{
|
||||
edat_log.warning("Metadata signature is invalid!");
|
||||
}
|
||||
@@ -510,10 +505,12 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
if ((edat->flags & EDAT_FLAG_0x20) != 0) //Sony failed again, they used buffer from 0x100 with half size of real metadata.
|
||||
{
|
||||
const usz metadata_buf_size = block_num * 0x10;
|
||||
std::unique_ptr<u8[]> metadata_buf(new u8[metadata_buf_size]);
|
||||
f->seek(file_offset + metadata_offset);
|
||||
f->read(metadata_buf.get(), metadata_buf_size);
|
||||
sha1(metadata_buf.get(), metadata_buf_size, signature_hash);
|
||||
|
||||
std::vector<u8> metadata_buf(metadata_buf_size);
|
||||
|
||||
f->read_at(file_offset + metadata_offset, metadata_buf.data(), metadata_buf_size);
|
||||
|
||||
sha1(metadata_buf.data(), metadata_buf_size, signature_hash);
|
||||
}
|
||||
else
|
||||
sha1(metadata.get(), metadata_size, signature_hash);
|
||||
@@ -528,22 +525,23 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
|
||||
// Checking header signature.
|
||||
// Setup header signature r and s.
|
||||
memset(signature_r, 0, 0x15);
|
||||
memset(signature_s, 0, 0x15);
|
||||
memcpy(signature_r + 01, header_signature, 0x14);
|
||||
memcpy(signature_s + 01, header_signature + 0x14, 0x14);
|
||||
signature_r[0] = 0;
|
||||
signature_s[0] = 0;
|
||||
std::memcpy(signature_r + 1, header_signature, 0x14);
|
||||
std::memcpy(signature_s + 1, header_signature + 0x14, 0x14);
|
||||
|
||||
if ((!memcmp(signature_r, zero_buf, 0x15)) || (!memcmp(signature_s, zero_buf, 0x15)))
|
||||
if ((!std::memcmp(signature_r, zero_buf, 0x15)) || (!std::memcmp(signature_s, zero_buf, 0x15)))
|
||||
{
|
||||
edat_log.warning("Header signature is invalid!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Setup header signature hash.
|
||||
memset(signature_hash, 0, 20);
|
||||
u8 header_buf[0xD8];
|
||||
f->seek(file_offset);
|
||||
f->read(header_buf, 0xD8);
|
||||
std::memset(signature_hash, 0, 20);
|
||||
|
||||
u8 header_buf[0xD8]{};
|
||||
|
||||
f->read_at(file_offset, header_buf, 0xD8);
|
||||
sha1(header_buf, 0xD8, signature_hash);
|
||||
|
||||
if (!ecdsa_verify(signature_hash, signature_r, signature_s))
|
||||
@@ -551,7 +549,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
|
||||
@@ -585,21 +583,25 @@ bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
|
||||
return cmac_hash_compare(reinterpret_cast<uchar*>(&key), 0x10, dev, 0x60, npd->dev_hash, 0x10);
|
||||
}
|
||||
|
||||
bool validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *npd, EDAT_HEADER* edat, bool verbose)
|
||||
bool validate_npd_hashes(std::string_view file_name, const u8* klicensee, NPD_HEADER* npd, EDAT_HEADER* edat, bool verbose)
|
||||
{
|
||||
if (!file_name)
|
||||
{
|
||||
fmt::throw_exception("Empty filename");
|
||||
}
|
||||
|
||||
// Ignore header validation in DEBUG data.
|
||||
if (edat->flags & EDAT_DEBUG_DATA_FLAG)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const usz file_name_length = std::strlen(file_name);
|
||||
const usz buf_len = 0x30 + file_name_length;
|
||||
if (!validate_dev_klic(klicensee, npd))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_name.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const usz buf_len = 0x30 + file_name.size();
|
||||
|
||||
std::unique_ptr<u8[]> buf(new u8[buf_len]);
|
||||
std::unique_ptr<u8[]> buf_lower(new u8[buf_len]);
|
||||
@@ -607,12 +609,12 @@ bool validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER
|
||||
|
||||
// Build the title buffer (content_id + file_name).
|
||||
std::memcpy(buf.get(), npd->content_id, 0x30);
|
||||
std::memcpy(buf.get() + 0x30, file_name, file_name_length);
|
||||
std::memcpy(buf.get() + 0x30, file_name.data(), file_name.size());
|
||||
|
||||
std::memcpy(buf_lower.get(), buf.get(), buf_len);
|
||||
std::memcpy(buf_upper.get(), buf.get(), buf_len);
|
||||
|
||||
for (usz i = std::basic_string_view<u8>(buf.get() + 0x30, file_name_length).find_last_of('.'); i < buf_len; i++)
|
||||
for (usz i = std::basic_string_view<u8>(buf.get() + 0x30, file_name.size()).find_last_of('.'); i < buf_len; i++)
|
||||
{
|
||||
const u8 c = static_cast<u8>(buf[i]);
|
||||
buf_upper[i] = std::toupper(c);
|
||||
@@ -634,17 +636,17 @@ bool validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER
|
||||
edat_log.warning("NPD title hash is invalid!");
|
||||
}
|
||||
|
||||
const bool dev_hash_result = validate_dev_klic(klicensee, npd);
|
||||
|
||||
return title_hash_result && dev_hash_result;
|
||||
return title_hash_result;
|
||||
}
|
||||
|
||||
void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& EDAT)
|
||||
{
|
||||
char npd_header[0x80];
|
||||
char edat_header[0x10];
|
||||
input->read(npd_header, sizeof(npd_header));
|
||||
input->read(edat_header, sizeof(edat_header));
|
||||
char npd_header[0x80]{};
|
||||
char edat_header[0x10]{};
|
||||
|
||||
usz pos = input->pos();
|
||||
pos += input->read_at(pos, npd_header, sizeof(npd_header));
|
||||
input->read_at(pos, edat_header, sizeof(edat_header));
|
||||
|
||||
std::memcpy(&NPD.magic, npd_header, 4);
|
||||
NPD.version = read_from_ptr<be_t<s32>>(npd_header, 4);
|
||||
@@ -662,124 +664,6 @@ void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& E
|
||||
EDAT.file_size = read_from_ptr<be_t<u64>>(edat_header, 8);
|
||||
}
|
||||
|
||||
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;
|
||||
EDAT_HEADER EDAT;
|
||||
|
||||
// Read in the NPD and EDAT/SDAT headers.
|
||||
read_npd_edat_header(input, NPD, EDAT);
|
||||
|
||||
if (NPD.magic != "NPD\0"_u32)
|
||||
{
|
||||
edat_log.error("%s has invalid NPD header or already decrypted.", input_file_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
edat_log.notice("NPD HEADER");
|
||||
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.
|
||||
u128 key{};
|
||||
|
||||
// Check EDAT/SDAT flag.
|
||||
if ((EDAT.flags & SDAT_FLAG) == SDAT_FLAG)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
edat_log.notice("SDAT HEADER");
|
||||
edat_log.notice("SDAT flags: 0x%08X", EDAT.flags);
|
||||
edat_log.notice("SDAT block size: 0x%08X", EDAT.block_size);
|
||||
edat_log.notice("SDAT file size: 0x%08X", EDAT.file_size);
|
||||
}
|
||||
|
||||
// Generate SDAT key.
|
||||
key = std::bit_cast<u128>(NPD.dev_hash) ^ std::bit_cast<u128>(SDAT_KEY);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
edat_log.notice("EDAT HEADER");
|
||||
edat_log.notice("EDAT flags: 0x%08X", EDAT.flags);
|
||||
edat_log.notice("EDAT block size: 0x%08X", EDAT.block_size);
|
||||
edat_log.notice("EDAT file size: 0x%08X", EDAT.file_size);
|
||||
}
|
||||
|
||||
// Perform header validation (EDAT only).
|
||||
char real_file_name[CRYPTO_MAX_PATH];
|
||||
extract_file_name(input_file_name, real_file_name);
|
||||
if (!validate_npd_hashes(real_file_name, devklic, &NPD, &EDAT, verbose))
|
||||
{
|
||||
edat_log.error("NPD hash validation failed!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Select EDAT key.
|
||||
if ((NPD.license & 0x3) == 0x3) // Type 3: Use supplied devklic.
|
||||
{
|
||||
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! (license=%d)", NPD.license);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
edat_log.notice("RIFKEY: %s", std::bit_cast<be_t<u128>>(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
be_t<u128> data;
|
||||
|
||||
std::memcpy(&data, devklic, sizeof(data));
|
||||
edat_log.notice("DEVKLIC: %s", data);
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
edat_log.notice("DECRYPTION KEY: %s", std::bit_cast<be_t<u128>>(key));
|
||||
}
|
||||
|
||||
input->seek(0);
|
||||
if (check_data(reinterpret_cast<uchar*>(&key), &EDAT, &NPD, input, verbose))
|
||||
{
|
||||
edat_log.error("Data parsing failed!");
|
||||
return true;
|
||||
}
|
||||
|
||||
input->seek(0);
|
||||
if (decrypt_data(input, output, &EDAT, &NPD, reinterpret_cast<uchar*>(&key), verbose))
|
||||
{
|
||||
edat_log.error("Data decryption failed!");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file)
|
||||
{
|
||||
u128 rapkey{};
|
||||
@@ -814,7 +698,7 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& inpu
|
||||
}
|
||||
|
||||
// Perform header validation (EDAT only).
|
||||
char real_file_name[CRYPTO_MAX_PATH];
|
||||
char real_file_name[CRYPTO_MAX_PATH]{};
|
||||
extract_file_name(input_file_name.c_str(), real_file_name);
|
||||
if (!validate_npd_hashes(real_file_name, custom_klic, &NPD, &EDAT, false))
|
||||
{
|
||||
@@ -834,7 +718,7 @@ 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, u8 *custom_klic, bool verbose)
|
||||
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic)
|
||||
{
|
||||
if (!input)
|
||||
{
|
||||
@@ -890,25 +774,29 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
|
||||
}
|
||||
|
||||
// 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), verbose))
|
||||
auto data = std::make_unique<EDATADecrypter>(input, devklic, input_file_name, false);
|
||||
|
||||
if (!data->ReadHeader())
|
||||
{
|
||||
output.release();
|
||||
return fs::file{};
|
||||
}
|
||||
|
||||
output.seek(0);
|
||||
fs::file output;
|
||||
output.reset(std::move(data));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
bool EDATADecrypter::ReadHeader()
|
||||
{
|
||||
edata_file.seek(0);
|
||||
|
||||
// Read in the NPD and EDAT/SDAT headers.
|
||||
read_npd_edat_header(&edata_file, npdHeader, edatHeader);
|
||||
|
||||
if (npdHeader.magic != "NPD\0"_u32)
|
||||
{
|
||||
edat_log.error("Not an NPDRM file");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -920,25 +808,56 @@ bool EDATADecrypter::ReadHeader()
|
||||
}
|
||||
else
|
||||
{
|
||||
// verify key
|
||||
if (!validate_dev_klic(reinterpret_cast<uchar*>(&dec_key), &npdHeader))
|
||||
// extract key from RIF
|
||||
char real_file_name[CRYPTO_MAX_PATH]{};
|
||||
extract_file_name(m_file_name.c_str(), real_file_name);
|
||||
|
||||
if (!validate_npd_hashes(real_file_name, reinterpret_cast<const u8*>(&dec_key), &npdHeader, &edatHeader, false))
|
||||
{
|
||||
edat_log.error("Failed validating klic");
|
||||
return false;
|
||||
edat_log.error("NPD hash validation failed!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use provided dec_key
|
||||
// Select EDAT key.
|
||||
if (m_is_key_final)
|
||||
{
|
||||
// Already provided
|
||||
}
|
||||
// Type 3: Use supplied dec_key.
|
||||
else if ((npdHeader.license & 0x3) == 0x3)
|
||||
{
|
||||
//
|
||||
}
|
||||
// Type 2: Use key from RAP file (RIF key). (also used for type 1 at the moment)
|
||||
else
|
||||
{
|
||||
const std::string rap_path = rpcs3::utils::get_rap_file_path(npdHeader.content_id);
|
||||
|
||||
if (fs::file rap{rap_path}; rap && rap.size() >= sizeof(dec_key))
|
||||
{
|
||||
dec_key = GetEdatRifKeyFromRapFile(rap);
|
||||
}
|
||||
|
||||
// Make sure we don't have an empty RIF key.
|
||||
if (!dec_key)
|
||||
{
|
||||
edat_log.error("A valid RAP file is needed for this EDAT file! (license=%d)", npdHeader.license);
|
||||
return true;
|
||||
}
|
||||
|
||||
edat_log.trace("RIFKEY: %s", std::bit_cast<be_t<u128>>(dec_key));
|
||||
}
|
||||
}
|
||||
|
||||
edata_file.seek(0);
|
||||
|
||||
// k the ecdsa_verify function in this check_data function takes a ridiculous amount of time
|
||||
// like it slows down load time by a factor of x20, at least, so its ignored for now
|
||||
|
||||
/*if (check_data(dec_key._bytes, &edatHeader, &npdHeader, &sdata_file, false))
|
||||
{
|
||||
return false;
|
||||
}*/
|
||||
//if (!check_data(reinterpret_cast<u8*>(&dec_key), &edatHeader, &npdHeader, &edata_file, false))
|
||||
//{
|
||||
// edat_log.error("NPDRM check_data() failed!");
|
||||
// return false;
|
||||
//}
|
||||
|
||||
file_size = edatHeader.file_size;
|
||||
total_blocks = ::narrow<u32>(utils::aligned_div(edatHeader.file_size, edatHeader.block_size));
|
||||
@@ -948,6 +867,7 @@ bool EDATADecrypter::ReadHeader()
|
||||
|
||||
if (file_size && !ReadData(0, data_sample, 1))
|
||||
{
|
||||
edat_log.error("NPDRM ReadData() failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -967,28 +887,40 @@ u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
|
||||
const u64 startOffset = pos % edatHeader.block_size;
|
||||
|
||||
const u64 num_blocks = utils::aligned_div(startOffset + size, edatHeader.block_size);
|
||||
data_buf.resize(num_blocks * edatHeader.block_size);
|
||||
|
||||
// Find and decrypt block range covering pos + size
|
||||
const u32 starting_block = ::narrow<u32>(pos / edatHeader.block_size);
|
||||
const u32 ending_block = ::narrow<u32>(std::min<u64>(starting_block + num_blocks, total_blocks));
|
||||
|
||||
u64 writeOffset = 0;
|
||||
|
||||
for (u32 i = starting_block; i < ending_block; ++i)
|
||||
std::vector<u8> data_buf(edatHeader.block_size + 16);
|
||||
|
||||
for (u32 i = starting_block; i < ending_block; i++)
|
||||
{
|
||||
edata_file.seek(0);
|
||||
u64 res = decrypt_block(&edata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, reinterpret_cast<uchar*>(&dec_key), i, total_blocks, edatHeader.file_size);
|
||||
u64 res = decrypt_block(&edata_file, data_buf.data(), &edatHeader, &npdHeader, reinterpret_cast<uchar*>(&dec_key), i, total_blocks, edatHeader.file_size, true);
|
||||
|
||||
if (res == umax)
|
||||
{
|
||||
edat_log.error("Error Decrypting data");
|
||||
return 0;
|
||||
}
|
||||
|
||||
writeOffset += res;
|
||||
const usz skip_start = (i == starting_block ? startOffset : 0);
|
||||
|
||||
if (skip_start >= res)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const usz end_pos = (i != total_blocks - 1 ? edatHeader.block_size : (edatHeader.file_size - 1) % edatHeader.block_size + 1);
|
||||
const usz read_end = std::min<usz>(res, i == ending_block - 1 ? std::min<usz>(end_pos, (startOffset + size - 1) % edatHeader.block_size + 1) : end_pos);
|
||||
|
||||
std::memcpy(data + writeOffset, data_buf.data() + skip_start, read_end - skip_start);
|
||||
std::memset(data_buf.data(), 0, read_end - skip_start);
|
||||
|
||||
writeOffset += read_end - skip_start;
|
||||
}
|
||||
|
||||
const u64 bytesWrote = std::min<u64>(writeOffset - startOffset, size);
|
||||
|
||||
memcpy(data, &data_buf[startOffset], bytesWrote);
|
||||
return bytesWrote;
|
||||
return writeOffset;
|
||||
}
|
||||
|
||||
+25
-7
@@ -18,6 +18,7 @@ struct loaded_npdrm_keys
|
||||
{
|
||||
atomic_t<u128> dec_keys[16]{};
|
||||
atomic_t<u64> dec_keys_pos = 0;
|
||||
u128 one_time_key{}; // For savestates
|
||||
atomic_t<u32> npdrm_fds{0};
|
||||
|
||||
void install_decryption_key(u128 key)
|
||||
@@ -61,7 +62,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, u8 *custom_klic, bool verbose);
|
||||
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic);
|
||||
|
||||
extern void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& EDAT);
|
||||
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, NPD_HEADER *npd_out = nullptr);
|
||||
@@ -71,7 +72,10 @@ u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file);
|
||||
struct EDATADecrypter final : fs::file_base
|
||||
{
|
||||
// file stream
|
||||
fs::file edata_file;
|
||||
fs::file m_edata_file;
|
||||
const fs::file& edata_file;
|
||||
std::string m_file_name;
|
||||
bool m_is_key_final = true;
|
||||
u64 file_size{0};
|
||||
u32 total_blocks{0};
|
||||
u64 pos{0};
|
||||
@@ -79,14 +83,23 @@ struct EDATADecrypter final : fs::file_base
|
||||
NPD_HEADER npdHeader{};
|
||||
EDAT_HEADER edatHeader{};
|
||||
|
||||
// Internal data buffers.
|
||||
std::vector<u8> data_buf{};
|
||||
|
||||
u128 dec_key{};
|
||||
|
||||
public:
|
||||
EDATADecrypter(fs::file&& input, u128 dec_key = {})
|
||||
: edata_file(std::move(input))
|
||||
EDATADecrypter(fs::file&& input, u128 dec_key = {}, std::string file_name = {}, bool is_key_final = true) noexcept
|
||||
: m_edata_file(std::move(input))
|
||||
, edata_file(m_edata_file)
|
||||
, m_file_name(std::move(file_name))
|
||||
, m_is_key_final(is_key_final)
|
||||
, dec_key(dec_key)
|
||||
{
|
||||
}
|
||||
|
||||
EDATADecrypter(const fs::file& input, u128 dec_key = {}, std::string file_name = {}, bool is_key_final = true) noexcept
|
||||
: m_edata_file(fs::file{})
|
||||
, edata_file(input)
|
||||
, m_file_name(std::move(file_name))
|
||||
, m_is_key_final(is_key_final)
|
||||
, dec_key(dec_key)
|
||||
{
|
||||
}
|
||||
@@ -150,4 +163,9 @@ public:
|
||||
id.type.insert(0, "EDATADecrypter: "sv);
|
||||
return id;
|
||||
}
|
||||
|
||||
u128 get_key() const
|
||||
{
|
||||
return dec_key;
|
||||
}
|
||||
};
|
||||
|
||||
+178
-23
@@ -862,10 +862,12 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
|
||||
return true;
|
||||
}
|
||||
|
||||
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose = false);
|
||||
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic);
|
||||
|
||||
void package_reader::extract_worker(thread_key thread_data_key)
|
||||
{
|
||||
std::vector<u8> read_cache;
|
||||
|
||||
while (m_num_failures == 0 && !m_aborted)
|
||||
{
|
||||
// Make sure m_entry_indexer does not exceed m_install_entries
|
||||
@@ -941,42 +943,195 @@ void package_reader::extract_worker(thread_key thread_data_key)
|
||||
pkg_log.warning("NPDRM EDAT!");
|
||||
}
|
||||
|
||||
if (fs::file out = is_buffered ? fs::make_stream<std::vector<u8>>() : fs::file{ path, did_overwrite ? fs::rewrite : fs::write_new })
|
||||
if (fs::file out{ path, did_overwrite ? fs::rewrite : fs::write_new })
|
||||
{
|
||||
bool extract_success = true;
|
||||
for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
|
||||
|
||||
struct pkg_file_reader : fs::file_base
|
||||
{
|
||||
const u64 block_size = std::min<u64>(BUF_SIZE, entry.file_size - pos);
|
||||
const std::function<u64(u64, void*, u64)> m_read_func;
|
||||
const install_entry& m_entry;
|
||||
usz m_pos;
|
||||
|
||||
const std::span<const char> data_span = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), thread_data_key);
|
||||
|
||||
if (data_span.size() != block_size)
|
||||
explicit pkg_file_reader(std::function<u64(u64, void* buffer, u64)> read_func, const install_entry& entry) noexcept
|
||||
: m_read_func(std::move(read_func))
|
||||
, m_entry(entry)
|
||||
, m_pos(0)
|
||||
{
|
||||
extract_success = false;
|
||||
pkg_log.error("Failed to extract file %s (data_span.size=%d, block_size=%d)", path, data_span.size(), block_size);
|
||||
break;
|
||||
}
|
||||
|
||||
if (out.write(data_span.data(), block_size) != block_size)
|
||||
fs::stat_t get_stat() override
|
||||
{
|
||||
extract_success = false;
|
||||
pkg_log.error("Failed to write file %s (error=%s)", path, fs::g_tls_error);
|
||||
break;
|
||||
fs::stat_t stat{};
|
||||
stat.size = m_entry.file_size;
|
||||
return stat;
|
||||
}
|
||||
|
||||
m_written_bytes += block_size;
|
||||
}
|
||||
bool trunc(u64) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 read(void* buffer, u64 size) override
|
||||
{
|
||||
const u64 result = pkg_file_reader::read_at(m_pos, buffer, size);
|
||||
m_pos += result;
|
||||
return result;
|
||||
}
|
||||
|
||||
u64 read_at(u64 offset, void* buffer, u64 size) override
|
||||
{
|
||||
return m_read_func(offset, buffer, size);
|
||||
}
|
||||
|
||||
u64 write(const void*, u64) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, fs::seek_mode whence) override
|
||||
{
|
||||
const s64 new_pos =
|
||||
whence == fs::seek_set ? offset :
|
||||
whence == fs::seek_cur ? offset + m_pos :
|
||||
whence == fs::seek_end ? offset + size() : -1;
|
||||
|
||||
if (new_pos < 0)
|
||||
{
|
||||
fs::g_tls_error = fs::error::inval;
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_pos = new_pos;
|
||||
return m_pos;
|
||||
}
|
||||
|
||||
u64 size() override
|
||||
{
|
||||
return m_entry.file_size;
|
||||
}
|
||||
|
||||
fs::file_id get_id() override
|
||||
{
|
||||
fs::file_id id{};
|
||||
|
||||
id.type.insert(0, "pkg_file_reader: "sv);
|
||||
return id;
|
||||
}
|
||||
};
|
||||
|
||||
read_cache.clear();
|
||||
|
||||
auto reader = std::make_unique<pkg_file_reader>([&, cache_off = u64{umax}](usz pos, void* ptr, usz size) mutable -> u64
|
||||
{
|
||||
if (pos >= entry.file_size || !size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = std::min<u64>(entry.file_size - pos, size);
|
||||
|
||||
u64 size_cache_end = 0;
|
||||
u64 read_size = 0;
|
||||
|
||||
// Check if exists in cache
|
||||
if (!read_cache.empty() && cache_off <= pos && pos < cache_off + read_cache.size())
|
||||
{
|
||||
read_size = std::min<u64>(pos + size, cache_off + read_cache.size()) - pos;
|
||||
|
||||
std::memcpy(ptr, read_cache.data() + (pos - cache_off), read_size);
|
||||
pos += read_size;
|
||||
}
|
||||
else if (!read_cache.empty() && cache_off < pos + size && cache_off + read_cache.size() >= pos + size)
|
||||
{
|
||||
size_cache_end = size - (std::max<u64>(cache_off, pos) - pos);
|
||||
|
||||
std::memcpy(static_cast<u8*>(ptr) + (cache_off - pos), read_cache.data(), size_cache_end);
|
||||
size -= size_cache_end;
|
||||
}
|
||||
|
||||
if (pos >= entry.file_size || !size)
|
||||
{
|
||||
return read_size + size_cache_end;
|
||||
}
|
||||
|
||||
// Try to cache for later
|
||||
if (size <= BUF_SIZE && !size_cache_end && !read_size)
|
||||
{
|
||||
const u64 block_size = std::min<u64>({BUF_SIZE, std::max<u64>(size * 5 / 3, 65536), entry.file_size - pos});
|
||||
|
||||
read_cache.resize(block_size);
|
||||
cache_off = pos;
|
||||
|
||||
const std::span<const char> data_span = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), thread_data_key);
|
||||
|
||||
if (data_span.empty())
|
||||
{
|
||||
cache_off = umax;
|
||||
read_cache.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
read_cache.resize(data_span.size());
|
||||
std::memcpy(read_cache.data(), data_span.data(), data_span.size());
|
||||
|
||||
size = std::min<usz>(data_span.size(), size);
|
||||
std::memcpy(ptr, data_span.data(), size);
|
||||
return size;
|
||||
}
|
||||
|
||||
while (read_size < size)
|
||||
{
|
||||
const u64 block_size = std::min<u64>(BUF_SIZE, size - read_size);
|
||||
|
||||
const std::span<const char> data_span = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), thread_data_key);
|
||||
|
||||
if (data_span.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
std::memcpy(static_cast<u8*>(ptr) + read_size, data_span.data(), data_span.size());
|
||||
|
||||
read_size += data_span.size();
|
||||
pos += data_span.size();
|
||||
}
|
||||
|
||||
return read_size + size_cache_end;
|
||||
}, entry);
|
||||
|
||||
fs::file in_data;
|
||||
in_data.reset(std::move(reader));
|
||||
|
||||
fs::file final_data;
|
||||
|
||||
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))
|
||||
{
|
||||
m_num_failures++;
|
||||
pkg_log.error("Failed to create file %s (error=%s)", path, fs::g_tls_error);
|
||||
break;
|
||||
}
|
||||
final_data = DecryptEDAT(in_data, name, 1, reinterpret_cast<u8*>(&m_header.klicensee));
|
||||
}
|
||||
else
|
||||
{
|
||||
final_data = std::move(in_data);
|
||||
}
|
||||
|
||||
if (!final_data)
|
||||
{
|
||||
m_num_failures++;
|
||||
pkg_log.error("Failed to decrypt EDAT file %s (error=%s)", path, fs::g_tls_error);
|
||||
break;
|
||||
}
|
||||
|
||||
// 16MB buffer
|
||||
std::vector<u8> buffer(std::min<usz>(entry.file_size, 1u << 24));
|
||||
|
||||
while (usz read_size = final_data.read(buffer.data(), buffer.size()))
|
||||
{
|
||||
out.write(buffer.data(), read_size);
|
||||
m_written_bytes += read_size;
|
||||
}
|
||||
|
||||
final_data.close();
|
||||
out.close();
|
||||
|
||||
if (extract_success)
|
||||
{
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
#include <Windows.h>
|
||||
#include <system_error>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "xaudio2_9redist.lib")
|
||||
#endif
|
||||
|
||||
#ifndef XAUDIO2_USE_DEFAULT_PROCESSOR
|
||||
#define XAUDIO2_USE_DEFAULT_PROCESSOR XAUDIO2_DEFAULT_PROCESSOR
|
||||
#endif
|
||||
|
||||
@@ -8,12 +8,8 @@
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <xaudio2redist.h>
|
||||
#else
|
||||
#include <initguid.h>
|
||||
#include <xaudio2.h>
|
||||
#endif
|
||||
#include <wrl/client.h>
|
||||
#include <MMDeviceAPI.h>
|
||||
|
||||
|
||||
+20
-30
@@ -56,7 +56,8 @@ target_sources(rpcs3_emu PRIVATE
|
||||
../../Utilities/cond.cpp
|
||||
../../Utilities/Config.cpp
|
||||
../../Utilities/File.cpp
|
||||
../../Utilities/JIT.cpp
|
||||
../../Utilities/JITASM.cpp
|
||||
../../Utilities/JITLLVM.cpp
|
||||
../../Utilities/LUrlParser.cpp
|
||||
../../Utilities/mutex.cpp
|
||||
../../Utilities/rXml.cpp
|
||||
@@ -79,29 +80,15 @@ target_link_libraries(rpcs3_emu
|
||||
PUBLIC
|
||||
3rdparty::pugixml)
|
||||
|
||||
if(MSVC)
|
||||
set_source_files_properties("../../Utilities/JIT.cpp" PROPERTIES
|
||||
COMPILE_FLAGS /GR-
|
||||
SKIP_PRECOMPILE_HEADERS ON
|
||||
)
|
||||
else()
|
||||
set_source_files_properties("../../Utilities/JIT.cpp" PROPERTIES
|
||||
COMPILE_FLAGS -fno-rtti
|
||||
SKIP_PRECOMPILE_HEADERS ON
|
||||
)
|
||||
endif()
|
||||
set_source_files_properties("../../Utilities/JITLLVM.cpp" "../../Utilities/JITASM.cpp" PROPERTIES
|
||||
COMPILE_FLAGS "$<IF:$<CXX_COMPILER_ID:MSVC>,/GR-,-fno-rtti>"
|
||||
SKIP_PRECOMPILE_HEADERS ON
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
set_source_files_properties("../util/yaml.cpp" PROPERTIES
|
||||
COMPILE_FLAGS /EHsc
|
||||
SKIP_PRECOMPILE_HEADERS ON
|
||||
)
|
||||
else()
|
||||
set_source_files_properties("../util/yaml.cpp" PROPERTIES
|
||||
COMPILE_FLAGS -fexceptions
|
||||
SKIP_PRECOMPILE_HEADERS ON
|
||||
)
|
||||
endif()
|
||||
set_source_files_properties("../util/yaml.cpp" PROPERTIES
|
||||
COMPILE_FLAGS "$<IF:$<CXX_COMPILER_ID:MSVC>,/EHsc,-fexceptions>"
|
||||
SKIP_PRECOMPILE_HEADERS ON
|
||||
)
|
||||
|
||||
# Crypto
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
@@ -154,12 +141,8 @@ if(USE_FAUDIO)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_include_directories(rpcs3_emu PUBLIC "${PROJECT_SOURCE_DIR}/3rdparty/XAudio2Redist/include")
|
||||
if(MSVC)
|
||||
target_link_libraries(rpcs3_emu PRIVATE "${PROJECT_SOURCE_DIR}/3rdparty/XAudio2Redist/libs/xaudio2_9redist.lib")
|
||||
target_link_options(rpcs3_emu PRIVATE /DELAYLOAD:xaudio2_9redist.dll)
|
||||
else()
|
||||
target_link_libraries(rpcs3_emu PRIVATE xaudio2_8)
|
||||
if(NOT MSVC)
|
||||
target_link_libraries(rpcs3_emu PRIVATE xaudio2_9)
|
||||
endif()
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Audio/XAudio2/XAudio2Backend.cpp
|
||||
@@ -199,7 +182,8 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Cell/SPUASMJITRecompiler.cpp
|
||||
Cell/SPUDisAsm.cpp
|
||||
Cell/SPUInterpreter.cpp
|
||||
Cell/SPURecompiler.cpp
|
||||
Cell/SPUCommonRecompiler.cpp
|
||||
Cell/SPULLVMRecompiler.cpp
|
||||
Cell/SPUThread.cpp
|
||||
Cell/lv2/lv2.cpp
|
||||
Cell/lv2/sys_bdemu.cpp
|
||||
@@ -359,6 +343,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Cell/Modules/sceNpCommerce2.cpp
|
||||
Cell/Modules/sceNp.cpp
|
||||
Cell/Modules/sceNpMatchingInt.cpp
|
||||
Cell/Modules/sceNpPlus.cpp
|
||||
Cell/Modules/sceNpSns.cpp
|
||||
Cell/Modules/sceNpTrophy.cpp
|
||||
Cell/Modules/sceNpTus.cpp
|
||||
@@ -425,6 +410,8 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Io/midi_config_types.cpp
|
||||
Io/RB3MidiKeyboard.cpp
|
||||
Io/RB3MidiGuitar.cpp
|
||||
Io/RB3MidiDrums.cpp
|
||||
Io/rb3drums_config.cpp
|
||||
Io/Buzz.cpp
|
||||
Io/Turntable.cpp
|
||||
Io/usio.cpp
|
||||
@@ -483,6 +470,8 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/Overlays/HomeMenu/overlay_home_menu_message_box.cpp
|
||||
RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp
|
||||
RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp
|
||||
RSX/Overlays/Network/overlay_recvmessage_dialog.cpp
|
||||
RSX/Overlays/Network/overlay_sendmessage_dialog.cpp
|
||||
RSX/Overlays/overlay_animated_icon.cpp
|
||||
RSX/Overlays/overlay_animation.cpp
|
||||
RSX/Overlays/overlay_controls.cpp
|
||||
@@ -525,6 +514,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/GL/glutils/program.cpp
|
||||
RSX/GL/glutils/ring_buffer.cpp
|
||||
RSX/GL/glutils/sampler.cpp
|
||||
RSX/GL/upscalers/fsr1/fsr_pass.cpp
|
||||
RSX/GL/GLCommonDecompiler.cpp
|
||||
RSX/GL/GLCompute.cpp
|
||||
RSX/GL/GLDraw.cpp
|
||||
|
||||
@@ -952,7 +952,7 @@ bool cpu_thread::check_state() noexcept
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state0 & cpu_flag::yield && cpu_flag::wait - state0 && cpu_can_stop)
|
||||
if (state0 & cpu_flag::yield && cpu_can_stop)
|
||||
{
|
||||
if (auto spu = try_get<spu_thread>())
|
||||
{
|
||||
@@ -964,6 +964,14 @@ bool cpu_thread::check_state() noexcept
|
||||
}
|
||||
else if (auto ppu = try_get<ppu_thread>())
|
||||
{
|
||||
if (u32 usec = ppu->hw_sleep_time)
|
||||
{
|
||||
thread_ctrl::wait_for_accurate(usec);
|
||||
ppu->hw_sleep_time = 0;
|
||||
ppu->raddr = 0; // Also lose reservation if there is any (reservation is unsaved on hw thread switch)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ppu->raddr && ppu->rtime == vm::reservation_acquire(ppu->raddr))
|
||||
{
|
||||
// Same
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
// Helper function
|
||||
llvm::Value* peek_through_bitcasts(llvm::Value*);
|
||||
|
||||
enum class i2 : char
|
||||
{
|
||||
};
|
||||
@@ -147,7 +150,7 @@ struct llvm_value_t
|
||||
|
||||
std::tuple<> match(llvm::Value*& value, llvm::Module*) const
|
||||
{
|
||||
if (value != this->value)
|
||||
if (peek_through_bitcasts(value) != peek_through_bitcasts(this->value))
|
||||
{
|
||||
value = nullptr;
|
||||
}
|
||||
@@ -503,9 +506,6 @@ using llvm_common_t = std::enable_if_t<(is_llvm_expr_of<T, Types>::ok && ...), t
|
||||
template <typename... Args>
|
||||
using llvm_match_tuple = decltype(std::tuple_cat(std::declval<llvm_expr_t<Args>&>().match(std::declval<llvm::Value*&>(), nullptr)...));
|
||||
|
||||
// Helper function
|
||||
llvm::Value* peek_through_bitcasts(llvm::Value*);
|
||||
|
||||
template <typename T, typename U = llvm_common_t<llvm_value_t<T>>>
|
||||
struct llvm_match_t
|
||||
{
|
||||
@@ -532,7 +532,7 @@ struct llvm_match_t
|
||||
|
||||
std::tuple<> match(llvm::Value*& value, llvm::Module*) const
|
||||
{
|
||||
if (value != this->value)
|
||||
if (peek_through_bitcasts(value) != peek_through_bitcasts(this->value))
|
||||
{
|
||||
value = nullptr;
|
||||
}
|
||||
@@ -685,6 +685,18 @@ struct llvm_add
|
||||
return std::tuple_cat(r1, r2);
|
||||
}
|
||||
}
|
||||
|
||||
v1 = i->getOperand(0);
|
||||
v2 = i->getOperand(1);
|
||||
|
||||
// Argument order does not matter here, try when swapped
|
||||
if (auto r1 = a1.match(v2, _m); v2)
|
||||
{
|
||||
if (auto r2 = a2.match(v1, _m); v1)
|
||||
{
|
||||
return std::tuple_cat(r1, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = nullptr;
|
||||
@@ -858,6 +870,18 @@ struct llvm_mul
|
||||
return std::tuple_cat(r1, r2);
|
||||
}
|
||||
}
|
||||
|
||||
v1 = i->getOperand(0);
|
||||
v2 = i->getOperand(1);
|
||||
|
||||
// Argument order does not matter here, try when swapped
|
||||
if (auto r1 = a1.match(v2, _m); v2)
|
||||
{
|
||||
if (auto r2 = a2.match(v1, _m); v1)
|
||||
{
|
||||
return std::tuple_cat(r1, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = nullptr;
|
||||
@@ -2239,6 +2263,18 @@ struct llvm_add_sat
|
||||
return std::tuple_cat(r1, r2);
|
||||
}
|
||||
}
|
||||
|
||||
v1 = i->getOperand(0);
|
||||
v2 = i->getOperand(1);
|
||||
|
||||
// Argument order does not matter here, try when swapped
|
||||
if (auto r1 = a1.match(v2, _m); v2)
|
||||
{
|
||||
if (auto r2 = a2.match(v1, _m); v1)
|
||||
{
|
||||
return std::tuple_cat(r1, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = nullptr;
|
||||
@@ -2868,6 +2904,22 @@ struct llvm_fmuladd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v1 = i->getOperand(0);
|
||||
v2 = i->getOperand(1);
|
||||
v3 = i->getOperand(2);
|
||||
|
||||
// With multiplication args swapped
|
||||
if (auto r1 = a1.match(v2, _m); v2)
|
||||
{
|
||||
if (auto r2 = a2.match(v1, _m); v1)
|
||||
{
|
||||
if (auto r3 = a3.match(v3, _m); v3)
|
||||
{
|
||||
return std::tuple_cat(r1, r2, r3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = nullptr;
|
||||
@@ -2884,6 +2936,18 @@ struct llvm_calli
|
||||
|
||||
std::tuple<llvm_expr_t<A>...> a;
|
||||
|
||||
std::array<usz, sizeof...(A)> order_equality_hint = []()
|
||||
{
|
||||
std::array<usz, sizeof...(A)> r{};
|
||||
|
||||
for (usz i = 0; i < r.size(); i++)
|
||||
{
|
||||
r[i] = i;
|
||||
}
|
||||
|
||||
return r;
|
||||
}();
|
||||
|
||||
llvm::Value*(*c)(llvm::Value**, llvm::IRBuilder<>*){};
|
||||
|
||||
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||
@@ -2917,6 +2981,13 @@ struct llvm_calli
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Args> requires (sizeof...(Args) == sizeof...(A))
|
||||
llvm_calli& set_order_equality_hint(Args... args)
|
||||
{
|
||||
order_equality_hint = {static_cast<usz>(args)...};
|
||||
return *this;
|
||||
}
|
||||
|
||||
llvm_match_tuple<A...> match(llvm::Value*& value, llvm::Module* _m) const
|
||||
{
|
||||
return match(value, _m, std::make_index_sequence<sizeof...(A)>());
|
||||
@@ -2939,6 +3010,20 @@ struct llvm_calli
|
||||
{
|
||||
return std::tuple_cat(std::get<I>(r)...);
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(A) >= 2)
|
||||
{
|
||||
if (order_equality_hint[0] == order_equality_hint[1])
|
||||
{
|
||||
// Test if it works with the first pair swapped
|
||||
((v[I <= 1 ? I ^ 1 : I] = i->getOperand(I)), ...);
|
||||
|
||||
if (((std::get<I>(r) = std::get<I>(a).match(v[I], _m), v[I]) && ...))
|
||||
{
|
||||
return std::tuple_cat(std::get<I>(r)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ public:
|
||||
#endif
|
||||
AVCodecContext* ctx = nullptr;
|
||||
AVFormatContext* fmt = nullptr;
|
||||
u8* io_buf;
|
||||
u8* io_buf = nullptr;
|
||||
|
||||
struct AudioReader
|
||||
{
|
||||
@@ -371,17 +371,16 @@ public:
|
||||
}
|
||||
if (ctx)
|
||||
{
|
||||
avcodec_close(ctx);
|
||||
avformat_close_input(&fmt);
|
||||
avcodec_free_context(&ctx);
|
||||
}
|
||||
if (io_buf)
|
||||
{
|
||||
av_freep(&io_buf);
|
||||
}
|
||||
if (fmt)
|
||||
{
|
||||
if (io_buf)
|
||||
{
|
||||
av_free(io_buf);
|
||||
}
|
||||
if (fmt->pb) av_free(fmt->pb);
|
||||
avformat_free_context(fmt);
|
||||
if (fmt->pb) av_freep(&fmt->pb);
|
||||
avformat_close_input(&fmt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +450,11 @@ public:
|
||||
}
|
||||
|
||||
AVPacket* packet = av_packet_alloc();
|
||||
std::unique_ptr<AVPacket, decltype([](AVPacket* p){av_packet_unref(p);})> packet_(packet);
|
||||
std::unique_ptr<AVPacket, decltype([](AVPacket* p)
|
||||
{
|
||||
av_packet_unref(p);
|
||||
av_packet_free(&p);
|
||||
})> packet_(packet);
|
||||
|
||||
if (just_started && just_finished)
|
||||
{
|
||||
|
||||
+1560
-152
File diff suppressed because it is too large
Load Diff
@@ -94,12 +94,30 @@ enum
|
||||
CELL_FONT_TYPE_SEURAT_CAPIE_MARU_GOTHIC_VAGR2_SET = 0x00300224,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CELL_FONT_LIBRARY_TYPE_NONE = 0
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CELL_FONT_MAP_FONT = 0,
|
||||
CELL_FONT_MAP_UNICODE = 1,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CELL_FONT_OPEN_MODE_DEFAULT = 0,
|
||||
CELL_FONT_OPEN_MODE_IGNORE_VERTICAL_METRICS = 1,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CELL_FONT_GRAPHICS_DRAW_TYPE_MONO = 0,
|
||||
CELL_FONT_GRAPHICS_DRAW_TYPE_COLOR = 1,
|
||||
CELL_FONT_GRAPHICS_DRAW_TYPE_COLOR_REVERSE = 2,
|
||||
};
|
||||
|
||||
//Custom enum to determine the origin of a CellFont object
|
||||
enum
|
||||
{
|
||||
@@ -109,6 +127,8 @@ enum
|
||||
CELL_FONT_OPEN_MEMORY,
|
||||
};
|
||||
|
||||
constexpr f32 CELL_FONT_GLYPH_OUTLINE_CONTROL_DISTANCE_DEFAULT = 0.125f;
|
||||
|
||||
|
||||
using CellFontMallocCallback = vm::ptr<void>(vm::ptr<void> arg, u32 size);
|
||||
using CellFontFreeCallback = void(vm::ptr<void> arg, vm::ptr<void> ptr);
|
||||
@@ -148,7 +168,7 @@ struct CellFontLibrary
|
||||
{
|
||||
be_t<u32> libraryType;
|
||||
be_t<u32> libraryVersion;
|
||||
// ...
|
||||
vm::bptr<u32> SystemClosed;
|
||||
};
|
||||
|
||||
struct CellFontType
|
||||
@@ -171,6 +191,24 @@ struct CellFontVerticalLayout
|
||||
be_t<f32> effectWidth;
|
||||
};
|
||||
|
||||
struct CellFontVertexesGlyphSubHeader
|
||||
{
|
||||
be_t<u32> size;
|
||||
be_t<f32> SystemReserved[11];
|
||||
};
|
||||
|
||||
struct CellFontVertexesGlyphData
|
||||
{
|
||||
const be_t<u32> size;
|
||||
vm::bptr<f32> SystemClosed;
|
||||
};
|
||||
|
||||
struct CellFontVertexesGlyph
|
||||
{
|
||||
vm::bptr<CellFontVertexesGlyphData> data;
|
||||
vm::bptr<CellFontVertexesGlyphSubHeader> subHeader;
|
||||
};
|
||||
|
||||
struct CellFontGlyphMetrics
|
||||
{
|
||||
be_t<f32> width;
|
||||
@@ -185,6 +223,64 @@ struct CellFontGlyphMetrics
|
||||
be_t<f32> v_advance;
|
||||
};
|
||||
|
||||
struct CellFontGlyphOutline
|
||||
{
|
||||
be_t<s16> contoursCount;
|
||||
be_t<s16> pointsCount;
|
||||
|
||||
struct Point
|
||||
{
|
||||
be_t<f32> x;
|
||||
be_t<f32> y;
|
||||
};
|
||||
vm::bptr<Point> points;
|
||||
vm::bptr<u8> pointTags;
|
||||
vm::bptr<u16> contourIndexs;
|
||||
|
||||
be_t<u32> flags;
|
||||
|
||||
vm::bptr<void> generateEnv;
|
||||
};
|
||||
|
||||
using CellFontGetOutlineVertexCallback = vm::ptr<void>(vm::ptr<void> arg, s32 contourN, s32 vertexNumber, s32 vertexAttr, f32 x, f32 y);
|
||||
|
||||
struct CellFontGetOutlineVertexesIF
|
||||
{
|
||||
vm::bptr<CellFontGetOutlineVertexCallback> callback;
|
||||
vm::bptr<void> arg;
|
||||
};
|
||||
|
||||
struct CellFontGlyphBoundingBox
|
||||
{
|
||||
be_t<f32> min_x;
|
||||
be_t<f32> min_y;
|
||||
be_t<f32> max_x;
|
||||
be_t<f32> max_y;
|
||||
};
|
||||
|
||||
struct CellFontKerning
|
||||
{
|
||||
be_t<f32> offsetX;
|
||||
be_t<f32> offsetY;
|
||||
};
|
||||
|
||||
struct CellFontGlyphStyle
|
||||
{
|
||||
be_t<f32> scale_widthPixel;
|
||||
be_t<f32> scale_heightPixel;
|
||||
be_t<f32> effect_weight;
|
||||
be_t<f32> effect_slant;
|
||||
};
|
||||
|
||||
struct CellFontGlyph
|
||||
{
|
||||
be_t<u16> CF_type;
|
||||
be_t<u16> type;
|
||||
be_t<u32> size;
|
||||
CellFontGlyphMetrics Metrics;
|
||||
CellFontGlyphOutline Outline;
|
||||
};
|
||||
|
||||
struct CellFontRenderSurface
|
||||
{
|
||||
vm::bptr<void> buffer;
|
||||
@@ -235,11 +331,16 @@ struct CellFontRendererConfig
|
||||
|
||||
struct CellFontRenderer
|
||||
{
|
||||
void *systemReserved[64];
|
||||
vm::bptr<void> systemReserved[64];
|
||||
};
|
||||
|
||||
struct CellFontGraphics
|
||||
{
|
||||
be_t<u32> graphicsType;
|
||||
// ...
|
||||
vm::bptr<uint32_t> SystemClosed;
|
||||
};
|
||||
|
||||
struct CellFontGraphicsDrawContext
|
||||
{
|
||||
vm::bptr<void> SystemReserved[64];
|
||||
};
|
||||
|
||||
@@ -5,30 +5,67 @@
|
||||
|
||||
LOG_CHANNEL(cellFontFT);
|
||||
|
||||
error_code cellFontInitLibraryFreeType()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellFontInitLibraryFreeTypeWithRevision(u64 revisionFlags, vm::ptr<CellFontLibraryConfigFT> config, vm::pptr<CellFontLibrary> lib)
|
||||
{
|
||||
cellFontFT.warning("cellFontInitLibraryFreeTypeWithRevision(revisionFlags=0x%llx, config=*0x%x, lib=**0x%x)", revisionFlags, config, lib);
|
||||
cellFontFT.todo("cellFontInitLibraryFreeTypeWithRevision(revisionFlags=0x%llx, config=*0x%x, lib=**0x%x)", revisionFlags, config, lib);
|
||||
|
||||
if (!lib)
|
||||
{
|
||||
return CELL_FONT_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*lib = {};
|
||||
|
||||
if (!config)
|
||||
{
|
||||
return CELL_FONT_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (false) // TODO
|
||||
{
|
||||
return CELL_FONT_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
lib->set(vm::alloc(sizeof(CellFontLibrary), vm::main));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellFontFTGetRevisionFlags()
|
||||
error_code cellFontInitLibraryFreeType(vm::ptr<CellFontLibraryConfigFT> config, vm::pptr<CellFontLibrary> lib)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
cellFontFT.todo("cellFontInitLibraryFreeType(config=*0x%x, lib=**0x%x)", config, lib);
|
||||
|
||||
uint64_t revisionFlags = 0LL;
|
||||
//cellFontFTGetStubRevisionFlags(&revisionFlags);
|
||||
return cellFontInitLibraryFreeTypeWithRevision(revisionFlags, config, lib);
|
||||
}
|
||||
|
||||
error_code cellFontFTGetInitializedRevisionFlags()
|
||||
void cellFontFTGetRevisionFlags(vm::ptr<u64> revisionFlags)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
cellFontFT.notice("cellFontFTGetRevisionFlags(revisionFlags=*0x%x)", revisionFlags);
|
||||
|
||||
if (revisionFlags)
|
||||
{
|
||||
*revisionFlags = 0x43;
|
||||
}
|
||||
}
|
||||
|
||||
error_code cellFontFTGetInitializedRevisionFlags(vm::ptr<u64> revisionFlags)
|
||||
{
|
||||
cellFontFT.notice("cellFontFTGetInitializedRevisionFlags(revisionFlags=*0x%x)", revisionFlags);
|
||||
|
||||
if (!revisionFlags)
|
||||
{
|
||||
return CELL_FONT_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (false) // TODO
|
||||
{
|
||||
return CELL_FONT_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
//*revisionFlags = something; // TODO
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,20 @@ void fmt_class_string<CellGemVideoConvertFormatEnum>::format(std::string& out, u
|
||||
});
|
||||
}
|
||||
|
||||
// last 4 out of 7 ports (7,6,5,4). index starts at 1
|
||||
static u32 port_num(u32 gem_num)
|
||||
{
|
||||
ensure(gem_num < CELL_GEM_MAX_NUM);
|
||||
return CELL_PAD_MAX_PORT_NUM - gem_num;
|
||||
}
|
||||
|
||||
// last 4 out of 7 ports (6,5,4,3). index starts at 0
|
||||
static u32 pad_num(u32 gem_num)
|
||||
{
|
||||
ensure(gem_num < CELL_GEM_MAX_NUM);
|
||||
return (CELL_PAD_MAX_PORT_NUM - 1) - gem_num;
|
||||
}
|
||||
|
||||
// **********************
|
||||
// * HLE helper structs *
|
||||
// **********************
|
||||
@@ -278,12 +292,44 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_connected = false;
|
||||
|
||||
switch (g_cfg.io.move)
|
||||
{
|
||||
case move_handler::fake:
|
||||
case move_handler::mouse:
|
||||
{
|
||||
connected_controllers = 1;
|
||||
connected_controllers = 0;
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
const auto handler = pad::get_current_handler();
|
||||
for (u32 i = 0; i < std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM); i++)
|
||||
{
|
||||
const auto& pad = ::at32(handler->GetPads(), pad_num(i));
|
||||
if (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
connected_controllers++;
|
||||
|
||||
if (gem_num == i)
|
||||
{
|
||||
is_connected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case move_handler::mouse:
|
||||
case move_handler::raw_mouse:
|
||||
{
|
||||
auto& handler = g_fxo->get<MouseHandlerBase>();
|
||||
|
||||
// Make sure that the mouse handler is initialized
|
||||
handler.Init(std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM));
|
||||
|
||||
const MouseInfo& info = handler.GetInfo();
|
||||
connected_controllers = std::min<u32>({ info.now_connect, attribute.max_connect, CELL_GEM_MAX_NUM });
|
||||
if (gem_num < connected_controllers)
|
||||
{
|
||||
is_connected = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
@@ -291,8 +337,13 @@ public:
|
||||
{
|
||||
gun_thread& gun = g_fxo->get<gun_thread>();
|
||||
std::scoped_lock lock(gun.handler.mutex);
|
||||
connected_controllers = gun.handler.init() ? gun.handler.get_num_guns() : 0;
|
||||
gun.num_devices = connected_controllers;
|
||||
gun.num_devices = gun.handler.init() ? gun.handler.get_num_guns() : 0;
|
||||
connected_controllers = std::min<u32>(std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM), gun.num_devices);
|
||||
|
||||
if (gem_num < connected_controllers)
|
||||
{
|
||||
is_connected = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -306,10 +357,10 @@ public:
|
||||
controller.sphere_rgb = gem_color::get_default_color(gem_num);
|
||||
|
||||
// Assign status and port number
|
||||
if (gem_num < connected_controllers)
|
||||
if (is_connected)
|
||||
{
|
||||
controller.status = CELL_GEM_STATUS_READY;
|
||||
controller.port = CELL_PAD_MAX_PORT_NUM - gem_num;
|
||||
controller.port = port_num(gem_num);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,8 +592,8 @@ static bool check_gem_num(const u32 gem_num)
|
||||
|
||||
static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_controller&, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
|
||||
{
|
||||
const u16 x = static_cast<u16>(x_pos / (x_max / static_cast<f32>(rsx::overlays::overlay::virtual_width)));
|
||||
const u16 y = static_cast<u16>(y_pos / (y_max / static_cast<f32>(rsx::overlays::overlay::virtual_height)));
|
||||
const s16 x = static_cast<s16>(x_pos / (x_max / static_cast<f32>(rsx::overlays::overlay::virtual_width)));
|
||||
const s16 y = static_cast<s16>(y_pos / (y_max / static_cast<f32>(rsx::overlays::overlay::virtual_height)));
|
||||
|
||||
// Note: We shouldn't use sphere_rgb here. The game will set it to black in many cases.
|
||||
const gem_config_data::gem_color& rgb = gem_config_data::gem_color::get_default_color(gem_num);
|
||||
@@ -638,12 +689,12 @@ extern bool is_input_allowed();
|
||||
/**
|
||||
* \brief Maps Move controller data (digital buttons, and analog Trigger data) to DS3 pad input.
|
||||
* Unavoidably buttons conflict with DS3 mappings, which is problematic for some games.
|
||||
* \param port_no DS3 port number to use
|
||||
* \param gem_num gem index to use
|
||||
* \param digital_buttons Bitmask filled with CELL_GEM_CTRL_* values
|
||||
* \param analog_t Analog value of Move's Trigger. Currently mapped to R2.
|
||||
* \return true on success, false if port_no controller is invalid
|
||||
* \return true on success, false if controller is disconnected
|
||||
*/
|
||||
static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
|
||||
static void ds3_input_to_pad(const u32 gem_num, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
|
||||
{
|
||||
digital_buttons = 0;
|
||||
analog_t = 0;
|
||||
@@ -656,14 +707,14 @@ static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pad = ::at32(handler->GetPads(), port_no);
|
||||
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& cfg = ::at32(g_cfg_gem.players, port_no);
|
||||
const auto& cfg = ::at32(g_cfg_gem.players, gem_num);
|
||||
cfg->handle_input(pad, true, [&](gem_btn btn, u16 value, bool pressed)
|
||||
{
|
||||
if (!pressed)
|
||||
@@ -707,12 +758,12 @@ static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t
|
||||
constexpr u16 ds3_max_x = 255;
|
||||
constexpr u16 ds3_max_y = 255;
|
||||
|
||||
static inline void ds3_get_stick_values(u32 port_no, const std::shared_ptr<Pad>& pad, s32& x_pos, s32& y_pos)
|
||||
static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr<Pad>& pad, s32& x_pos, s32& y_pos)
|
||||
{
|
||||
x_pos = 0;
|
||||
y_pos = 0;
|
||||
|
||||
const auto& cfg = ::at32(g_cfg_gem.players, port_no);
|
||||
const auto& cfg = ::at32(g_cfg_gem.players, gem_num);
|
||||
cfg->handle_input(pad, true, [&](gem_btn btn, u16 value, bool pressed)
|
||||
{
|
||||
if (!pressed)
|
||||
@@ -733,7 +784,7 @@ static inline void ds3_get_stick_values(u32 port_no, const std::shared_ptr<Pad>&
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_controller& controller, T& gem_state)
|
||||
static void ds3_pos_to_gem_state(u32 gem_num, const gem_config::gem_controller& controller, T& gem_state)
|
||||
{
|
||||
if (!gem_state || !is_input_allowed())
|
||||
{
|
||||
@@ -743,7 +794,7 @@ static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_contro
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pad = ::at32(handler->GetPads(), port_no);
|
||||
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
@@ -751,15 +802,15 @@ static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_contro
|
||||
}
|
||||
|
||||
s32 ds3_pos_x, ds3_pos_y;
|
||||
ds3_get_stick_values(port_no, pad, ds3_pos_x, ds3_pos_y);
|
||||
ds3_get_stick_values(gem_num, pad, ds3_pos_x, ds3_pos_y);
|
||||
|
||||
if constexpr (std::is_same<T, vm::ptr<CellGemState>>::value)
|
||||
{
|
||||
pos_to_gem_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
|
||||
pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
|
||||
}
|
||||
else if constexpr (std::is_same<T, vm::ptr<CellGemImageState>>::value)
|
||||
{
|
||||
pos_to_gem_image_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
|
||||
pos_to_gem_image_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,11 +818,11 @@ static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_contro
|
||||
* \brief Maps external Move controller data to DS3 input. (This can be input from any physical pad, not just the DS3)
|
||||
* Implementation detail: CellGemExtPortData's digital/analog fields map the same way as
|
||||
* libPad, so no translation is needed.
|
||||
* \param port_no DS3 port number to use
|
||||
* \param gem_num gem index to use
|
||||
* \param ext External data to modify
|
||||
* \return true on success, false if port_no controller is invalid
|
||||
* \return true on success, false if controller is disconnected
|
||||
*/
|
||||
static void ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller& controller, CellGemExtPortData& ext)
|
||||
static void ds3_input_to_ext(const u32 gem_num, const gem_config::gem_controller& controller, CellGemExtPortData& ext)
|
||||
{
|
||||
ext = {};
|
||||
|
||||
@@ -783,7 +834,7 @@ static void ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pad = ::at32(handler->GetPads(), port_no);
|
||||
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
@@ -1372,6 +1423,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> gem_imag
|
||||
ds3_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_image_state);
|
||||
break;
|
||||
case move_handler::mouse:
|
||||
case move_handler::raw_mouse:
|
||||
mouse_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_image_state);
|
||||
break;
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
@@ -1426,6 +1478,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
|
||||
ds3_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T);
|
||||
break;
|
||||
case move_handler::mouse:
|
||||
case move_handler::raw_mouse:
|
||||
mouse_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T);
|
||||
break;
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
@@ -1460,6 +1513,33 @@ error_code cellGemGetInfo(vm::ptr<CellGemInfo> info)
|
||||
}
|
||||
|
||||
// TODO: Support connecting PlayStation Move controllers
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake)
|
||||
{
|
||||
gem.connected_controllers = 0;
|
||||
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++)
|
||||
{
|
||||
const auto& pad = ::at32(handler->GetPads(), pad_num(i));
|
||||
const bool connected = (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && i < gem.attribute.max_connect);
|
||||
|
||||
if (connected)
|
||||
{
|
||||
gem.connected_controllers++;
|
||||
gem.controllers[i].status = CELL_GEM_STATUS_READY;
|
||||
gem.controllers[i].port = port_num(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
gem.controllers[i].status = CELL_GEM_STATUS_DISCONNECTED;
|
||||
gem.controllers[i].port = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info->max_connect = gem.attribute.max_connect;
|
||||
info->now_connect = gem.connected_controllers;
|
||||
|
||||
@@ -1603,6 +1683,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
|
||||
ds3_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_state);
|
||||
break;
|
||||
case move_handler::mouse:
|
||||
case move_handler::raw_mouse:
|
||||
mouse_input_to_pad(gem_num, gem_state->pad.digitalbuttons, gem_state->pad.analog_T);
|
||||
mouse_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_state);
|
||||
break;
|
||||
|
||||
@@ -40,7 +40,7 @@ ime_jp_manager::ime_jp_manager()
|
||||
|
||||
bool ime_jp_manager::addChar(u16 c)
|
||||
{
|
||||
if (!c || cursor >= (CELL_IMEJP_STRING_MAXLENGTH - 1) || cursor > input_string.length())
|
||||
if (!c || cursor >= (CELL_IMEJP_STRING_MAXLENGTH - 1ULL) || cursor > input_string.length())
|
||||
return false;
|
||||
|
||||
std::u16string tmp;
|
||||
@@ -49,11 +49,29 @@ bool ime_jp_manager::addChar(u16 c)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wrestrict"
|
||||
#endif
|
||||
input_string.insert(cursor++, tmp);
|
||||
input_string.insert(cursor, tmp);
|
||||
#if defined(__GNUG__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
cursor_end = cursor;
|
||||
|
||||
const usz cursor_old = cursor;
|
||||
const bool cursor_was_in_focus = cursor >= focus_begin && cursor <= (focus_begin + focus_length);
|
||||
|
||||
move_cursor(1);
|
||||
|
||||
if (cursor_was_in_focus)
|
||||
{
|
||||
// Add this char to the focus
|
||||
move_focus_end(1, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's just move the focus to the cursor, so that it contains the new char.
|
||||
focus_begin = cursor_old;
|
||||
focus_length = 1;
|
||||
move_focus(0); // Sanitize focus
|
||||
}
|
||||
|
||||
input_state = CELL_IMEJP_BEFORE_CONVERT;
|
||||
return true;
|
||||
}
|
||||
@@ -74,59 +92,138 @@ bool ime_jp_manager::addString(vm::cptr<u16> str)
|
||||
|
||||
bool ime_jp_manager::backspaceWord()
|
||||
{
|
||||
if (!cursor || cursor > CELL_IMEJP_STRING_MAXLENGTH || cursor > input_string.length())
|
||||
return false;
|
||||
|
||||
input_string.erase(--cursor);
|
||||
cursor_end = cursor;
|
||||
|
||||
if (input_string.empty())
|
||||
input_state = CELL_IMEJP_BEFORE_INPUT;
|
||||
|
||||
return true;
|
||||
return remove_character(false);
|
||||
}
|
||||
|
||||
bool ime_jp_manager::deleteWord()
|
||||
{
|
||||
if (cursor >= (CELL_IMEJP_STRING_MAXLENGTH - 1) || cursor > (input_string.length() - 1))
|
||||
return false;
|
||||
return remove_character(true);
|
||||
}
|
||||
|
||||
input_string.erase(cursor);
|
||||
cursor_end = cursor;
|
||||
bool ime_jp_manager::remove_character(bool forward)
|
||||
{
|
||||
if (!forward && !cursor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const usz pos = forward ? cursor : (cursor - 1);
|
||||
|
||||
if (pos >= (CELL_IMEJP_STRING_MAXLENGTH - 1ULL) || pos >= input_string.length())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete the character at the position
|
||||
input_string.erase(pos, 1);
|
||||
|
||||
// Move cursor and focus
|
||||
const bool deleted_part_of_focus = pos > focus_begin && pos <= (focus_begin + focus_length);
|
||||
|
||||
if (!forward)
|
||||
{
|
||||
move_cursor(-1);
|
||||
}
|
||||
|
||||
if (deleted_part_of_focus)
|
||||
{
|
||||
move_focus_end(-1, false);
|
||||
}
|
||||
else if (focus_begin > pos)
|
||||
{
|
||||
move_focus(-1);
|
||||
}
|
||||
|
||||
if (input_string.empty())
|
||||
{
|
||||
input_state = CELL_IMEJP_BEFORE_INPUT;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ime_jp_manager::moveCursor(s8 amount)
|
||||
void ime_jp_manager::clear_input()
|
||||
{
|
||||
cursor = 0;
|
||||
focus_begin = 0;
|
||||
focus_length = 0;
|
||||
input_string.clear();
|
||||
converted_string.clear();
|
||||
}
|
||||
|
||||
void ime_jp_manager::move_cursor(s8 amount)
|
||||
{
|
||||
cursor = std::max(0, std::min(static_cast<s32>(cursor) + amount, ::narrow<s32>(input_string.length())));
|
||||
}
|
||||
|
||||
void ime_jp_manager::move_focus(s8 amount)
|
||||
{
|
||||
focus_begin = std::max(0, std::min(static_cast<s32>(focus_begin) + amount, ::narrow<s32>(input_string.length())));
|
||||
move_focus_end(amount, false);
|
||||
}
|
||||
|
||||
void ime_jp_manager::move_focus_end(s8 amount, bool wrap_around)
|
||||
{
|
||||
if (focus_begin >= input_string.length())
|
||||
{
|
||||
focus_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr usz min_length = 1;
|
||||
const usz max_length = input_string.length() - focus_begin;
|
||||
|
||||
if (amount > 0)
|
||||
{
|
||||
cursor = std::min(cursor + amount, input_string.length() - 1);
|
||||
cursor_end = std::min(cursor_end + amount, input_string.length() - 1);
|
||||
if (wrap_around && focus_length >= max_length)
|
||||
{
|
||||
focus_length = min_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
focus_length += static_cast<usz>(amount);
|
||||
}
|
||||
}
|
||||
else if (amount < 0)
|
||||
{
|
||||
cursor = std::max(0, static_cast<s32>(cursor) + amount);
|
||||
cursor_end = std::max(static_cast<s32>(cursor), static_cast<s32>(cursor_end) + amount);
|
||||
if (wrap_around && focus_length <= min_length)
|
||||
{
|
||||
focus_length = max_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
focus_length = std::max(0, static_cast<s32>(focus_length) + amount);
|
||||
}
|
||||
}
|
||||
|
||||
focus_length = std::max(min_length, std::min(max_length, focus_length));
|
||||
}
|
||||
|
||||
void ime_jp_manager::moveCursorEnd(s8 amount)
|
||||
std::vector<ime_jp_manager::candidate> ime_jp_manager::get_candidate_list() const
|
||||
{
|
||||
if (amount > 0)
|
||||
{
|
||||
cursor_end = std::max(static_cast<s32>(cursor), std::min<s32>(static_cast<s32>(cursor_end) + amount, ::narrow<s32>(input_string.length()) - 1));
|
||||
}
|
||||
else if (amount < 0)
|
||||
{
|
||||
cursor_end = std::max(static_cast<s32>(cursor), static_cast<s32>(cursor_end) + amount);
|
||||
}
|
||||
std::vector<candidate> candidates;
|
||||
if (input_string.empty() || focus_length == 0 || focus_begin >= input_string.length())
|
||||
return candidates;
|
||||
|
||||
// TODO: we just fake this with one candidate for now
|
||||
candidates.push_back(candidate{
|
||||
.text = get_focus_string(),
|
||||
.offset = 0
|
||||
});
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
error_code cellImeJpOpen(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cptr<CellImeJpAddDic> addDicPath)
|
||||
std::u16string ime_jp_manager::get_focus_string() const
|
||||
{
|
||||
if (input_string.empty() || focus_length == 0 || focus_begin >= input_string.length())
|
||||
return {};
|
||||
|
||||
return input_string.substr(focus_begin, focus_length);
|
||||
}
|
||||
|
||||
|
||||
static error_code cellImeJpOpen(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cptr<CellImeJpAddDic> addDicPath)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpOpen(container_id=*0x%x, hImeJpHandle=*0x%x, addDicPath=*0x%x)", container_id, hImeJpHandle, addDicPath);
|
||||
|
||||
@@ -156,7 +253,7 @@ error_code cellImeJpOpen(sys_memory_container_t container_id, vm::ptr<CellImeJpH
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpOpen2(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cptr<CellImeJpAddDic> addDicPath)
|
||||
static error_code cellImeJpOpen2(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cptr<CellImeJpAddDic> addDicPath)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpOpen2(container_id=*0x%x, hImeJpHandle=*0x%x, addDicPath=*0x%x)", container_id, hImeJpHandle, addDicPath);
|
||||
|
||||
@@ -187,7 +284,7 @@ error_code cellImeJpOpen2(sys_memory_container_t container_id, vm::ptr<CellImeJp
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpOpen3(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cpptr<CellImeJpAddDic> addDicPath)
|
||||
static error_code cellImeJpOpen3(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cpptr<CellImeJpAddDic> addDicPath)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpOpen3(container_id=*0x%x, hImeJpHandle=*0x%x, addDicPath=*0x%x)", container_id, hImeJpHandle, addDicPath);
|
||||
|
||||
@@ -224,13 +321,13 @@ error_code cellImeJpOpen3(sys_memory_container_t container_id, vm::ptr<CellImeJp
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpOpenExt()
|
||||
static error_code cellImeJpOpenExt()
|
||||
{
|
||||
cellImeJp.todo("cellImeJpOpenExt()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpClose(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpClose(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpClose(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -243,17 +340,14 @@ error_code cellImeJpClose(CellImeJpHandle hImeJpHandle)
|
||||
}
|
||||
|
||||
manager.input_state = CELL_IMEJP_BEFORE_INPUT;
|
||||
manager.input_string.clear();
|
||||
manager.converted_string.clear();
|
||||
manager.clear_input();
|
||||
manager.confirmed_string.clear();
|
||||
manager.cursor = 0;
|
||||
manager.cursor_end = 0;
|
||||
manager.is_initialized = false;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpSetKanaInputMode(CellImeJpHandle hImeJpHandle, s16 inputOption)
|
||||
static error_code cellImeJpSetKanaInputMode(CellImeJpHandle hImeJpHandle, s16 inputOption)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpSetKanaInputMode(hImeJpHandle=*0x%x, inputOption=%d)", hImeJpHandle, inputOption);
|
||||
|
||||
@@ -275,7 +369,7 @@ error_code cellImeJpSetKanaInputMode(CellImeJpHandle hImeJpHandle, s16 inputOpti
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpSetInputCharType(CellImeJpHandle hImeJpHandle, s16 charTypeOption)
|
||||
static error_code cellImeJpSetInputCharType(CellImeJpHandle hImeJpHandle, s16 charTypeOption)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpSetInputCharType(hImeJpHandle=*0x%x, charTypeOption=%d)", hImeJpHandle, charTypeOption);
|
||||
|
||||
@@ -292,7 +386,7 @@ error_code cellImeJpSetInputCharType(CellImeJpHandle hImeJpHandle, s16 charTypeO
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpSetFixInputMode(CellImeJpHandle hImeJpHandle, s16 fixInputMode)
|
||||
static error_code cellImeJpSetFixInputMode(CellImeJpHandle hImeJpHandle, s16 fixInputMode)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpSetFixInputMode(hImeJpHandle=*0x%x, fixInputMode=%d)", hImeJpHandle, fixInputMode);
|
||||
|
||||
@@ -309,7 +403,7 @@ error_code cellImeJpSetFixInputMode(CellImeJpHandle hImeJpHandle, s16 fixInputMo
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpAllowExtensionCharacters(CellImeJpHandle hImeJpHandle, s16 extensionCharacters)
|
||||
static error_code cellImeJpAllowExtensionCharacters(CellImeJpHandle hImeJpHandle, s16 extensionCharacters)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpSetFixInputMode(hImeJpHandle=*0x%x, extensionCharacters=%d)", hImeJpHandle, extensionCharacters);
|
||||
|
||||
@@ -331,7 +425,7 @@ error_code cellImeJpAllowExtensionCharacters(CellImeJpHandle hImeJpHandle, s16 e
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpReset(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpReset(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpReset(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -344,16 +438,13 @@ error_code cellImeJpReset(CellImeJpHandle hImeJpHandle)
|
||||
}
|
||||
|
||||
manager.input_state = CELL_IMEJP_BEFORE_INPUT;
|
||||
manager.input_string.clear();
|
||||
manager.converted_string.clear();
|
||||
manager.clear_input();
|
||||
manager.confirmed_string.clear();
|
||||
manager.cursor = 0;
|
||||
manager.cursor_end = 0;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetStatus(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pInputStatus)
|
||||
static error_code cellImeJpGetStatus(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pInputStatus)
|
||||
{
|
||||
cellImeJp.warning("cellImeJpGetStatus(hImeJpHandle=*0x%x, pInputStatus=%d)", hImeJpHandle, pInputStatus);
|
||||
|
||||
@@ -375,7 +466,7 @@ error_code cellImeJpGetStatus(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pInputS
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpEnterChar(CellImeJpHandle hImeJpHandle, u16 inputChar, vm::ptr<s16> pOutputStatus)
|
||||
static error_code cellImeJpEnterChar(CellImeJpHandle hImeJpHandle, u16 inputChar, vm::ptr<s16> pOutputStatus)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpEnterChar(hImeJpHandle=*0x%x, inputChar=%d, pOutputStatus=%d)", hImeJpHandle, inputChar, pOutputStatus);
|
||||
|
||||
@@ -404,13 +495,13 @@ error_code cellImeJpEnterChar(CellImeJpHandle hImeJpHandle, u16 inputChar, vm::p
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpEnterCharExt(CellImeJpHandle hImeJpHandle, u16 inputChar, vm::ptr<s16> pOutputStatus)
|
||||
static error_code cellImeJpEnterCharExt(CellImeJpHandle hImeJpHandle, u16 inputChar, vm::ptr<s16> pOutputStatus)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpEnterCharExt(hImeJpHandle=*0x%x, inputChar=%d, pOutputStatus=%d", hImeJpHandle, inputChar, pOutputStatus);
|
||||
return cellImeJpEnterChar(hImeJpHandle, inputChar, pOutputStatus);
|
||||
}
|
||||
|
||||
error_code cellImeJpEnterString(CellImeJpHandle hImeJpHandle, vm::cptr<u16> pInputString, vm::ptr<s16> pOutputStatus)
|
||||
static error_code cellImeJpEnterString(CellImeJpHandle hImeJpHandle, vm::cptr<u16> pInputString, vm::ptr<s16> pOutputStatus)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpEnterString(hImeJpHandle=*0x%x, pInputString=*0x%x, pOutputStatus=%d", hImeJpHandle, pInputString, pOutputStatus);
|
||||
|
||||
@@ -439,13 +530,13 @@ error_code cellImeJpEnterString(CellImeJpHandle hImeJpHandle, vm::cptr<u16> pInp
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpEnterStringExt(CellImeJpHandle hImeJpHandle, vm::cptr<u16> pInputString, vm::ptr<s16> pOutputStatus)
|
||||
static error_code cellImeJpEnterStringExt(CellImeJpHandle hImeJpHandle, vm::cptr<u16> pInputString, vm::ptr<s16> pOutputStatus)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpEnterStringExt(hImeJpHandle=*0x%x, pInputString=*0x%x, pOutputStatus=%d", hImeJpHandle, pInputString, pOutputStatus);
|
||||
return cellImeJpEnterString(hImeJpHandle, pInputString, pOutputStatus);
|
||||
}
|
||||
|
||||
error_code cellImeJpModeCaretRight(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpModeCaretRight(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpModeCaretRight(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -462,12 +553,12 @@ error_code cellImeJpModeCaretRight(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_IMEJP_ERROR_ERR;
|
||||
}
|
||||
|
||||
manager.moveCursor(1);
|
||||
manager.move_cursor(1);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpModeCaretLeft(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpModeCaretLeft(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpModeCaretLeft(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -484,12 +575,12 @@ error_code cellImeJpModeCaretLeft(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_IMEJP_ERROR_ERR;
|
||||
}
|
||||
|
||||
manager.moveCursor(-1);
|
||||
manager.move_cursor(-1);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpBackspaceWord(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpBackspaceWord(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpBackspaceWord(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -511,7 +602,7 @@ error_code cellImeJpBackspaceWord(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpDeleteWord(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpDeleteWord(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpDeleteWord(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -533,7 +624,7 @@ error_code cellImeJpDeleteWord(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpAllDeleteConvertString(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpAllDeleteConvertString(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpAllDeleteConvertString(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -550,16 +641,13 @@ error_code cellImeJpAllDeleteConvertString(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_IMEJP_ERROR_ERR;
|
||||
}
|
||||
|
||||
manager.cursor = 0;
|
||||
manager.cursor_end = 0;
|
||||
manager.input_string.clear();
|
||||
manager.converted_string.clear();
|
||||
manager.clear_input();
|
||||
manager.input_state = CELL_IMEJP_BEFORE_INPUT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpConvertForward(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpConvertForward(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpConvertForward(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -581,7 +669,7 @@ error_code cellImeJpConvertForward(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpConvertBackward(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpConvertBackward(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpConvertBackward(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -603,7 +691,7 @@ error_code cellImeJpConvertBackward(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpCurrentPartConfirm(CellImeJpHandle hImeJpHandle, s16 listItem)
|
||||
static error_code cellImeJpCurrentPartConfirm(CellImeJpHandle hImeJpHandle, s16 listItem)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpCurrentPartConfirm(hImeJpHandle=*0x%x, listItem=%d)", hImeJpHandle, listItem);
|
||||
|
||||
@@ -623,7 +711,7 @@ error_code cellImeJpCurrentPartConfirm(CellImeJpHandle hImeJpHandle, s16 listIte
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpAllConfirm(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpAllConfirm(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpAllConfirm(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -642,16 +730,13 @@ error_code cellImeJpAllConfirm(CellImeJpHandle hImeJpHandle)
|
||||
|
||||
// Use input_string for now
|
||||
manager.confirmed_string = manager.input_string;
|
||||
manager.cursor = 0;
|
||||
manager.cursor_end = 0;
|
||||
manager.input_string.clear();
|
||||
manager.converted_string.clear();
|
||||
manager.clear_input();
|
||||
manager.input_state = CELL_IMEJP_BEFORE_INPUT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpAllConvertCancel(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpAllConvertCancel(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpAllConvertCancel(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -674,7 +759,7 @@ error_code cellImeJpAllConvertCancel(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpConvertCancel(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpConvertCancel(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpConvertCancel(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -691,11 +776,13 @@ error_code cellImeJpConvertCancel(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_IMEJP_ERROR_ERR;
|
||||
}
|
||||
|
||||
// TODO: only cancel all if cursor is at 0
|
||||
return cellImeJpAllConvertCancel(hImeJpHandle);
|
||||
manager.converted_string.clear();
|
||||
manager.input_state = CELL_IMEJP_BEFORE_CONVERT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpExtendConvertArea(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpExtendConvertArea(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpExtendConvertArea(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -712,12 +799,13 @@ error_code cellImeJpExtendConvertArea(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_IMEJP_ERROR_ERR;
|
||||
}
|
||||
|
||||
manager.moveCursorEnd(1);
|
||||
// Move end of the focus by one. Wrap around if the focus end is already at the end of the input string.
|
||||
manager.move_focus_end(1, true);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpShortenConvertArea(CellImeJpHandle hImeJpHandle)
|
||||
static error_code cellImeJpShortenConvertArea(CellImeJpHandle hImeJpHandle)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpShortenConvertArea(hImeJpHandle=*0x%x)", hImeJpHandle);
|
||||
|
||||
@@ -734,12 +822,13 @@ error_code cellImeJpShortenConvertArea(CellImeJpHandle hImeJpHandle)
|
||||
return CELL_IMEJP_ERROR_ERR;
|
||||
}
|
||||
|
||||
manager.moveCursorEnd(-1);
|
||||
// Move end of focus by one. Wrap around if the focus end is already at the beginning of the input string.
|
||||
manager.move_focus_end(-1, true);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpTemporalConfirm(CellImeJpHandle hImeJpHandle, s16 selectIndex)
|
||||
static error_code cellImeJpTemporalConfirm(CellImeJpHandle hImeJpHandle, s16 selectIndex)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpTemporalConfirm(hImeJpHandle=*0x%x, selectIndex=%d)", hImeJpHandle, selectIndex);
|
||||
|
||||
@@ -759,7 +848,7 @@ error_code cellImeJpTemporalConfirm(CellImeJpHandle hImeJpHandle, s16 selectInde
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpPostConvert(CellImeJpHandle hImeJpHandle, s16 postType)
|
||||
static error_code cellImeJpPostConvert(CellImeJpHandle hImeJpHandle, s16 postType)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpPostConvert(hImeJpHandle=*0x%x, postType=%d)", hImeJpHandle, postType);
|
||||
|
||||
@@ -779,7 +868,7 @@ error_code cellImeJpPostConvert(CellImeJpHandle hImeJpHandle, s16 postType)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpMoveFocusClause(CellImeJpHandle hImeJpHandle, s16 moveType)
|
||||
static error_code cellImeJpMoveFocusClause(CellImeJpHandle hImeJpHandle, s16 moveType)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpMoveFocusClause(hImeJpHandle=*0x%x, moveType=%d)", hImeJpHandle, moveType);
|
||||
|
||||
@@ -799,17 +888,17 @@ error_code cellImeJpMoveFocusClause(CellImeJpHandle hImeJpHandle, s16 moveType)
|
||||
switch (moveType)
|
||||
{
|
||||
case CELL_IMEJP_FOCUS_NEXT:
|
||||
manager.moveCursor(1);
|
||||
manager.move_focus(1);
|
||||
break;
|
||||
case CELL_IMEJP_FOCUS_BEFORE:
|
||||
manager.moveCursor(-1);
|
||||
manager.move_focus(-1);
|
||||
break;
|
||||
case CELL_IMEJP_FOCUS_TOP:
|
||||
manager.moveCursor(-1 * ::narrow<s8>(manager.input_string.length()));
|
||||
manager.move_focus(-1 * ::narrow<s8>(manager.input_string.length()));
|
||||
break;
|
||||
case CELL_IMEJP_FOCUS_END:
|
||||
manager.moveCursor(::narrow<s8>(manager.input_string.length()));
|
||||
manager.moveCursor(-1);
|
||||
manager.move_focus(::narrow<s8>(manager.input_string.length()));
|
||||
manager.move_focus(-1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -820,7 +909,7 @@ error_code cellImeJpMoveFocusClause(CellImeJpHandle hImeJpHandle, s16 moveType)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetFocusTop(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pFocusTop)
|
||||
static error_code cellImeJpGetFocusTop(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pFocusTop)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpGetFocusTop(hImeJpHandle=*0x%x, pFocusTop=*0x%x)", hImeJpHandle, pFocusTop);
|
||||
|
||||
@@ -837,12 +926,12 @@ error_code cellImeJpGetFocusTop(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pFocu
|
||||
return CELL_IMEJP_ERROR_CONTEXT;
|
||||
}
|
||||
|
||||
*pFocusTop = static_cast<u16>(manager.cursor * 2); // offset in bytes
|
||||
*pFocusTop = static_cast<u16>(manager.focus_begin * 2); // offset in bytes
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetFocusLength(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pFocusLength)
|
||||
static error_code cellImeJpGetFocusLength(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pFocusLength)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpGetFocusLength(hImeJpHandle=*0x%x, pFocusLength=*0x%x)", hImeJpHandle, pFocusLength);
|
||||
|
||||
@@ -859,19 +948,12 @@ error_code cellImeJpGetFocusLength(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pF
|
||||
return CELL_IMEJP_ERROR_CONTEXT;
|
||||
}
|
||||
|
||||
if (manager.cursor >= (CELL_IMEJP_STRING_MAXLENGTH - 1))
|
||||
{
|
||||
*pFocusLength = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pFocusLength = static_cast<u16>((manager.cursor_end - manager.cursor + 1) * 2); // offset in bytes
|
||||
}
|
||||
*pFocusLength = ::narrow<s16>(manager.focus_length * 2); // offset in bytes
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetConfirmYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pYomiString)
|
||||
static error_code cellImeJpGetConfirmYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pYomiString)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpGetConfirmYomiString(hImeJpHandle=*0x%x, pYomiString=*0x%x)", hImeJpHandle, pYomiString);
|
||||
|
||||
@@ -888,22 +970,22 @@ error_code cellImeJpGetConfirmYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u
|
||||
return CELL_IMEJP_ERROR_CONTEXT;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < CELL_IMEJP_STRING_MAXLENGTH; i++)
|
||||
{
|
||||
pYomiString[i] = 0;
|
||||
}
|
||||
|
||||
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1, manager.confirmed_string.length());
|
||||
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1ULL, manager.confirmed_string.length());
|
||||
|
||||
for (u32 i = 0; i < max_len; i++)
|
||||
{
|
||||
pYomiString[i] = manager.confirmed_string[i];
|
||||
}
|
||||
|
||||
for (u32 i = static_cast<u32>(max_len); i < CELL_IMEJP_STRING_MAXLENGTH; i++)
|
||||
{
|
||||
pYomiString[i] = 0;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetConfirmString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pConfirmString)
|
||||
static error_code cellImeJpGetConfirmString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pConfirmString)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpGetConfirmString(hImeJpHandle=*0x%x, pConfirmString=*0x%x)", hImeJpHandle, pConfirmString);
|
||||
|
||||
@@ -920,22 +1002,22 @@ error_code cellImeJpGetConfirmString(CellImeJpHandle hImeJpHandle, vm::ptr<u16>
|
||||
return CELL_IMEJP_ERROR_CONTEXT;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < CELL_IMEJP_STRING_MAXLENGTH; i++)
|
||||
{
|
||||
pConfirmString[i] = 0;
|
||||
}
|
||||
|
||||
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1, manager.confirmed_string.length());
|
||||
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1ULL, manager.confirmed_string.length());
|
||||
|
||||
for (u32 i = 0; i < max_len; i++)
|
||||
{
|
||||
pConfirmString[i] = manager.confirmed_string[i];
|
||||
}
|
||||
|
||||
for (u32 i = static_cast<u32>(max_len); i < CELL_IMEJP_STRING_MAXLENGTH; i++)
|
||||
{
|
||||
pConfirmString[i] = 0;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetConvertYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pYomiString)
|
||||
static error_code cellImeJpGetConvertYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pYomiString)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpGetConvertYomiString(hImeJpHandle=*0x%x, pYomiString=*0x%x)", hImeJpHandle, pYomiString);
|
||||
|
||||
@@ -952,22 +1034,22 @@ error_code cellImeJpGetConvertYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u
|
||||
return CELL_IMEJP_ERROR_CONTEXT;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < CELL_IMEJP_STRING_MAXLENGTH; i++)
|
||||
{
|
||||
pYomiString[i] = 0;
|
||||
}
|
||||
|
||||
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1, manager.input_string.length());
|
||||
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1ULL, manager.input_string.length());
|
||||
|
||||
for (u32 i = 0; i < max_len; i++)
|
||||
{
|
||||
pYomiString[i] = manager.input_string[i];
|
||||
}
|
||||
|
||||
for (u32 i = static_cast<u32>(max_len); i < CELL_IMEJP_STRING_MAXLENGTH; i++)
|
||||
{
|
||||
pYomiString[i] = 0;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetConvertString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pConvertString)
|
||||
static error_code cellImeJpGetConvertString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pConvertString)
|
||||
{
|
||||
cellImeJp.warning("cellImeJpGetConvertString(hImeJpHandle=*0x%x, pConvertString=*0x%x)", hImeJpHandle, pConvertString);
|
||||
|
||||
@@ -984,22 +1066,22 @@ error_code cellImeJpGetConvertString(CellImeJpHandle hImeJpHandle, vm::ptr<u16>
|
||||
return CELL_IMEJP_ERROR_CONTEXT;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < CELL_IMEJP_STRING_MAXLENGTH; i++)
|
||||
{
|
||||
pConvertString[i] = 0;
|
||||
}
|
||||
|
||||
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1, manager.input_string.length());
|
||||
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1ULL, manager.input_string.length());
|
||||
|
||||
for (u32 i = 0; i < max_len; i++)
|
||||
{
|
||||
pConvertString[i] = manager.input_string[i];
|
||||
}
|
||||
|
||||
for (u32 i = static_cast<u32>(max_len); i < CELL_IMEJP_STRING_MAXLENGTH; i++)
|
||||
{
|
||||
pConvertString[i] = 0;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetCandidateListSize(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pListSize)
|
||||
static error_code cellImeJpGetCandidateListSize(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pListSize)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpGetCandidateListSize(hImeJpHandle=*0x%x, pListSize=*0x%x)", hImeJpHandle, pListSize);
|
||||
|
||||
@@ -1021,12 +1103,23 @@ error_code cellImeJpGetCandidateListSize(CellImeJpHandle hImeJpHandle, vm::ptr<s
|
||||
return CELL_IMEJP_ERROR_ERR;
|
||||
}
|
||||
|
||||
*pListSize = 0;
|
||||
// Add focus string size, including null terminator
|
||||
const std::u16string focus_string = manager.get_focus_string();
|
||||
usz size = sizeof(u16) * (focus_string.length() + 1);
|
||||
|
||||
// Add candidates, including null terminators and offsets
|
||||
for (const ime_jp_manager::candidate& can : manager.get_candidate_list())
|
||||
{
|
||||
constexpr usz offset_size = sizeof(u16);
|
||||
size += offset_size + (can.text.size() + 1) * sizeof(u16);
|
||||
}
|
||||
|
||||
*pListSize = ::narrow<s16>(size);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetCandidateList(CellImeJpHandle hImeJpHandle, vm::ptr<s16> plistNum, vm::ptr<u16> pCandidateString)
|
||||
static error_code cellImeJpGetCandidateList(CellImeJpHandle hImeJpHandle, vm::ptr<s16> plistNum, vm::ptr<u16> pCandidateString)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpGetCandidateList(hImeJpHandle=*0x%x, plistNum=*0x%x, pCandidateString=*0x%x)", hImeJpHandle, plistNum, pCandidateString);
|
||||
|
||||
@@ -1048,12 +1141,42 @@ error_code cellImeJpGetCandidateList(CellImeJpHandle hImeJpHandle, vm::ptr<s16>
|
||||
return CELL_IMEJP_ERROR_ERR;
|
||||
}
|
||||
|
||||
*plistNum = 0;
|
||||
// First, copy the focus string
|
||||
u32 pos = 0;
|
||||
const std::u16string focus_string = manager.get_focus_string();
|
||||
for (u32 i = pos; i < focus_string.length(); i++)
|
||||
{
|
||||
pCandidateString[i] = focus_string[i];
|
||||
}
|
||||
pos += ::narrow<u32>(focus_string.length());
|
||||
|
||||
// Add null terminator
|
||||
pCandidateString[pos++] = 0;
|
||||
|
||||
// Add list of candidates
|
||||
const std::vector<ime_jp_manager::candidate> list = manager.get_candidate_list();
|
||||
for (const ime_jp_manager::candidate& can : list)
|
||||
{
|
||||
// Copy the candidate
|
||||
for (u32 i = pos; i < can.text.length(); i++)
|
||||
{
|
||||
pCandidateString[i] = can.text[i];
|
||||
}
|
||||
pos += ::narrow<u32>(can.text.length());
|
||||
|
||||
// Add null terminator
|
||||
pCandidateString[pos++] = 0;
|
||||
|
||||
// Add offset
|
||||
pCandidateString[pos++] = can.offset;
|
||||
}
|
||||
|
||||
*plistNum = ::narrow<s16>(list.size());
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetCandidateSelect(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pIndex)
|
||||
static error_code cellImeJpGetCandidateSelect(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pIndex)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpGetCandidateSelect(hImeJpHandle=*0x%x, pIndex=*0x%x)", hImeJpHandle, pIndex);
|
||||
|
||||
@@ -1080,7 +1203,7 @@ error_code cellImeJpGetCandidateSelect(CellImeJpHandle hImeJpHandle, vm::ptr<s16
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpGetPredictList(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pYomiString, s32 itemNum, vm::ptr<s32> plistCount, vm::ptr<CellImeJpPredictItem> pPredictItem)
|
||||
static error_code cellImeJpGetPredictList(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pYomiString, s32 itemNum, vm::ptr<s32> plistCount, vm::ptr<CellImeJpPredictItem> pPredictItem)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpGetPredictList(hImeJpHandle=*0x%x, pYomiString=*0x%x, itemNum=%d, plistCount=*0x%x, pPredictItem=*0x%x)", hImeJpHandle, pYomiString, itemNum, plistCount, pPredictItem);
|
||||
|
||||
@@ -1102,7 +1225,7 @@ error_code cellImeJpGetPredictList(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pY
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellImeJpConfirmPrediction(CellImeJpHandle hImeJpHandle, vm::ptr<CellImeJpPredictItem> pPredictItem)
|
||||
static error_code cellImeJpConfirmPrediction(CellImeJpHandle hImeJpHandle, vm::ptr<CellImeJpPredictItem> pPredictItem)
|
||||
{
|
||||
cellImeJp.todo("cellImeJpConfirmPrediction(hImeJpHandle=*0x%x, pPredictItem=*0x%x)", hImeJpHandle, pPredictItem);
|
||||
|
||||
|
||||
@@ -115,11 +115,12 @@ struct ime_jp_manager
|
||||
|
||||
u32 input_state = CELL_IMEJP_BEFORE_INPUT;
|
||||
std::vector<std::string> dictionary_paths;
|
||||
std::u16string confirmed_string;
|
||||
std::u16string converted_string;
|
||||
std::u16string input_string;
|
||||
usz cursor = 0;
|
||||
usz cursor_end = 0;
|
||||
std::u16string confirmed_string; // Confirmed part of the string (first part of the entire string)
|
||||
std::u16string converted_string; // Converted part of the unconfirmed input string
|
||||
std::u16string input_string; // Unconfirmed part of the string (second part of the entire string)
|
||||
usz cursor = 0; // The cursor. Can move across the entire input string.
|
||||
usz focus_begin = 0; // Begin of the focus string
|
||||
usz focus_length = 0; // Length of the focus string
|
||||
s16 fix_input_mode = CELL_IMEJP_FIXINMODE_OFF;
|
||||
s16 input_char_type = CELL_IMEJP_DSPCHAR_HIRA;
|
||||
s16 kana_input_mode = CELL_IMEJP_ROMAN_INPUT;
|
||||
@@ -131,6 +132,17 @@ struct ime_jp_manager
|
||||
bool addString(vm::cptr<u16> str);
|
||||
bool backspaceWord();
|
||||
bool deleteWord();
|
||||
void moveCursor(s8 amount);
|
||||
void moveCursorEnd(s8 amount);
|
||||
bool remove_character(bool forward);
|
||||
void clear_input();
|
||||
void move_cursor(s8 amount); // s8 because CELL_IMEJP_STRING_MAXLENGTH is 128
|
||||
void move_focus(s8 amount); // s8 because CELL_IMEJP_STRING_MAXLENGTH is 128
|
||||
void move_focus_end(s8 amount, bool wrap_around); // s8 because CELL_IMEJP_STRING_MAXLENGTH is 128
|
||||
|
||||
struct candidate
|
||||
{
|
||||
std::u16string text; // Actual text of the candidate
|
||||
u16 offset = 0; // The offset of the next character after the candidate replaced part of the current focus.
|
||||
};
|
||||
std::vector<candidate> get_candidate_list() const;
|
||||
std::u16string get_focus_string() const;
|
||||
};
|
||||
|
||||
@@ -259,7 +259,7 @@ error_code cellMouseGetData(u32 port_no, vm::ptr<CellMouseData> data)
|
||||
|
||||
error_code cellMouseGetDataList(u32 port_no, vm::ptr<CellMouseDataList> data)
|
||||
{
|
||||
sys_io.notice("cellMouseGetDataList(port_no=%d, data=0x%x)", port_no, data);
|
||||
sys_io.trace("cellMouseGetDataList(port_no=%d, data=0x%x)", port_no, data);
|
||||
|
||||
auto& handler = g_fxo->get<MouseHandlerBase>();
|
||||
|
||||
@@ -406,7 +406,7 @@ error_code cellMouseGetTabletDataList(u32 port_no, vm::ptr<CellMouseTabletDataLi
|
||||
|
||||
error_code cellMouseGetRawData(u32 port_no, vm::ptr<CellMouseRawData> data)
|
||||
{
|
||||
sys_io.notice("cellMouseGetRawData(port_no=%d, data=*0x%x)", port_no, data);
|
||||
sys_io.trace("cellMouseGetRawData(port_no=%d, data=*0x%x)", port_no, data);
|
||||
|
||||
auto& handler = g_fxo->get<MouseHandlerBase>();
|
||||
|
||||
|
||||
@@ -227,6 +227,12 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
|
||||
|
||||
std::memcpy(buf.get_ptr(), &dec.decoder.data[dec.read_pos], size_to_read);
|
||||
|
||||
if (size_to_read < reqSize)
|
||||
{
|
||||
// Set the rest of the buffer to zero to prevent loud pops at the end of the stream if the game ignores the readSize.
|
||||
std::memset(vm::static_ptr_cast<u8>(buf).get_ptr() + size_to_read, 0, reqSize - size_to_read);
|
||||
}
|
||||
|
||||
dec.read_pos += size_to_read;
|
||||
|
||||
s64 start_time_ms = 0;
|
||||
|
||||
+1153
-274
File diff suppressed because it is too large
Load Diff
@@ -24,14 +24,30 @@ enum
|
||||
|
||||
enum CellPamfStreamType
|
||||
{
|
||||
CELL_PAMF_STREAM_TYPE_AVC = 0,
|
||||
CELL_PAMF_STREAM_TYPE_M2V = 1,
|
||||
CELL_PAMF_STREAM_TYPE_ATRAC3PLUS = 2,
|
||||
CELL_PAMF_STREAM_TYPE_PAMF_LPCM = 3,
|
||||
CELL_PAMF_STREAM_TYPE_AC3 = 4,
|
||||
CELL_PAMF_STREAM_TYPE_USER_DATA = 5,
|
||||
CELL_PAMF_STREAM_TYPE_VIDEO = 20,
|
||||
CELL_PAMF_STREAM_TYPE_AUDIO = 21,
|
||||
CELL_PAMF_STREAM_TYPE_AVC = 0,
|
||||
CELL_PAMF_STREAM_TYPE_M2V = 1,
|
||||
CELL_PAMF_STREAM_TYPE_ATRAC3PLUS = 2,
|
||||
CELL_PAMF_STREAM_TYPE_PAMF_LPCM = 3,
|
||||
CELL_PAMF_STREAM_TYPE_AC3 = 4,
|
||||
CELL_PAMF_STREAM_TYPE_USER_DATA = 5,
|
||||
CELL_PAMF_STREAM_TYPE_PSMF_AVC = 6,
|
||||
CELL_PAMF_STREAM_TYPE_PSMF_ATRAC3PLUS = 7,
|
||||
CELL_PAMF_STREAM_TYPE_PSMF_LPCM = 8,
|
||||
CELL_PAMF_STREAM_TYPE_PSMF_USER_DATA = 9,
|
||||
CELL_PAMF_STREAM_TYPE_VIDEO = 20,
|
||||
CELL_PAMF_STREAM_TYPE_AUDIO = 21,
|
||||
CELL_PAMF_STREAM_TYPE_UNK = 22,
|
||||
};
|
||||
|
||||
enum PamfStreamCodingType : u8
|
||||
{
|
||||
PAMF_STREAM_CODING_TYPE_M2V = 0x02,
|
||||
PAMF_STREAM_CODING_TYPE_AVC = 0x1b,
|
||||
PAMF_STREAM_CODING_TYPE_PAMF_LPCM = 0x80,
|
||||
PAMF_STREAM_CODING_TYPE_AC3 = 0x81,
|
||||
PAMF_STREAM_CODING_TYPE_ATRAC3PLUS = 0xdc,
|
||||
PAMF_STREAM_CODING_TYPE_USER_DATA = 0xdd,
|
||||
PAMF_STREAM_CODING_TYPE_PSMF = 0xff,
|
||||
};
|
||||
|
||||
enum
|
||||
@@ -144,7 +160,7 @@ struct CellCodecTimeStamp
|
||||
be_t<u32> lower;
|
||||
};
|
||||
|
||||
static const u64 CODEC_TS_INVALID = 0xffffffffffffffffull;
|
||||
constexpr u32 CODEC_TS_INVALID = umax;
|
||||
|
||||
// Entry point information
|
||||
struct CellPamfEp
|
||||
@@ -155,13 +171,21 @@ struct CellPamfEp
|
||||
be_t<u64> rpnOffset;
|
||||
};
|
||||
|
||||
struct CellPamfEpUnk // Speculative name, only used in two undocumented functions
|
||||
{
|
||||
CellPamfEp ep;
|
||||
be_t<u64> nextRpnOffset;
|
||||
};
|
||||
|
||||
CHECK_SIZE(CellPamfEpUnk, 0x20);
|
||||
|
||||
// Entry point iterator
|
||||
struct CellPamfEpIterator
|
||||
{
|
||||
b8 isPamf;
|
||||
be_t<u32> index;
|
||||
be_t<u32> num;
|
||||
be_t<u32> pCur_addr;
|
||||
vm::bcptr<void> pCur;
|
||||
};
|
||||
|
||||
struct CellCodecEsFilterId
|
||||
@@ -252,18 +276,23 @@ struct CellPamfLpcmInfo
|
||||
be_t<u16> bitsPerSample;
|
||||
};
|
||||
|
||||
CHECK_SIZE(CellPamfLpcmInfo, 8);
|
||||
|
||||
// PAMF file structs, everything here is not aligned (LLE uses exclusively u8 pointers)
|
||||
|
||||
struct PamfStreamHeader
|
||||
{
|
||||
u8 type;
|
||||
u8 unknown[3];
|
||||
u8 fid_major;
|
||||
u8 fid_minor;
|
||||
u8 unknown1;
|
||||
u8 unknown2;
|
||||
be_t<u32> ep_offset; // offset of EP section in header
|
||||
be_t<u32> ep_num; // count of EPs
|
||||
u8 stream_coding_type;
|
||||
|
||||
u8 reserved[3];
|
||||
|
||||
u8 stream_id;
|
||||
u8 private_stream_id; // for streams multiplexed as private data streams (stream_id == 0xbd)
|
||||
|
||||
be_t<u16, 1> p_std_buffer; // 2 bits: unused ??? "00", 1 bit: P_STD_buffer_scale, 13 bits: P_STD_buffer_size
|
||||
|
||||
be_t<u32, 1> ep_offset; // offset of EP section in header
|
||||
be_t<u32, 1> ep_num; // count of EPs
|
||||
|
||||
union
|
||||
{
|
||||
@@ -276,32 +305,20 @@ struct PamfStreamHeader
|
||||
u8 levelIdc;
|
||||
u8 x2; // contains frameMbsOnlyFlag, videoSignalInfoFlag, frameRateInfo
|
||||
u8 aspectRatioIdc;
|
||||
u32 x4; // 0 (not used)
|
||||
be_t<u16> horizontalSize; // divided by 16
|
||||
be_t<u16> verticalSize; // divided by 16
|
||||
be_t<u16> frameCropLeftOffset;
|
||||
be_t<u16> frameCropRightOffset;
|
||||
be_t<u16> frameCropTopOffset;
|
||||
be_t<u16> frameCropBottomOffset;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
be_t<u16> width;
|
||||
be_t<u16> height;
|
||||
}
|
||||
sarInfo;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 x14; // contains videoFormat and videoFullRangeFlag
|
||||
u8 colourPrimaries;
|
||||
u8 transferCharacteristics;
|
||||
u8 matrixCoefficients;
|
||||
};
|
||||
};
|
||||
|
||||
be_t<u16, 1> sarWidth;
|
||||
be_t<u16, 1> sarHeight;
|
||||
u8 reserved1;
|
||||
u8 horizontalSize; // divided by 16
|
||||
u8 reserved2;
|
||||
u8 verticalSize; // divided by 16
|
||||
be_t<u16, 1> frameCropLeftOffset;
|
||||
be_t<u16, 1> frameCropRightOffset;
|
||||
be_t<u16, 1> frameCropTopOffset;
|
||||
be_t<u16, 1> frameCropBottomOffset;
|
||||
u8 x14; // contains videoFormat and videoFullRangeFlag
|
||||
u8 colourPrimaries;
|
||||
u8 transferCharacteristics;
|
||||
u8 matrixCoefficients;
|
||||
u8 x18; // contains entropyCodingModeFlag, deblockingFilterFlag, minNumSlicePerPictureIdc, nfwIdc
|
||||
u8 maxMeanBitrate;
|
||||
}
|
||||
@@ -314,13 +331,15 @@ struct PamfStreamHeader
|
||||
u8 x1; // not used
|
||||
u8 x2; // contains progressiveSequence, videoSignalInfoFlag, frameRateInfo
|
||||
u8 aspectRatioIdc;
|
||||
be_t<u16> sarWidth;
|
||||
be_t<u16> sarHeight;
|
||||
be_t<u16> horizontalSize;
|
||||
be_t<u16> verticalSize;
|
||||
be_t<u16> horizontalSizeValue;
|
||||
be_t<u16> verticalSizeValue;
|
||||
u32 x10; // not used
|
||||
be_t<u16, 1> sarWidth;
|
||||
be_t<u16, 1> sarHeight;
|
||||
u8 reserved1;
|
||||
u8 horizontalSize; // in units of 16 pixels
|
||||
u8 reserved2;
|
||||
u8 verticalSize; // in units of 16 pixels
|
||||
be_t<u16, 1> horizontalSizeValue;
|
||||
be_t<u16, 1> verticalSizeValue;
|
||||
be_t<u32, 1> x10; // not used
|
||||
u8 x14; // contains videoFormat and videoFullRangeFlag
|
||||
u8 colourPrimaries;
|
||||
u8 transferCharacteristics;
|
||||
@@ -331,7 +350,7 @@ struct PamfStreamHeader
|
||||
// Audio specific information
|
||||
struct
|
||||
{
|
||||
u16 unknown; // 0
|
||||
be_t<u16, 1> unknown; // 0
|
||||
u8 channels; // number of channels (1, 2, 6, 8)
|
||||
u8 freq; // 1 (always 48000)
|
||||
u8 bps; // LPCM only
|
||||
@@ -340,59 +359,236 @@ struct PamfStreamHeader
|
||||
};
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PamfStreamHeader, 48, 4);
|
||||
CHECK_SIZE_ALIGN(PamfStreamHeader, 48, 1);
|
||||
|
||||
struct PamfGroup
|
||||
{
|
||||
be_t<u32, 1> size; // doesn't include this field
|
||||
|
||||
u8 reserved;
|
||||
|
||||
u8 stream_num; // same value as in PamfSequenceInfo
|
||||
PamfStreamHeader streams;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PamfGroup, 6 + sizeof(PamfStreamHeader), 1);
|
||||
|
||||
struct PamfGroupingPeriod
|
||||
{
|
||||
be_t<u32, 1> size; // doesn't include this field
|
||||
|
||||
be_t<u16, 1> start_pts_high; // always 0, greatest valid pts is UINT32_MAX
|
||||
be_t<u32, 1> start_pts_low; // same value as in PamfSequenceInfo, since there is only one PamfGroupingPeriod
|
||||
be_t<u16, 1> end_pts_high; // unused due to bug
|
||||
be_t<u32, 1> end_pts_low; // same value as in PamfSequenceInfo, since there is only one PamfGroupingPeriod
|
||||
|
||||
u8 reserved;
|
||||
|
||||
u8 group_num; // always 1
|
||||
PamfGroup groups;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PamfGroupingPeriod, 0x12 + sizeof(PamfGroup), 1);
|
||||
|
||||
struct PamfSequenceInfo
|
||||
{
|
||||
be_t<u32, 1> size; // doesn't include this field
|
||||
|
||||
be_t<u16, 1> reserved1;
|
||||
|
||||
be_t<u16, 1> start_pts_high; // always 0, greatest valid pts is UINT32_MAX
|
||||
be_t<u32, 1> start_pts_low; // Presentation Time Stamp (start)
|
||||
be_t<u16, 1> end_pts_high; // always 0, greatest valid pts is UINT32_MAX
|
||||
be_t<u32, 1> end_pts_low; // Presentation Time Stamp (end)
|
||||
|
||||
be_t<u32, 1> mux_rate_bound; // multiplex bitrate in units of 50 bytes per second
|
||||
be_t<u32, 1> std_delay_bound; // buffer delay in units of 1/90000 seconds
|
||||
|
||||
be_t<u32, 1> total_stream_num; // across all groups; since there is always only one group, this is equal stream_count in PamfGroup
|
||||
|
||||
u8 reserved2;
|
||||
|
||||
u8 grouping_period_num; // always 1
|
||||
PamfGroupingPeriod grouping_periods;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PamfSequenceInfo, 0x20 + sizeof(PamfGroupingPeriod), 1);
|
||||
|
||||
struct PamfHeader
|
||||
{
|
||||
u32 magic; //"PAMF"
|
||||
u32 version; //"0041" (is it const?)
|
||||
be_t<u32> data_offset; //== 2048 >> 11, PAMF headers seem to be always 2048 bytes in size
|
||||
be_t<u32> data_size; //== ((fileSize - 2048) >> 11)
|
||||
u32 reserved[16];
|
||||
be_t<u32> table_size; //== size of mapping-table
|
||||
u16 reserved1;
|
||||
be_t<u16> start_pts_high;
|
||||
be_t<u32, 2> start_pts_low; //Presentation Time Stamp (start)
|
||||
be_t<u16> end_pts_high;
|
||||
be_t<u32, 2> end_pts_low; //Presentation Time Stamp (end)
|
||||
be_t<u32, 2> mux_rate_max; //== 0x01D470 (400 bps per unit, == 48000000 bps)
|
||||
be_t<u32, 2> mux_rate_min; //== 0x0107AC (?????)
|
||||
u16 reserved2; // ?????
|
||||
u8 reserved3;
|
||||
u8 stream_count; //total stream count (reduced to 1 byte)
|
||||
be_t<u16> unk1; //== 1 (?????)
|
||||
be_t<u32, 2> table_data_size; //== table_size - 0x20 == 0x14 + (0x30 * total_stream_num) (?????)
|
||||
//TODO: check relative offset of stream structs (could be from 0x0c to 0x14, currently 0x14)
|
||||
be_t<u16> start_pts_high2; //????? (probably same values)
|
||||
be_t<u32, 2> start_pts_low2; //?????
|
||||
be_t<u16> end_pts_high2; //?????
|
||||
be_t<u32, 2> end_pts_low2; //?????
|
||||
be_t<u32> unk2; //== 0x10000 (?????)
|
||||
be_t<u16> unk3; // ?????
|
||||
be_t<u16> unk4; // == stream_count
|
||||
//==========================
|
||||
PamfStreamHeader stream_headers[256];
|
||||
be_t<u32, 1> magic; // "PAMF"
|
||||
be_t<u32, 1> version; // "0040" or "0041"
|
||||
be_t<u32, 1> header_size; // in units of 2048 bytes
|
||||
be_t<u32, 1> data_size; // in units of 2048 bytes
|
||||
|
||||
be_t<u32, 1> psmf_marks_offset; // always 0
|
||||
be_t<u32, 1> psmf_marks_size; // always 0
|
||||
be_t<u32, 1> unk_offset; // always 0
|
||||
be_t<u32, 1> unk_size; // always 0
|
||||
|
||||
u8 reserved[0x30];
|
||||
|
||||
PamfSequenceInfo seq_info;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PamfHeader, 136 + sizeof(PamfHeader::stream_headers), 4);
|
||||
CHECK_SIZE_ALIGN(PamfHeader, 0x50 + sizeof(PamfSequenceInfo), 1);
|
||||
|
||||
struct PamfEpHeader
|
||||
{
|
||||
be_t<u16> value0; //mixed indexN (probably left 2 bits) and nThRefPictureOffset
|
||||
be_t<u16> pts_high;
|
||||
be_t<u32> pts_low;
|
||||
be_t<u32> rpnOffset;
|
||||
be_t<u16, 1> value0; // 2 bits: indexN, 1 bit: unused, 13 bits: nThRefPictureOffset in units of 2048 bytes
|
||||
be_t<u16, 1> pts_high; // always 0, greatest valid pts is UINT32_MAX
|
||||
be_t<u32, 1> pts_low;
|
||||
be_t<u32, 1> rpnOffset; // in units of 2048 bytes
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PamfEpHeader, 12, 4);
|
||||
CHECK_SIZE_ALIGN(PamfEpHeader, 12, 1);
|
||||
|
||||
// PSMF specific
|
||||
|
||||
struct PsmfStreamHeader
|
||||
{
|
||||
u8 stream_id;
|
||||
u8 private_stream_id; // for streams multiplexed as private data streams (stream_id == 0xbd)
|
||||
|
||||
be_t<u16, 1> p_std_buffer; // 2 bits: unused ??? "00", 1 bit: P_STD_buffer_scale, 13 bits: P_STD_buffer_size
|
||||
|
||||
be_t<u32, 1> ep_offset; // offset of EP section in header
|
||||
be_t<u32, 1> ep_num; // count of EPs
|
||||
|
||||
union
|
||||
{
|
||||
// Video specific information
|
||||
struct
|
||||
{
|
||||
u8 horizontalSize; // in units of 16 pixels
|
||||
u8 verticalSize; // in units of 16 pixels
|
||||
}
|
||||
video;
|
||||
|
||||
// Audio specific information
|
||||
struct
|
||||
{
|
||||
be_t<u16, 1> unknown; // 0
|
||||
u8 channelConfiguration; // 1 = mono, 2 = stereo
|
||||
u8 samplingFrequency; // 2 = 44.1kHz
|
||||
}
|
||||
audio;
|
||||
};
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PsmfStreamHeader, 0x10, 1);
|
||||
|
||||
struct PsmfGroup
|
||||
{
|
||||
be_t<u32, 1> size; // doesn't include this field
|
||||
|
||||
u8 reserved;
|
||||
|
||||
u8 stream_num; // same value as in PsmfSequenceInfo
|
||||
PsmfStreamHeader streams;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PsmfGroup, 6 + sizeof(PsmfStreamHeader), 1);
|
||||
|
||||
struct PsmfGroupingPeriod
|
||||
{
|
||||
be_t<u32, 1> size; // doesn't include this field
|
||||
|
||||
be_t<u16, 1> start_pts_high; // always 0, greatest valid pts is UINT32_MAX
|
||||
be_t<u32, 1> start_pts_low; // same value as in PsmfSequenceInfo, since there is only one PsmfGroupingPeriod
|
||||
be_t<u16, 1> end_pts_high; // unused due to bug
|
||||
be_t<u32, 1> end_pts_low; // same value as in PsmfSequenceInfo, since there is only one PsmfGroupingPeriod
|
||||
|
||||
u8 reserved;
|
||||
|
||||
u8 group_num; // always 1
|
||||
PsmfGroup groups;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PsmfGroupingPeriod, 0x12 + sizeof(PsmfGroup), 1);
|
||||
|
||||
struct PsmfSequenceInfo
|
||||
{
|
||||
be_t<u32, 1> size; // doesn't include this field
|
||||
|
||||
be_t<u16, 1> start_pts_high; // always 0, greatest valid pts is UINT32_MAX
|
||||
be_t<u32, 1> start_pts_low; // Presentation Time Stamp (start)
|
||||
be_t<u16, 1> end_pts_high; // always 0, greatest valid pts is UINT32_MAX
|
||||
be_t<u32, 1> end_pts_low; // Presentation Time Stamp (end)
|
||||
|
||||
be_t<u32, 1> mux_rate_bound; // multiplex bitrate in units of 50 bytes per second
|
||||
be_t<u32, 1> std_delay_bound; // buffer delay in units of 1/90000 seconds
|
||||
|
||||
u8 total_stream_num; // across all groups; since there is always only one group, this is equal stream_count in PsmfGroup
|
||||
|
||||
u8 grouping_period_num; // always 1
|
||||
PsmfGroupingPeriod grouping_periods;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PsmfSequenceInfo, 0x1a + sizeof(PsmfGroupingPeriod), 1);
|
||||
|
||||
struct PsmfHeader
|
||||
{
|
||||
be_t<u32, 1> magic; // "PSMF"
|
||||
be_t<u32, 1> version; // "0012", "0013", "0014" or "0015"
|
||||
be_t<u32, 1> header_size; // not scaled, unlike PAMF
|
||||
be_t<u32, 1> data_size; // not scaled, unlike PAMF
|
||||
|
||||
be_t<u32, 1> psmf_marks_offset;
|
||||
be_t<u32, 1> psmf_marks_size;
|
||||
be_t<u32, 1> unk[2];
|
||||
|
||||
u8 reserved[0x30];
|
||||
|
||||
PsmfSequenceInfo seq_info;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PsmfHeader, 0x50 + sizeof(PsmfSequenceInfo), 1);
|
||||
|
||||
struct PsmfEpHeader
|
||||
{
|
||||
be_t<u16, 1> value0; // 2 bits: indexN, 2 bits: unused, 11 bits: nThRefPictureOffset in units of 1024 bytes, 1 bit: pts_high
|
||||
be_t<u32, 1> pts_low;
|
||||
be_t<u32, 1> rpnOffset; // in units of 2048 bytes
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(PsmfEpHeader, 10, 1);
|
||||
|
||||
// not directly accessed by virtual CPU, fields are unknown
|
||||
struct CellPamfReader
|
||||
{
|
||||
vm::cptr<PamfHeader> pAddr;
|
||||
s32 stream;
|
||||
u64 fileSize;
|
||||
u32 internalData[28];
|
||||
be_t<u64> headerSize;
|
||||
be_t<u64> dataSize;
|
||||
be_t<u32> attribute;
|
||||
be_t<u16> isPsmf;
|
||||
be_t<u16> version;
|
||||
be_t<u32> currentGroupingPeriodIndex;
|
||||
be_t<u32> currentGroupIndex;
|
||||
be_t<u32> currentStreamIndex;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
vm::bcptr<PamfHeader> header;
|
||||
vm::bcptr<PamfSequenceInfo> sequenceInfo;
|
||||
vm::bcptr<PamfGroupingPeriod> currentGroupingPeriod;
|
||||
vm::bcptr<PamfGroup> currentGroup;
|
||||
vm::bcptr<PamfStreamHeader> currentStream;
|
||||
}
|
||||
pamf;
|
||||
|
||||
struct
|
||||
{
|
||||
vm::bcptr<PsmfHeader> header;
|
||||
vm::bcptr<PsmfSequenceInfo> sequenceInfo;
|
||||
vm::bcptr<PsmfGroupingPeriod> currentGroupingPeriod;
|
||||
vm::bcptr<PsmfGroup> currentGroup;
|
||||
vm::bcptr<PsmfStreamHeader> currentStream;
|
||||
}
|
||||
psmf;
|
||||
};
|
||||
|
||||
u32 reserved[18];
|
||||
};
|
||||
|
||||
CHECK_SIZE(CellPamfReader, 128);
|
||||
|
||||
@@ -1657,9 +1657,8 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
//CELL_SAVEDATA_RECREATE_NO = overwrite and let the user know, not data is corrupt.
|
||||
//cellSaveData.error("Savedata %s considered broken", save_entry.dirName);
|
||||
//TODO: if this is a save, and it's not auto, then show a dialog
|
||||
// fallthrough
|
||||
[[fallthrough]];
|
||||
}
|
||||
|
||||
case CELL_SAVEDATA_RECREATE_NO_NOBROKEN:
|
||||
{
|
||||
break;
|
||||
|
||||
@@ -879,7 +879,7 @@ error_code cellSearchStartContentSearchInList(vm::cptr<CellSearchContentId> list
|
||||
{
|
||||
curr_find->type = CELL_SEARCH_CONTENTTYPE_MUSIC;
|
||||
|
||||
const std::string path = vfs::get(vpath) + "/" + item.name;
|
||||
const std::string path = vfs::get(item_path);
|
||||
const auto [success, mi] = utils::get_media_info(path, 1); // AVMEDIA_TYPE_AUDIO
|
||||
if (!success)
|
||||
{
|
||||
@@ -900,7 +900,7 @@ error_code cellSearchStartContentSearchInList(vm::cptr<CellSearchContentId> list
|
||||
{
|
||||
curr_find->type = CELL_SEARCH_CONTENTTYPE_VIDEO;
|
||||
|
||||
const std::string path = vfs::get(vpath) + "/" + item.name;
|
||||
const std::string path = vfs::get(item_path);
|
||||
const auto [success, mi] = utils::get_media_info(path, 0); // AVMEDIA_TYPE_VIDEO
|
||||
if (!success)
|
||||
{
|
||||
@@ -1120,7 +1120,7 @@ error_code cellSearchStartContentSearch(CellSearchContentSearchType type, CellSe
|
||||
{
|
||||
curr_find->type = CELL_SEARCH_CONTENTTYPE_MUSIC;
|
||||
|
||||
const std::string path = vfs::get(vpath) + "/" + item.name;
|
||||
const std::string path = vfs::get(item_path);
|
||||
const auto [success, mi] = utils::get_media_info(path, 1); // AVMEDIA_TYPE_AUDIO
|
||||
if (!success)
|
||||
{
|
||||
@@ -1141,7 +1141,7 @@ error_code cellSearchStartContentSearch(CellSearchContentSearchType type, CellSe
|
||||
{
|
||||
curr_find->type = CELL_SEARCH_CONTENTTYPE_VIDEO;
|
||||
|
||||
const std::string path = vfs::get(vpath) + "/" + item.name;
|
||||
const std::string path = vfs::get(item_path);
|
||||
const auto [success, mi] = utils::get_media_info(path, 0); // AVMEDIA_TYPE_VIDEO
|
||||
if (!success)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,8 @@ void fmt_class_string<CellSysCacheError>::format(std::string& out, u64 arg)
|
||||
|
||||
extern lv2_fs_mount_point g_mp_sys_dev_hdd1;
|
||||
|
||||
extern std::string get_syscache_state_corruption_indicator_file_path(std::string_view dir_path);
|
||||
|
||||
struct syscache_info
|
||||
{
|
||||
const std::string cache_root = rpcs3::utils::get_hdd1_dir() + "/caches/";
|
||||
@@ -39,6 +41,8 @@ struct syscache_info
|
||||
|
||||
std::string cache_id;
|
||||
|
||||
bool retain_caches = false;
|
||||
|
||||
syscache_info() noexcept
|
||||
{
|
||||
// Check if dev_hdd1 is mounted by parent process
|
||||
@@ -47,10 +51,15 @@ struct syscache_info
|
||||
const auto lock = init.init();
|
||||
|
||||
// Extract cache id from path
|
||||
cache_id = Emu.hdd1;
|
||||
if (cache_id.back() == '/')
|
||||
cache_id.resize(cache_id.size() - 1);
|
||||
cache_id = cache_id.substr(cache_id.find_last_of('/') + 1);
|
||||
std::string_view id = Emu.hdd1;
|
||||
id = id.substr(0, id.find_last_not_of(fs::delim) + 1);
|
||||
id = id.substr(id.find_last_of(fs::delim) + 1);
|
||||
cache_id = std::string{id};
|
||||
|
||||
if (!Emu.DeserialManager() && !fs::write_file<true>(get_syscache_state_corruption_indicator_file_path(Emu.hdd1), fs::write_new))
|
||||
{
|
||||
fmt::throw_exception("Failed to create HDD1 corruption indicator file! (path='%s', reason='%s')", Emu.hdd1, fs::g_tls_error);
|
||||
}
|
||||
|
||||
cellSysutil.success("Retained cache from parent process: %s", Emu.hdd1);
|
||||
return;
|
||||
@@ -63,7 +72,17 @@ struct syscache_info
|
||||
{
|
||||
if (entry.is_directory && entry.name.starts_with(prefix))
|
||||
{
|
||||
cache_id = std::move(entry.name);
|
||||
cache_id = vfs::unescape(entry.name);
|
||||
|
||||
if (fs::is_file(get_syscache_state_corruption_indicator_file_path(cache_root + '/' + cache_id)))
|
||||
{
|
||||
// State is not complete
|
||||
clear(true);
|
||||
cache_id.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
cellSysutil.notice("Retained cache from past data: %s", cache_root + '/' + cache_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -89,8 +108,50 @@ struct syscache_info
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
~syscache_info() noexcept
|
||||
{
|
||||
if (cache_id.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!retain_caches)
|
||||
{
|
||||
vfs::host::remove_all(cache_root + cache_id, cache_root, &g_mp_sys_dev_hdd1, true, false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
idm::select<lv2_fs_object, lv2_file>([](u32 /*id*/, lv2_file& file)
|
||||
{
|
||||
if (file.file && file.mp->flags & lv2_mp_flag::cache && file.flags & CELL_FS_O_ACCMODE)
|
||||
{
|
||||
file.file.sync();
|
||||
}
|
||||
});
|
||||
|
||||
fs::remove_file(get_syscache_state_corruption_indicator_file_path(cache_root + cache_id));
|
||||
}
|
||||
};
|
||||
|
||||
extern std::string get_syscache_state_corruption_indicator_file_path(std::string_view dir_path)
|
||||
{
|
||||
constexpr std::u8string_view append_path = u8"/$hdd0_temp_state_indicator";
|
||||
const std::string_view filename = reinterpret_cast<const char*>(append_path.data());
|
||||
|
||||
if (dir_path.empty())
|
||||
{
|
||||
return rpcs3::utils::get_hdd1_dir() + "/caches/" + ensure(g_fxo->try_get<syscache_info>())->cache_id + "/" + filename.data();
|
||||
}
|
||||
|
||||
return std::string{dir_path} + filename.data();
|
||||
}
|
||||
|
||||
extern void signal_system_cache_can_stay()
|
||||
{
|
||||
ensure(g_fxo->try_get<syscache_info>())->retain_caches = true;
|
||||
}
|
||||
|
||||
error_code cellSysCacheClear()
|
||||
{
|
||||
cellSysutil.notice("cellSysCacheClear()");
|
||||
@@ -116,17 +177,26 @@ error_code cellSysCacheClear()
|
||||
|
||||
error_code cellSysCacheMount(vm::ptr<CellSysCacheParam> param)
|
||||
{
|
||||
cellSysutil.notice("cellSysCacheMount(param=*0x%x)", param);
|
||||
cellSysutil.notice("cellSysCacheMount(param=*0x%x ('%s'))", param, param.ptr(&CellSysCacheParam::cacheId));
|
||||
|
||||
auto& cache = g_fxo->get<syscache_info>();
|
||||
|
||||
if (!param || (param->cacheId[0] && sysutil_check_name_string(param->cacheId, 1, CELL_SYSCACHE_ID_SIZE) != 0))
|
||||
if (!param)
|
||||
{
|
||||
return CELL_SYSCACHE_ERROR_PARAM;
|
||||
}
|
||||
|
||||
std::string cache_name;
|
||||
|
||||
ensure(vm::read_string(param.ptr(&CellSysCacheParam::cacheId).addr(), sizeof(param->cacheId), cache_name), "Access violation");
|
||||
|
||||
if (!cache_name.empty() && sysutil_check_name_string(cache_name.data(), 1, CELL_SYSCACHE_ID_SIZE) != 0)
|
||||
{
|
||||
return CELL_SYSCACHE_ERROR_PARAM;
|
||||
}
|
||||
|
||||
// Full virtualized cache id (with title id included)
|
||||
std::string cache_id = vfs::escape(Emu.GetTitleID() + '_' + param->cacheId);
|
||||
std::string cache_id = vfs::escape(Emu.GetTitleID() + '_' + cache_name);
|
||||
|
||||
// Full path to virtual cache root (/dev_hdd1)
|
||||
std::string new_path = cache.cache_root + cache_id + '/';
|
||||
@@ -141,8 +211,8 @@ error_code cellSysCacheMount(vm::ptr<CellSysCacheParam> param)
|
||||
|
||||
std::lock_guard lock0(g_mp_sys_dev_hdd1.mutex);
|
||||
|
||||
// Check if can reuse existing cache (won't if cache id is an empty string)
|
||||
if (param->cacheId[0] && cache_id == cache.cache_id)
|
||||
// Check if can reuse existing cache (won't if cache id is an empty string or cache is damaged/incomplete)
|
||||
if (!cache_name.empty() && cache_id == cache.cache_id)
|
||||
{
|
||||
// Isn't mounted yet on first call to cellSysCacheMount
|
||||
if (vfs::mount("/dev_hdd1", new_path))
|
||||
@@ -152,15 +222,38 @@ error_code cellSysCacheMount(vm::ptr<CellSysCacheParam> param)
|
||||
return not_an_error(CELL_SYSCACHE_RET_OK_RELAYED);
|
||||
}
|
||||
|
||||
// Clear existing cache
|
||||
const bool can_create = cache.cache_id != cache_id || !cache.cache_id.empty();
|
||||
|
||||
if (!cache.cache_id.empty())
|
||||
{
|
||||
// Clear previous cache
|
||||
cache.clear(true);
|
||||
}
|
||||
|
||||
// Set new cache id
|
||||
cache.cache_id = std::move(cache_id);
|
||||
fs::create_dir(new_path);
|
||||
|
||||
if (can_create)
|
||||
{
|
||||
const bool created = fs::create_dir(new_path);
|
||||
|
||||
if (!created)
|
||||
{
|
||||
if (fs::g_tls_error != fs::error::exist)
|
||||
{
|
||||
fmt::throw_exception("Failed to create HDD1 cache! (path='%s', reason='%s')", new_path, fs::g_tls_error);
|
||||
}
|
||||
|
||||
// Clear new cache
|
||||
cache.clear(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs::write_file<true>(get_syscache_state_corruption_indicator_file_path(new_path), fs::write_new))
|
||||
{
|
||||
fmt::throw_exception("Failed to create HDD1 corruption indicator file! (path='%s', reason='%s')", new_path, fs::g_tls_error);
|
||||
}
|
||||
|
||||
if (vfs::mount("/dev_hdd1", new_path))
|
||||
g_fxo->get<lv2_fs_mount_info_map>().add("/dev_hdd1", &g_mp_sys_dev_hdd1);
|
||||
|
||||
|
||||
@@ -35,6 +35,30 @@ void fmt_class_string<CellSysutilAvc2Error>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<CellSysutilAvc2AttributeId>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](CellSysutilAvc2AttributeId value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_DETECT_EVENT_TYPE);
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_DETECT_INTERVAL_TIME);
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_DETECT_SIGNAL_LEVEL);
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_MAX_BITRATE);
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_DATA_FEC);
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_PACKET_CONTENTION);
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_DTX_MODE);
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_MIC_STATUS_DETECTION);
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_MIC_SETTING_NOTIFICATION);
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_MUTING_NOTIFICATION);
|
||||
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_CAMERA_STATUS_DETECTION);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
vm::ptr<CellSysutilAvc2Callback> avc2_cb{};
|
||||
vm::ptr<void> avc2_cb_arg{};
|
||||
|
||||
@@ -655,10 +679,13 @@ error_code cellSysutilAvc2Load(SceNpMatching2ContextId ctx_id, u32 container, vm
|
||||
|
||||
error_code cellSysutilAvc2SetAttribute(vm::cptr<CellSysutilAvc2Attribute> attr)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2SetAttribute(attr=*0x%x)", attr);
|
||||
|
||||
if (!attr)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2SetAttribute(attr=*0x%x)", attr);
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2SetAttribute(%s=%d)", attr->attr_id, attr->attr_param.int_param);
|
||||
|
||||
switch (attr->attr_id)
|
||||
{
|
||||
|
||||
@@ -1,74 +1,96 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "cellSysutil.h"
|
||||
#include "sceNp.h"
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
LOG_CHANNEL(cellSysutilNpEula);
|
||||
|
||||
struct sceNpEulaStructUnk0
|
||||
enum SceNpEulaStatus
|
||||
{
|
||||
char buf[0xc];
|
||||
SCE_NP_EULA_UNKNOWN = 0,
|
||||
SCE_NP_EULA_ACCEPTED = 1,
|
||||
SCE_NP_EULA_ALREADY_ACCEPTED = 2,
|
||||
SCE_NP_EULA_REJECTED = 3,
|
||||
SCE_NP_EULA_ABORTED = 4,
|
||||
SCE_NP_EULA_ERROR = 5,
|
||||
};
|
||||
|
||||
// All error codes are unknown at this point in implementation and just guesses
|
||||
enum cellSysutilNpEulaError : u32
|
||||
using SceNpEulaVersion = u32;
|
||||
using SceNpEulaCheckEulaStatusCallback = void(SceNpEulaStatus status, u32 errorCode, SceNpEulaVersion version, vm::ptr<void> arg);
|
||||
|
||||
struct sceNpEulaCallbacksRegistered
|
||||
{
|
||||
CELL_SYSUTIL_NP_EULA_NOT_INIT = 0x8002E500,
|
||||
CELL_SYSUTIL_NP_EULA_INVALID_PARAM = 0x8002E501,
|
||||
CELL_SYSUTIL_NP_EULA_UNKNOWN_502 = 0x8002E502,
|
||||
CELL_SYSUTIL_NP_EULA_UNKNOWN_503 = 0x8002E503,
|
||||
atomic_t<SceNpEulaStatus> status = SCE_NP_EULA_UNKNOWN;
|
||||
atomic_t<bool> sceNpEulaCheckEulaStatus_callback_registered = false;
|
||||
atomic_t<bool> sceNpEulaShowCurrentEula_callback_registered = false;
|
||||
};
|
||||
|
||||
template<>
|
||||
void fmt_class_string<cellSysutilNpEulaError>::format(std::string& out, u64 arg)
|
||||
error_code sceNpEulaCheckEulaStatus(vm::cptr<SceNpCommunicationId> communicationId, u32 arg2, u64 arg3, vm::ptr<SceNpEulaCheckEulaStatusCallback> cbFunc, vm::ptr<void> cbFuncArg)
|
||||
{
|
||||
format_enum(out, arg, [](auto error)
|
||||
cellSysutilNpEula.warning("sceNpEulaCheckEulaStatus(communicationId=*0x%x, arg2=0x%x, arg3=0x%x, cbFunc=*0x%x, cbFuncArg=*0x%x)", communicationId, arg2, arg3, cbFunc, cbFuncArg);
|
||||
|
||||
if (!communicationId || !cbFunc)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(CELL_SYSUTIL_NP_EULA_NOT_INIT);
|
||||
STR_CASE(CELL_SYSUTIL_NP_EULA_INVALID_PARAM);
|
||||
STR_CASE(CELL_SYSUTIL_NP_EULA_UNKNOWN_502);
|
||||
STR_CASE(CELL_SYSUTIL_NP_EULA_UNKNOWN_503);
|
||||
}
|
||||
return SCE_NP_EULA_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return unknown;
|
||||
auto& cb_infos = g_fxo->get<sceNpEulaCallbacksRegistered>();
|
||||
|
||||
if (cb_infos.sceNpEulaCheckEulaStatus_callback_registered || cb_infos.sceNpEulaShowCurrentEula_callback_registered)
|
||||
{
|
||||
return SCE_NP_EULA_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
cb_infos.sceNpEulaCheckEulaStatus_callback_registered = true;
|
||||
cb_infos.status = SCE_NP_EULA_ALREADY_ACCEPTED;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
auto& cb_infos = g_fxo->get<sceNpEulaCallbacksRegistered>();
|
||||
cbFunc(cb_ppu, cb_infos.status, CELL_OK, 1, cbFuncArg);
|
||||
cb_infos.sceNpEulaCheckEulaStatus_callback_registered = false;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
// Seen on: Resistance 3, Uncharted 2
|
||||
error_code sceNpEulaCheckEulaStatus(vm::ptr<sceNpEulaStructUnk0> arg1, u32 arg2, u64 arg3, u32 arg4, vm::ptr<CellSysutilCallback> callback1)
|
||||
{
|
||||
cellSysutilNpEula.todo("sceNpEulaCheckEulaStatus(arg1=*0x%x, arg2=0x%x, arg3=0x%x, arg4=0x%x, callback1=*0x%x)", arg1, arg2, arg3, arg4, callback1);
|
||||
|
||||
if (!arg1)
|
||||
return CELL_SYSUTIL_NP_EULA_INVALID_PARAM;
|
||||
|
||||
if (arg4 == 0)
|
||||
return CELL_SYSUTIL_NP_EULA_INVALID_PARAM;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// Seen on: Resistance 3
|
||||
error_code sceNpEulaAbort()
|
||||
{
|
||||
cellSysutilNpEula.todo("sceNpEulaAbort()");
|
||||
cellSysutilNpEula.warning("sceNpEulaAbort()");
|
||||
|
||||
// Can return CELL_SYSUTIL_NP_EULA_NOT_INIT
|
||||
auto& cb_infos = g_fxo->get<sceNpEulaCallbacksRegistered>();
|
||||
|
||||
if (!cb_infos.sceNpEulaCheckEulaStatus_callback_registered && !cb_infos.sceNpEulaShowCurrentEula_callback_registered)
|
||||
{
|
||||
return SCE_NP_EULA_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// It would forcefully abort the dialog/process of getting the eula but since we don't show the dialog, just alter the status returned
|
||||
cb_infos.status = SCE_NP_EULA_ABORTED;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// Seen on: Resistance 3, Uncharted 2
|
||||
error_code sceNpEulaShowCurrentEula(vm::ptr<sceNpEulaStructUnk0> arg1, u64 arg2, vm::ptr<CellSysutilCallback> callback1, vm::ptr<CellSysutilCallback> callback2)
|
||||
error_code sceNpEulaShowCurrentEula(vm::cptr<SceNpCommunicationId> communicationId, u64 arg2, vm::ptr<CellSysutilCallback> cbFunc, vm::ptr<void> cbFuncArg)
|
||||
{
|
||||
cellSysutilNpEula.todo("sceNpEulaShowCurrentEula(arg1=*0x%x, arg2=0x%x, callback1=*0x%x, callback2=*0x%x)", arg1, arg2, callback1, callback2);
|
||||
cellSysutilNpEula.todo("sceNpEulaShowCurrentEula(communicationId=*0x%x, arg2=0x%x, cbFunc=*0x%x, cbFuncArg=*0x%x)", communicationId, arg2, cbFunc, cbFuncArg);
|
||||
|
||||
if (!arg1)
|
||||
return CELL_SYSUTIL_NP_EULA_INVALID_PARAM;
|
||||
if (!communicationId || !cbFunc)
|
||||
{
|
||||
return SCE_NP_EULA_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!callback1)
|
||||
return CELL_SYSUTIL_NP_EULA_INVALID_PARAM;
|
||||
auto& cb_infos = g_fxo->get<sceNpEulaCallbacksRegistered>();
|
||||
|
||||
if (cb_infos.sceNpEulaCheckEulaStatus_callback_registered || cb_infos.sceNpEulaShowCurrentEula_callback_registered)
|
||||
{
|
||||
return SCE_NP_EULA_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
// Call callback (Unknown parameters)
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -280,7 +280,6 @@ struct vdec_context final
|
||||
|
||||
~vdec_context()
|
||||
{
|
||||
avcodec_close(ctx);
|
||||
avcodec_free_context(&ctx);
|
||||
sws_freeContext(sws);
|
||||
}
|
||||
|
||||
@@ -113,7 +113,9 @@ bool check_movie_path(const std::string& file_path)
|
||||
|
||||
std::string get_available_movie_path(const std::string& filename)
|
||||
{
|
||||
const std::string movie_dir = "/dev_hdd0/movie/";
|
||||
// TODO: Find out how to build this path properly. Apparently real hardware doesn't add a suffix,
|
||||
// but just randomly puts each video into a separate 2-Letter subdirectory like /video/hd/ or /video/ee/
|
||||
const std::string movie_dir = "/dev_hdd0/video/";
|
||||
std::string dst_path = vfs::get(movie_dir + filename);
|
||||
|
||||
// Do not overwrite existing files. Add a suffix instead.
|
||||
|
||||
@@ -156,6 +156,7 @@ error_code cellVoiceCreatePort(vm::ptr<u32> portId, vm::cptr<CellVoicePortParam>
|
||||
return CELL_VOICE_ERROR_ARGUMENT_INVALID;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELLVOICE_PORTTYPE_IN_MIC:
|
||||
case CELLVOICE_PORTTYPE_OUT_SECONDARY:
|
||||
|
||||
+1776
-273
File diff suppressed because it is too large
Load Diff
@@ -242,6 +242,85 @@ enum SceNpError : u32
|
||||
SCE_NP_COMMUNITY_SERVER_ERROR_TITLE_USER_STORAGE_MAINTENANCE = 0x8002a4ac,
|
||||
SCE_NP_COMMUNITY_SERVER_ERROR_UNSPECIFIED = 0x8002a4ff,
|
||||
|
||||
// COMMERCE
|
||||
SCE_NP_COMMERCE_ERROR_NOT_INITIALIZED = 0x80029401,
|
||||
SCE_NP_COMMERCE_ERROR_ALREADY_INITIALIZED = 0x80029402,
|
||||
SCE_NP_COMMERCE_ERROR_OUT_OF_MEMORY = 0x80029403,
|
||||
SCE_NP_COMMERCE_ERROR_UNSUPPORTED_VERSION = 0x80029404,
|
||||
SCE_NP_COMMERCE_ERROR_CTX_MAX = 0x80029405,
|
||||
SCE_NP_COMMERCE_ERROR_CTX_NOT_FOUND = 0x80029406,
|
||||
SCE_NP_COMMERCE_ERROR_CTXID_NOT_AVAILABLE = 0x80029407,
|
||||
SCE_NP_COMMERCE_ERROR_REQ_MAX = 0x80029408,
|
||||
SCE_NP_COMMERCE_ERROR_REQ_NOT_FOUND = 0x80029409,
|
||||
SCE_NP_COMMERCE_ERROR_REQID_NOT_AVAILABLE = 0x8002940a,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_CATEGORY_ID = 0x8002940b,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_LANG_CODE = 0x8002940c,
|
||||
SCE_NP_COMMERCE_ERROR_REQ_BUSY = 0x8002940d,
|
||||
SCE_NP_COMMERCE_ERROR_INSUFFICIENT_BUFFER = 0x8002940e,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_REQ_STATE = 0x8002940f,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_CTX_STATE = 0x80029410,
|
||||
SCE_NP_COMMERCE_ERROR_UNKNOWN = 0x80029411,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_REQ_TYPE = 0x80029412,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_MEMORY_CONTAINER = 0x80029413,
|
||||
SCE_NP_COMMERCE_ERROR_INSUFFICIENT_MEMORY_CONTAINER = 0x80029414,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_DATA_FLAG_TYPE = 0x80029415,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_DATA_FLAG_STATE = 0x80029416,
|
||||
SCE_NP_COMMERCE_ERROR_DATA_FLAG_NUM_NOT_FOUND = 0x80029417,
|
||||
SCE_NP_COMMERCE_ERROR_DATA_FLAG_INFO_NOT_FOUND = 0x80029418,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_PROVIDER_ID = 0x80029419,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_DATA_FLAG_NUM = 0x8002941a,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_SKU_ID = 0x8002941b,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_DATA_FLAG_ID = 0x8002941c,
|
||||
SCE_NP_COMMERCE_ERROR_GPC_SEND_REQUEST = 0x8002941d,
|
||||
SCE_NP_COMMERCE_ERROR_GDF_SEND_REQUEST = 0x8002941e,
|
||||
SCE_NP_COMMERCE_ERROR_SDF_SEND_REQUEST = 0x8002941f,
|
||||
|
||||
SCE_NP_COMMERCE_ERROR_PARSE_PRODUCT_CATEGORY = 0x80029421,
|
||||
SCE_NP_COMMERCE_ERROR_CURRENCY_INFO_NOT_FOUND = 0x80029422,
|
||||
SCE_NP_COMMERCE_ERROR_CATEGORY_INFO_NOT_FOUND = 0x80029423,
|
||||
SCE_NP_COMMERCE_ERROR_CHILD_CATEGORY_COUNT_NOT_FOUND = 0x80029424,
|
||||
SCE_NP_COMMERCE_ERROR_CHILD_CATEGORY_INFO_NOT_FOUND = 0x80029425,
|
||||
SCE_NP_COMMERCE_ERROR_SKU_COUNT_NOT_FOUND = 0x80029426,
|
||||
SCE_NP_COMMERCE_ERROR_SKU_INFO_NOT_FOUND = 0x80029427,
|
||||
SCE_NP_COMMERCE_ERROR_PLUGIN_LOAD_FAILURE = 0x80029428,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_SKU_NUM = 0x80029429,
|
||||
SCE_NP_COMMERCE_ERROR_INVALID_GPC_PROTOCOL_VERSION = 0x8002942a,
|
||||
|
||||
SCE_NP_COMMERCE_ERROR_CHECKOUT_UNEXPECTED = 0x80029430,
|
||||
SCE_NP_COMMERCE_ERROR_CHECKOUT_OUT_OF_SERVICE = 0x80029431,
|
||||
SCE_NP_COMMERCE_ERROR_CHECKOUT_INVALID_SKU = 0x80029432,
|
||||
SCE_NP_COMMERCE_ERROR_CHECKOUT_SERVER_BUSY = 0x80029433,
|
||||
SCE_NP_COMMERCE_ERROR_CHECKOUT_MAINTENANCE = 0x80029434,
|
||||
SCE_NP_COMMERCE_ERROR_CHECKOUT_ACCOUNT_SUSPENDED = 0x80029435,
|
||||
SCE_NP_COMMERCE_ERROR_CHECKOUT_OVER_SPENDING_LIMIT = 0x80029436,
|
||||
SCE_NP_COMMERCE_ERROR_CHECKOUT_NOT_ENOUGH_MONEY = 0x80029437,
|
||||
SCE_NP_COMMERCE_ERROR_CHECKOUT_UNKNOWN = 0x80029438,
|
||||
|
||||
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_UNKNOWN = 0x80029600,
|
||||
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_INVALID_CREDENTIALS = 0x80029601,
|
||||
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_INVALID_CATEGORY_ID = 0x80029602,
|
||||
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_SERVICE_END = 0x80029603,
|
||||
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_SERVICE_STOP = 0x80029604,
|
||||
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_SERVICE_BUSY = 0x80029605,
|
||||
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_UNSUPPORTED_VERSION = 0x80029606,
|
||||
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_INTERNAL_SERVER = 0x80029680,
|
||||
|
||||
SCE_NP_COMMERCE_GDF_SERVER_ERROR_UNKNOWN = 0x80029d00,
|
||||
SCE_NP_COMMERCE_GDF_SERVER_ERROR_INVALID_CREDENTIALS = 0x80029d01,
|
||||
SCE_NP_COMMERCE_GDF_SERVER_ERROR_INVALID_FLAGLIST = 0x80029d02,
|
||||
SCE_NP_COMMERCE_GDF_SERVER_ERROR_SERVICE_END = 0x80029d03,
|
||||
SCE_NP_COMMERCE_GDF_SERVER_ERROR_SERVICE_STOP = 0x80029d04,
|
||||
SCE_NP_COMMERCE_GDF_SERVER_ERROR_SERVICE_BUSY = 0x80029d05,
|
||||
SCE_NP_COMMERCE_GDF_SERVER_ERROR_UNSUPPORTED_VERSION = 0x80029d06,
|
||||
|
||||
SCE_NP_COMMERCE_SDF_SERVER_ERROR_UNKNOWN = 0x80029e00,
|
||||
SCE_NP_COMMERCE_SDF_SERVER_ERROR_INVALID_CREDENTIALS = 0x80029e01,
|
||||
SCE_NP_COMMERCE_SDF_SERVER_ERROR_INVALID_FLAGLIST = 0x80029e02,
|
||||
SCE_NP_COMMERCE_SDF_SERVER_ERROR_SERVICE_END = 0x80029e03,
|
||||
SCE_NP_COMMERCE_SDF_SERVER_ERROR_SERVICE_STOP = 0x80029e04,
|
||||
SCE_NP_COMMERCE_SDF_SERVER_ERROR_SERVICE_BUSY = 0x80029e05,
|
||||
SCE_NP_COMMERCE_SDF_SERVER_ERROR_UNSUPPORTED_VERSION = 0x80029e06,
|
||||
|
||||
// DRM
|
||||
SCE_NP_DRM_ERROR_OUT_OF_MEMORY = 0x80029501,
|
||||
SCE_NP_DRM_ERROR_INVALID_PARAM = 0x80029502,
|
||||
@@ -454,6 +533,197 @@ enum SceNpError : u32
|
||||
SCE_NP_CUSTOM_MENU_ERROR_NOT_REGISTERED = 0x80023b0b,
|
||||
SCE_NP_CUSTOM_MENU_ERROR_EXCEEDS_MAX = 0x80023b0c,
|
||||
SCE_NP_CUSTOM_MENU_ERROR_INVALID_CHARACTER = 0x80023b0d,
|
||||
|
||||
// EULA
|
||||
SCE_NP_EULA_ERROR_UNKNOWN = 0x8002e500,
|
||||
SCE_NP_EULA_ERROR_INVALID_ARGUMENT = 0x8002e501,
|
||||
SCE_NP_EULA_ERROR_NOT_INITIALIZED = 0x8002e502,
|
||||
SCE_NP_EULA_ERROR_ALREADY_INITIALIZED = 0x8002e503,
|
||||
SCE_NP_EULA_ERROR_OUT_OF_MEMORY = 0x8002e504,
|
||||
SCE_NP_EULA_ERROR_BUSY = 0x8002e505,
|
||||
SCE_NP_EULA_ERROR_EULA_NOT_FOUND = 0x8002e5a0,
|
||||
SCE_NP_EULA_ERROR_NET_OUT_OF_MEMORY = 0x8002e5a1,
|
||||
SCE_NP_EULA_ERROR_CONF_FORMAT = 0x8002e5b0,
|
||||
SCE_NP_EULA_ERROR_CONF_INVALID_FILENAME = 0x8002e5b1,
|
||||
SCE_NP_EULA_ERROR_CONF_TOO_MANY_EULA_FILES = 0x8002e5b2,
|
||||
SCE_NP_EULA_ERROR_CONF_INVALID_LANGUAGE = 0x8002e5b3,
|
||||
SCE_NP_EULA_ERROR_CONF_INVALID_COUNTRY = 0x8002e5b4,
|
||||
SCE_NP_EULA_ERROR_CONF_INVALID_NPCOMMID = 0x8002e5b5,
|
||||
SCE_NP_EULA_ERROR_CONF_INVALID_EULA_VERSION = 0x8002e5b6,
|
||||
|
||||
// Matching
|
||||
SCE_NP_MATCHING_ERROR_NOT_INITIALIZED = 0x8002a701,
|
||||
SCE_NP_MATCHING_ERROR_ALREADY_INITIALIZED = 0x8002a702,
|
||||
SCE_NP_MATCHING_ERROR_INVALID_ARG = 0x8002a703,
|
||||
SCE_NP_MATCHING_ERROR_TERMINATED = 0x8002a704,
|
||||
SCE_NP_MATCHING_ERROR_TIMEOUT = 0x8002a705,
|
||||
SCE_NP_MATCHING_ERROR_OUT_OF_MEMORY = 0x8002a706,
|
||||
SCE_NP_MATCHING_ERROR_CTXID_NOT_AVAIL = 0x8002a707,
|
||||
SCE_NP_MATCHING_ERROR_CTX_ALREADY_EXIST = 0x8002a708,
|
||||
SCE_NP_MATCHING_ERROR_CTX_NOT_FOUND = 0x8002a709,
|
||||
SCE_NP_MATCHING_ERROR_LOBBY_NOT_FOUND = 0x8002a70a,
|
||||
SCE_NP_MATCHING_ERROR_ROOM_NOT_FOUND = 0x8002a70b,
|
||||
SCE_NP_MATCHING_ERROR_MEMBER_NOT_FOUND = 0x8002a70c,
|
||||
SCE_NP_MATCHING_ERROR_TOO_BIG_VALUE = 0x8002a70d,
|
||||
SCE_NP_MATCHING_ERROR_IVALID_ATTR_TYPE = 0x8002a70e,
|
||||
SCE_NP_MATCHING_ERROR_INVALID_ATTR_ID = 0x8002a70f,
|
||||
SCE_NP_MATCHING_ERROR_ALREADY_REQUESTED = 0x8002a710,
|
||||
SCE_NP_MATCHING_ERROR_LIMITTED_SEATING = 0x8002a711,
|
||||
SCE_NP_MATCHING_ERROR_LOCKED = 0x8002a712,
|
||||
SCE_NP_MATCHING_ERROR_CTX_STILL_RUNNING = 0x8002a713,
|
||||
SCE_NP_MATCHING_ERROR_INSUFFICIENT_BUFFER = 0x8002a714,
|
||||
SCE_NP_MATCHING_ERROR_REQUEST_NOT_ALLOWED = 0x8002a715,
|
||||
SCE_NP_MATCHING_ERROR_CTX_MAX = 0x8002a716,
|
||||
SCE_NP_MATCHING_ERROR_INVALID_REQ_ID = 0x8002a717,
|
||||
SCE_NP_MATCHING_ERROR_RESULT_NOT_FOUND = 0x8002a718,
|
||||
SCE_NP_MATCHING_ERROR_BUSY = 0x8002a719,
|
||||
SCE_NP_MATCHING_ERROR_ALREADY_JOINED_ROOM = 0x8002a71a,
|
||||
SCE_NP_MATCHING_ERROR_ROOM_MAX = 0x8002a71b,
|
||||
SCE_NP_MATCHING_ERROR_QUICK_MATCH_PLAYER_NOT_FOUND = 0x8002a71c,
|
||||
SCE_NP_MATCHING_ERROR_COND_MAX = 0x8002a71d,
|
||||
SCE_NP_MATCHING_ERROR_INVALID_COND = 0x8002a71e,
|
||||
SCE_NP_MATCHING_ERROR_INVALID_ATTR = 0x8002a71f,
|
||||
SCE_NP_MATCHING_ERROR_COMP_OP_INEQUALITY_MAX = 0x8002a720,
|
||||
SCE_NP_MATCHING_ERROR_RESULT_OVERFLOWED = 0x8002a721,
|
||||
SCE_NP_MATCHING_ERROR_HTTPXML_TIMEOUT = 0x8002a722,
|
||||
SCE_NP_MATCHING_ERROR_CANCELED = 0x8002a723,
|
||||
SCE_NP_MATCHING_ERROR_SEARCH_JOIN_ROOM_NOT_FOUND = 0x8002a724,
|
||||
SCE_NP_MATCHING_ERROR_INVALID_COMP_OP = 0x8002a725,
|
||||
SCE_NP_MATCHING_ERROR_INVALID_COMP_TYPE = 0x8002a726,
|
||||
SCE_NP_MATCHING_ERROR_REQUEST_NOT_FOUND = 0x8002a727,
|
||||
SCE_NP_MATCHING_ERROR_INTERNAL_ERROR = 0x8002a728,
|
||||
SCE_NP_MATCHING_ERROR_INVALID_PROTOCOL_ID = 0x8002a729,
|
||||
SCE_NP_MATCHING_ERROR_ATTR_NOT_SPECIFIED = 0x8002a72a,
|
||||
SCE_NP_MATCHING_ERROR_SYSUTIL_INVALID_RESULT = 0x8002a72b,
|
||||
SCE_NP_MATCHING_ERROR_PLUGIN_LOAD_FAILURE = 0x8002a72c,
|
||||
SCE_NP_MATCHING_ERROR_INVALID_ATTR_VALUE = 0x8002a72d,
|
||||
SCE_NP_MATCHING_ERROR_DUPLICATE = 0x8002a72e,
|
||||
SCE_NP_MATCHING_ERROR_INVALID_MEMORY_CONTAINER = 0x8002a72f,
|
||||
SCE_NP_MATCHING_ERROR_SHUTDOWN = 0x8002a730,
|
||||
SCE_NP_MATCHING_ERROR_SYSUTIL_SERVER_BUSY = 0x8002a731,
|
||||
SCE_NP_MATCHING_ERROR_SEND_INVITATION_PARTIALLY_FAILED = 0x8002a732,
|
||||
SCE_NP_MATCHING_ERROR_UTILITY_UNAVAILABLE = 0x8002a733,
|
||||
|
||||
SCE_NP_MATCHING_SERVER_ERROR_OUT_OF_SERVICE = 0x8002a740,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_MAINTENANCE = 0x8002a741,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_SERVER_BUSY = 0x8002a742,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN = 0x8002a743,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_SERVER = 0x8002a744,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_LOBBY = 0x8002a745,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM = 0x8002a746,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_USER = 0x8002a747,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED = 0x8002a748,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_UNKNOWN = 0x8002a749,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_BAD_REQUEST_STANZA = 0x8002a74a,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_REQUEST_FORBIDDEN = 0x8002a74b,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_INTERNAL_ERROR = 0x8002a74c,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_ROOM_OVER = 0x8002a74d,
|
||||
SCE_NP_MATCHING_SERVER_ERROR_ROOM_CLOSED = 0x8002a74e,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_COMMERCE_CURRENCY_CODE_LEN = 4,
|
||||
SCE_NP_COMMERCE_CATEGORY_ID_LEN = 56,
|
||||
SCE_NP_COMMERCE_CATEGORY_NAME_LEN = 192,
|
||||
SCE_NP_COMMERCE_CATEGORY_DESCRIPTION_LEN = 384,
|
||||
SCE_NP_COMMERCE_CATEGORY_IMAGEURL_LEN = 128,
|
||||
SCE_NP_COMMERCE_PRODUCT_ID_LEN = 48,
|
||||
SCE_NP_COMMERCE_PRODUCT_NAME_LEN = 192,
|
||||
SCE_NP_COMMERCE_SKU_ID_LEN = 56,
|
||||
SCE_NP_COMMERCE_SKU_NAME_LEN = 64,
|
||||
SCE_NP_COMMERCE_SKU_DESCRIPTION_LEN = 384,
|
||||
SCE_NP_COMMERCE_SKU_IMAGEURL_LEN = 128,
|
||||
SCE_NP_COMMERCE_SKU_USER_DATA_LEN = 128,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_COMMERCE_EVENT_REQUEST_ERROR = 0x0001,
|
||||
SCE_NP_COMMERCE_EVENT_PRODUCT_CATEGORY_READY = 0x0011,
|
||||
SCE_NP_COMMERCE_EVENT_PRODUCT_CATEGORY_DONE = 0x0012,
|
||||
SCE_NP_COMMERCE_EVENT_PRODUCT_CATEGORY_ABORT = 0x0013,
|
||||
SCE_NP_COMMERCE_EVENT_DO_CHECKOUT_STARTED = 0x0021,
|
||||
SCE_NP_COMMERCE_EVENT_DO_CHECKOUT_SUCCESS = 0x0022,
|
||||
SCE_NP_COMMERCE_EVENT_DO_CHECKOUT_BACK = 0x0023,
|
||||
SCE_NP_COMMERCE_EVENT_DO_CHECKOUT_FINISHED = 0x0025,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_MATCHING_EVENT_ERROR = 0x0001,
|
||||
SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE = 0x0002,
|
||||
SCE_NP_MATCHING_EVENT_RESERVED1 = 0x0003,
|
||||
SCE_NP_MATCHING_EVENT_RESERVED2 = 0x0004,
|
||||
SCE_NP_MATCHING_EVENT_ROOM_UPDATE_NEW_MEMBER = 0x0005,
|
||||
SCE_NP_MATCHING_EVENT_ROOM_UPDATE_MEMBER_LEAVE = 0x0006,
|
||||
SCE_NP_MATCHING_EVENT_ROOM_DISAPPEARED = 0x0007,
|
||||
SCE_NP_MATCHING_EVENT_RESERVED3 = 0x0008,
|
||||
SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE = 0x0009,
|
||||
SCE_NP_MATCHING_EVENT_SET_ROOM_INFO_DONE = 0x000a,
|
||||
SCE_NP_MATCHING_EVENT_GRANT_OWNER_DONE = 0x000b,
|
||||
SCE_NP_MATCHING_EVENT_ROOM_UPDATE_OWNER_CHANGE = 0x000c,
|
||||
SCE_NP_MATCHING_EVENT_KICK_MEMBER_DONE = 0x000d,
|
||||
SCE_NP_MATCHING_EVENT_ROOM_KICKED = 0x000e,
|
||||
SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE = 0x000f,
|
||||
SCE_NP_MATCHING_EVENT_SET_ROOM_SEARCH_FLAG_DONE = 0x0010,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_MATCHING_ATTR_TYPE_BASIC_BIN = 1,
|
||||
SCE_NP_MATCHING_ATTR_TYPE_BASIC_NUM = 2,
|
||||
SCE_NP_MATCHING_ATTR_TYPE_GAME_BIN = 3,
|
||||
SCE_NP_MATCHING_ATTR_TYPE_GAME_NUM = 4,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_MATCHING_ROOM_ATTR_ID_TOTAL_SLOT = 1,
|
||||
SCE_NP_MATCHING_ROOM_ATTR_ID_PRIVATE_SLOT = 2,
|
||||
SCE_NP_MATCHING_ROOM_ATTR_ID_CUR_TOTAL_NUM = 3,
|
||||
SCE_NP_MATCHING_ROOM_ATTR_ID_CUR_PUBLIC_NUM = 4,
|
||||
SCE_NP_MATCHING_ROOM_ATTR_ID_CUR_PRIVATE_NUM = 5,
|
||||
SCE_NP_MATCHING_ROOM_ATTR_ID_PRIVILEGE_TYPE = 6,
|
||||
SCE_NP_MATCHING_ROOM_ATTR_ID_ROOM_SEARCH_FLAG = 7,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_MATCHING_ROOM_SLOT_TYPE_PUBLIC = 0,
|
||||
SCE_NP_MATCHING_ROOM_SLOT_TYPE_PRIVATE = 1,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_MATCHING_ROOM_PRIVILEGE_TYPE_NO_AUTO_GRANT = 0,
|
||||
SCE_NP_MATCHING_ROOM_PRIVILEGE_TYPE_AUTO_GRANT = 1,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_MATCHING_ROOM_SEARCH_FLAG_OPEN = 0,
|
||||
SCE_NP_MATCHING_ROOM_SEARCH_FLAG_STEALTH = 1,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_MATCHING_CONDITION_SEARCH_EQ = 0,
|
||||
SCE_NP_MATCHING_CONDITION_SEARCH_NE = 1,
|
||||
SCE_NP_MATCHING_CONDITION_SEARCH_LT = 2,
|
||||
SCE_NP_MATCHING_CONDITION_SEARCH_LE = 3,
|
||||
SCE_NP_MATCHING_CONDITION_SEARCH_GT = 4,
|
||||
SCE_NP_MATCHING_CONDITION_SEARCH_GE = 5,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_MATCHING_CONDITION_TYPE_VALUE = 0,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCE_NP_MATCHING_INVITATION_DESTINATION_MAX = 12,
|
||||
};
|
||||
|
||||
// Basic presence options
|
||||
@@ -592,7 +862,7 @@ enum
|
||||
enum : SceNpBasicAttachmentDataId
|
||||
{
|
||||
SCE_NP_BASIC_INVALID_ATTACHMENT_DATA_ID = 0,
|
||||
SCE_NP_BASIC_INVALID_MESSAGE_ID = 0,
|
||||
SCE_NP_BASIC_INVALID_MESSAGE_ID = 0,
|
||||
SCE_NP_BASIC_SELECTED_INVITATION_DATA = 1,
|
||||
SCE_NP_BASIC_SELECTED_MESSAGE_DATA = 2,
|
||||
};
|
||||
@@ -753,6 +1023,7 @@ enum
|
||||
SCE_NP_UTIL_NPID_VERSION = 1,
|
||||
SCE_NP_UTIL_NPLOBBYID_VERSION = 1,
|
||||
SCE_NP_UTIL_NPROOMID_VERSION = 1,
|
||||
SCE_NP_COMMERCE_VERSION = 1,
|
||||
};
|
||||
|
||||
// Languages
|
||||
@@ -1431,12 +1702,26 @@ struct message_data
|
||||
void print() const;
|
||||
};
|
||||
|
||||
struct np_state
|
||||
{
|
||||
atomic_t<bool> abort_gui_flag = false;
|
||||
};
|
||||
|
||||
namespace rpcn
|
||||
{
|
||||
class rpcn_client;
|
||||
}
|
||||
|
||||
class SendMessageDialogBase
|
||||
{
|
||||
public:
|
||||
virtual ~SendMessageDialogBase() = default;
|
||||
|
||||
virtual bool Exec(message_data& msg_data, std::set<std::string>& npids) = 0;
|
||||
virtual error_code Exec(message_data& msg_data, std::set<std::string>& npids) = 0;
|
||||
virtual void callback_handler(u16 ntype, const std::string& username, bool status) = 0;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<rpcn::rpcn_client> m_rpcn;
|
||||
};
|
||||
|
||||
class RecvMessageDialogBase
|
||||
@@ -1444,5 +1729,9 @@ class RecvMessageDialogBase
|
||||
public:
|
||||
virtual ~RecvMessageDialogBase() = default;
|
||||
|
||||
virtual bool Exec(SceNpBasicMessageMainType type, SceNpBasicMessageRecvOptions options, SceNpBasicMessageRecvAction& recv_result, u64& chosen_msg_id) = 0;
|
||||
virtual error_code Exec(SceNpBasicMessageMainType type, SceNpBasicMessageRecvOptions options, SceNpBasicMessageRecvAction& recv_result, u64& chosen_msg_id) = 0;
|
||||
virtual void callback_handler(const std::shared_ptr<std::pair<std::string, message_data>> new_msg, u64 msg_id) = 0;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<rpcn::rpcn_client> m_rpcn;
|
||||
};
|
||||
|
||||
@@ -179,14 +179,14 @@ error_code sceNpMatching2Init2(u32 stackSize, s32 priority, vm::ptr<SceNpMatchin
|
||||
error_code sceNpMatching2Term(ppu_thread& ppu);
|
||||
error_code sceNpMatching2Term2();
|
||||
|
||||
error_code generic_match2_error_check(const named_thread<np::np_handler>& nph, SceNpMatching2ContextId ctxId, vm::cptr<void> reqParam, vm::ptr<SceNpMatching2RequestId> assignedReqId)
|
||||
error_code generic_match2_error_check(const named_thread<np::np_handler>& nph, SceNpMatching2ContextId ctxId, vm::cptr<void> reqParam)
|
||||
{
|
||||
if (!nph.is_NP2_Match2_init)
|
||||
{
|
||||
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!reqParam || !assignedReqId)
|
||||
if (!reqParam)
|
||||
{
|
||||
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
@@ -313,7 +313,10 @@ error_code sceNpMatching2Term2()
|
||||
nph.is_NP2_Match2_init = false;
|
||||
}
|
||||
|
||||
// TODO: for all contexts: sceNpMatching2DestroyContext
|
||||
idm::clear<match2_ctx>();
|
||||
|
||||
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
sigh.clear_match2_ctx();
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -332,6 +335,9 @@ error_code sceNpMatching2DestroyContext(SceNpMatching2ContextId ctxId)
|
||||
if (!destroy_match2_context(ctxId))
|
||||
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND;
|
||||
|
||||
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
sigh.remove_match2_ctx(ctxId);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -341,7 +347,7 @@ error_code sceNpMatching2LeaveLobby(
|
||||
sceNp2.todo("sceNpMatching2LeaveLobby(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -369,7 +375,7 @@ error_code sceNpMatching2GetWorldInfoList(
|
||||
sceNp2.warning("sceNpMatching2GetWorldInfoList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -379,7 +385,12 @@ error_code sceNpMatching2GetWorldInfoList(
|
||||
return SCE_NP_MATCHING2_ERROR_INVALID_SERVER_ID;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.get_world_list(ctxId, optParam, reqParam->serverId);
|
||||
const u32 request_id = nph.get_world_list(ctxId, optParam, reqParam->serverId);
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -404,7 +415,7 @@ error_code sceNpMatching2GetLobbyMemberDataInternalList(SceNpMatching2ContextId
|
||||
sceNp2.todo("sceNpMatching2GetLobbyMemberDataInternalList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -418,18 +429,23 @@ error_code sceNpMatching2SearchRoom(
|
||||
sceNp2.warning("sceNpMatching2SearchRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.search_room(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.search_room(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpMatching2SignalingGetConnectionStatus(
|
||||
SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId memberId, vm::ptr<int> connStatus, vm::ptr<np_in_addr> peerAddr, vm::ptr<np_in_port_t> peerPort)
|
||||
SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId memberId, vm::ptr<s32> connStatus, vm::ptr<np_in_addr> peerAddr, vm::ptr<np_in_port_t> peerPort)
|
||||
{
|
||||
sceNp2.warning("sceNpMatching2SignalingGetConnectionStatus(ctxId=%d, roomId=%d, memberId=%d, connStatus=*0x%x, peerAddr=*0x%x, peerPort=*0x%x)", ctxId, roomId, memberId, connStatus, peerAddr, peerPort);
|
||||
|
||||
@@ -494,14 +510,21 @@ error_code sceNpMatching2SignalingGetConnectionStatus(
|
||||
error_code sceNpMatching2SetUserInfo(
|
||||
SceNpMatching2ContextId ctxId, vm::cptr<SceNpMatching2SetUserInfoRequest> reqParam, vm::cptr<SceNpMatching2RequestOptParam> optParam, vm::ptr<SceNpMatching2RequestId> assignedReqId)
|
||||
{
|
||||
sceNp2.todo("sceNpMatching2SetUserInfo(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
sceNp2.warning("sceNpMatching2SetUserInfo(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
const u32 request_id = nph.set_userinfo(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -525,7 +548,7 @@ error_code sceNpMatching2GetLobbyMemberDataInternal(
|
||||
sceNp2.todo("sceNpMatching2GetLobbyMemberDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -566,7 +589,7 @@ error_code sceNpMatching2CreateServerContext(
|
||||
sceNp2.warning("sceNpMatching2CreateServerContext(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -576,7 +599,12 @@ error_code sceNpMatching2CreateServerContext(
|
||||
return SCE_NP_MATCHING2_ERROR_INVALID_SERVER_ID;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.create_server_context(ctxId, optParam, reqParam->serverId);
|
||||
const u32 request_id = nph.create_server_context(ctxId, optParam, reqParam->serverId);
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -601,12 +629,17 @@ error_code sceNpMatching2LeaveRoom(
|
||||
sceNp2.warning("sceNpMatching2LeaveRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.leave_room(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.leave_room(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -617,12 +650,17 @@ error_code sceNpMatching2SetRoomDataExternal(
|
||||
sceNp2.warning("sceNpMatching2SetRoomDataExternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.set_roomdata_external(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.set_roomdata_external(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -713,12 +751,17 @@ error_code sceNpMatching2SendRoomMessage(
|
||||
sceNp2.warning("sceNpMatching2SendRoomMessage(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.send_room_message(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.send_room_message(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -729,7 +772,7 @@ error_code sceNpMatching2JoinLobby(
|
||||
sceNp2.todo("sceNpMatching2JoinLobby(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -743,7 +786,7 @@ error_code sceNpMatching2GetRoomMemberDataExternalList(SceNpMatching2ContextId c
|
||||
sceNp2.todo("sceNpMatching2GetRoomMemberDataExternalList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -784,12 +827,17 @@ error_code sceNpMatching2GetServerInfo(
|
||||
sceNp2.warning("sceNpMatching2GetServerInfo(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.get_server_status(ctxId, optParam, reqParam->serverId);
|
||||
const u32 request_id = nph.get_server_status(ctxId, optParam, reqParam->serverId);
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -824,6 +872,11 @@ error_code sceNpMatching2GetRoomSlotInfoLocal(SceNpMatching2ContextId ctxId, con
|
||||
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!roomSlotInfo)
|
||||
{
|
||||
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!ctxId)
|
||||
{
|
||||
return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
|
||||
@@ -857,7 +910,7 @@ error_code sceNpMatching2SendLobbyChatMessage(
|
||||
sceNp2.todo("sceNpMatching2SendLobbyChatMessage(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -933,12 +986,17 @@ error_code sceNpMatching2JoinRoom(
|
||||
sceNp2.warning("sceNpMatching2JoinRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.join_room(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.join_room(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1009,7 +1067,7 @@ error_code sceNpMatching2KickoutRoomMember(
|
||||
sceNp2.todo("sceNpMatching2KickoutRoomMember(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -1055,7 +1113,7 @@ error_code sceNpMatching2SetSignalingOptParam(
|
||||
sceNp2.todo("sceNpMatching2SetSignalingOptParam(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -1095,7 +1153,7 @@ error_code sceNpMatching2SendRoomChatMessage(
|
||||
sceNp2.todo("sceNpMatching2SendRoomChatMessage(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -1109,12 +1167,17 @@ error_code sceNpMatching2SetRoomDataInternal(
|
||||
sceNp2.warning("sceNpMatching2SetRoomDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.set_roomdata_internal(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.set_roomdata_internal(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1125,12 +1188,17 @@ error_code sceNpMatching2GetRoomDataInternal(
|
||||
sceNp2.warning("sceNpMatching2GetRoomDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.get_roomdata_internal(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.get_roomdata_internal(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1141,12 +1209,17 @@ error_code sceNpMatching2SignalingGetPingInfo(
|
||||
sceNp2.warning("sceNpMatching2SignalingGetPingInfo(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.get_ping_info(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.get_ping_info(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1206,7 +1279,7 @@ error_code sceNpMatching2GrantRoomOwner(
|
||||
sceNp2.todo("sceNpMatching2GrantRoomOwner(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -1266,8 +1339,19 @@ error_code sceNpMatching2RegisterSignalingCallback(SceNpMatching2ContextId ctxId
|
||||
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
auto ctx = get_match2_context(ctxId);
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND;
|
||||
}
|
||||
|
||||
std::lock_guard lock(ctx->mutex);
|
||||
ctx->signaling_cb = cbFunc;
|
||||
ctx->signaling_cb_arg = cbFuncArg;
|
||||
|
||||
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
sigh.set_sig2_cb(ctxId, cbFunc, cbFuncArg);
|
||||
sigh.add_match2_ctx(ctxId);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1292,7 +1376,7 @@ error_code sceNpMatching2GetUserInfoList(
|
||||
sceNp2.todo("sceNpMatching2GetUserInfoList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -1303,14 +1387,21 @@ error_code sceNpMatching2GetUserInfoList(
|
||||
error_code sceNpMatching2GetRoomMemberDataInternal(
|
||||
SceNpMatching2ContextId ctxId, vm::cptr<SceNpMatching2GetRoomMemberDataInternalRequest> reqParam, vm::cptr<SceNpMatching2RequestOptParam> optParam, vm::ptr<SceNpMatching2RequestId> assignedReqId)
|
||||
{
|
||||
sceNp2.todo("sceNpMatching2GetRoomMemberDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
sceNp2.warning("sceNpMatching2GetRoomMemberDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
const u32 request_id = nph.get_roommemberdata_internal(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1320,12 +1411,17 @@ error_code sceNpMatching2SetRoomMemberDataInternal(
|
||||
sceNp2.warning("sceNpMatching2SetRoomMemberDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.set_roommemberdata_internal(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.set_roommemberdata_internal(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1336,13 +1432,18 @@ error_code sceNpMatching2JoinProhibitiveRoom(
|
||||
sceNp2.warning("sceNpMatching2JoinProhibitiveRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
// TODO: add blocked users
|
||||
*assignedReqId = nph.join_room(ctxId, optParam, &reqParam->joinParam);
|
||||
const u32 request_id = nph.join_room(ctxId, optParam, &reqParam->joinParam);
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1367,7 +1468,7 @@ error_code sceNpMatching2DeleteServerContext(
|
||||
sceNp2.warning("sceNpMatching2DeleteServerContext(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -1377,7 +1478,12 @@ error_code sceNpMatching2DeleteServerContext(
|
||||
return SCE_NP_MATCHING2_ERROR_INVALID_SERVER_ID;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.delete_server_context(ctxId, optParam, reqParam->serverId);
|
||||
const u32 request_id = nph.delete_server_context(ctxId, optParam, reqParam->serverId);
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1460,12 +1566,15 @@ error_code sceNpMatching2GetRoomPasswordLocal(SceNpMatching2ContextId ctxId, Sce
|
||||
|
||||
if (password)
|
||||
{
|
||||
*withPassword = true;
|
||||
memcpy(roomPassword.get_ptr(), &*password, sizeof(SceNpMatching2SessionPassword));
|
||||
if (withPassword) *withPassword = true;
|
||||
if (roomPassword)
|
||||
{
|
||||
std::memcpy(roomPassword.get_ptr(), &*password, sizeof(SceNpMatching2SessionPassword));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*withPassword = false;
|
||||
if (withPassword) *withPassword = false;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
@@ -1477,12 +1586,17 @@ error_code sceNpMatching2GetRoomDataExternalList(
|
||||
sceNp2.warning("sceNpMatching2GetRoomDataExternalList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.get_roomdata_external_list(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.get_roomdata_external_list(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1493,12 +1607,17 @@ error_code sceNpMatching2CreateJoinRoom(
|
||||
sceNp2.warning("sceNpMatching2CreateJoinRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
*assignedReqId = nph.create_join_room(ctxId, optParam, reqParam.get_ptr());
|
||||
const u32 request_id = nph.create_join_room(ctxId, optParam, reqParam.get_ptr());
|
||||
|
||||
if (assignedReqId)
|
||||
{
|
||||
*assignedReqId = request_id;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1523,7 +1642,7 @@ error_code sceNpMatching2GetLobbyInfoList(
|
||||
sceNp2.todo("sceNpMatching2GetLobbyInfoList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -1552,7 +1671,7 @@ error_code sceNpMatching2SendLobbyInvitation(
|
||||
sceNp2.todo("sceNpMatching2SendLobbyInvitation(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
@@ -1562,7 +1681,7 @@ error_code sceNpMatching2SendLobbyInvitation(
|
||||
|
||||
error_code sceNpMatching2ContextStop(SceNpMatching2ContextId ctxId)
|
||||
{
|
||||
sceNp2.todo("sceNpMatching2ContextStop(ctxId=%d)", ctxId);
|
||||
sceNp2.warning("sceNpMatching2ContextStop(ctxId=%d)", ctxId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
@@ -1596,7 +1715,7 @@ error_code sceNpMatching2SetLobbyMemberDataInternal(
|
||||
sceNp2.todo("sceNpMatching2SetLobbyMemberDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
|
||||
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "sceNp.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
|
||||
#include "Emu/NP/np_handler.h"
|
||||
#include "Emu/NP/np_contexts.h"
|
||||
|
||||
@@ -61,22 +63,65 @@ void fmt_class_string<SceNpCommerce2Error>::format(std::string& out, u64 arg)
|
||||
STR_CASE(SCE_NP_COMMERCE2_SERVER_ERROR_VOUCHER_ALREADY_CONSUMED);
|
||||
STR_CASE(SCE_NP_COMMERCE2_SERVER_ERROR_EXCEEDS_AGE_LIMIT_IN_BROWSING);
|
||||
STR_CASE(SCE_NP_COMMERCE2_SYSTEM_UTIL_ERROR_INVALID_VOUCHER);
|
||||
STR_CASE(SCE_NP_COMMERCE_ERROR_REQ_BUSY);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
void initialize_common_data(SceNpCommerce2CommonData* common_data, vm::ptr<void> data, u32 internal_data_addr, u32 internal_data_size)
|
||||
{
|
||||
std::memset(common_data, 0, sizeof(SceNpCommerce2CommonData));
|
||||
common_data->version = SCE_NP_COMMERCE2_VERSION;
|
||||
common_data->buf_head = data.addr();
|
||||
common_data->data = internal_data_addr;
|
||||
common_data->data_size = internal_data_size;
|
||||
}
|
||||
|
||||
error_code check_and_get_data(vm::ptr<void> /*data*/, u32 data_size, u32& /*internal_data_addr*/, u32& /*internal_data_size*/)
|
||||
{
|
||||
if (data_size < 60)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_RESULT_DATA;
|
||||
|
||||
// TODO: check and extract data
|
||||
if (true)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_RESULT_DATA;
|
||||
|
||||
return not_an_error(60);
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2ExecuteStoreBrowse(s32 targetType, vm::cptr<char> targetId, s32 userdata)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2ExecuteStoreBrowse(targetType=%d, targetId=%s, userdata=%d)", targetType, targetId, userdata);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (targetType < SCE_NP_COMMERCE2_STORE_BROWSE_TYPE_CATEGORY || targetType > SCE_NP_COMMERCE2_STORE_BROWSE_TYPE_PRODUCT_CODE)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_TYPE;
|
||||
|
||||
if (targetType < SCE_NP_COMMERCE2_STORE_BROWSE_TYPE_PRODUCT_CODE && (!targetId || !targetId[0]))
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetStoreBrowseUserdata(vm::ptr<s32> userdata)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetStoreBrowseUserdata(userdata=*0x%x)", userdata);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!userdata)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
*userdata = 0; // TODO
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -84,6 +129,16 @@ error_code sceNpCommerce2Init()
|
||||
{
|
||||
sceNpCommerce2.warning("sceNpCommerce2Init()");
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_ALREADY_INITIALIZED;
|
||||
|
||||
if (!nph.is_NP_init)
|
||||
return SCE_NP_ERROR_NOT_INITIALIZED;
|
||||
|
||||
nph.is_NP_Com2_init = true;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -91,6 +146,10 @@ error_code sceNpCommerce2Term()
|
||||
{
|
||||
sceNpCommerce2.warning("sceNpCommerce2Term()");
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
nph.is_NP_Com2_init = false;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -98,7 +157,24 @@ error_code sceNpCommerce2CreateCtx(u32 version, vm::cptr<SceNpId> npId, vm::ptr<
|
||||
{
|
||||
sceNpCommerce2.warning("sceNpCommerce2CreateCtx(version=%d, npId=*0x%x, handler=*0x%x, arg=*0x%x, ctx_id=*0x%x)", version, npId, handler, arg, ctx_id);
|
||||
|
||||
*ctx_id = create_commerce2_context(version, npId, handler, arg);
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (version > SCE_NP_COMMERCE2_VERSION)
|
||||
return SCE_NP_COMMERCE2_ERROR_UNSUPPORTED_VERSION;
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_MAX;
|
||||
|
||||
if (!npId)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
if (ctx_id)
|
||||
*ctx_id = create_commerce2_context(version, npId, handler, arg);
|
||||
else
|
||||
sceNpCommerce2.warning("sceNpCommerce2CreateCtx: ctx_id is null");
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -107,18 +183,34 @@ s32 sceNpCommerce2DestroyCtx(u32 ctx_id)
|
||||
{
|
||||
sceNpCommerce2.warning("sceNpCommerce2DestroyCtx(ctx_id=%d)", ctx_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!destroy_commerce2_context(ctx_id))
|
||||
{
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND; // TODO: verify
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sceNpCommerce2EmptyStoreCheckStart(u32 ctx_id, s32 store_check_type, vm::cptr<char> target_id)
|
||||
error_code sceNpCommerce2EmptyStoreCheckStart(u32 ctx_id, s32 store_check_type, vm::cptr<char> target_id)
|
||||
{
|
||||
sceNpCommerce2.warning("sceNpCommerce2EmptyStoreCheckStart(ctx_id=%d, store_check_type=%d, target_id=*0x%x(%s))", ctx_id, store_check_type, target_id, target_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!target_id || !target_id[0])
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID;
|
||||
|
||||
if (store_check_type != SCE_NP_COMMERCE2_STORE_CHECK_TYPE_CATEGORY)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
|
||||
if (!ctx)
|
||||
@@ -141,25 +233,45 @@ s32 sceNpCommerce2EmptyStoreCheckStart(u32 ctx_id, s32 store_check_type, vm::cpt
|
||||
error_code sceNpCommerce2EmptyStoreCheckAbort(u32 ctx_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2EmptyStoreCheckAbort(ctx_id=%d)", ctx_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sceNpCommerce2EmptyStoreCheckFinish(u32 ctx_id, vm::ptr<s32> is_empty)
|
||||
error_code sceNpCommerce2EmptyStoreCheckFinish(u32 ctx_id, vm::ptr<s32> is_empty)
|
||||
{
|
||||
sceNpCommerce2.warning("sceNpCommerce2EmptyStoreCheckFinish(ctx_id=%d, is_empty=*0x%x)", ctx_id, is_empty);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!is_empty)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
*is_empty = SCE_NP_COMMERCE2_STORE_IS_NOT_EMPTY;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sceNpCommerce2CreateSessionStart(u32 ctx_id)
|
||||
error_code sceNpCommerce2CreateSessionStart(u32 ctx_id)
|
||||
{
|
||||
sceNpCommerce2.warning("sceNpCommerce2CreateSessionStart(ctx_id=%d)", ctx_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND; // TODO: verify
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (ctx->context_callback)
|
||||
@@ -177,61 +289,208 @@ s32 sceNpCommerce2CreateSessionStart(u32 ctx_id)
|
||||
error_code sceNpCommerce2CreateSessionAbort(u32 ctx_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2CreateSessionAbort(ctx_id=%d)", ctx_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sceNpCommerce2CreateSessionFinish(u32 ctx_id, vm::ptr<SceNpCommerce2SessionInfo> sessionInfo)
|
||||
{
|
||||
sceNpCommerce2.warning("sceNpCommerce2CreateSessionFinish(ctx_id=%d, sessionInfo=*0x%x)", ctx_id, sessionInfo);
|
||||
memset(sessionInfo.get_ptr(), 0, sizeof(sessionInfo));
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
s32 sdk_ver{};
|
||||
[[maybe_unused]] s32 ret = process_get_sdk_version(process_getpid(), sdk_ver);
|
||||
|
||||
if (sdk_ver < 0x300000)
|
||||
{
|
||||
std::memset(sessionInfo.get_ptr(), 0, 8); // currencyCode, decimals
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memset(sessionInfo.get_ptr(), 0, 44); // sizeof(SceNpCommerce2SessionInfo) - reserved
|
||||
}
|
||||
|
||||
// TODO
|
||||
if (false)
|
||||
{
|
||||
sessionInfo->currencyCode[0] = 'U';
|
||||
sessionInfo->currencyCode[1] = 'S';
|
||||
sessionInfo->currencyCode[2] = 'D';
|
||||
sessionInfo->currencyCode[3] = '\0';
|
||||
sessionInfo->decimals = 2;
|
||||
sessionInfo->currencySymbol[0] = '$';
|
||||
sessionInfo->currencySymbol[1] = '\0';
|
||||
sessionInfo->currencySymbol[2] = '\0';
|
||||
sessionInfo->currencySymbol[3] = '\0';
|
||||
sessionInfo->symbolPosition = SCE_NP_COMMERCE2_SYM_POS_PRE;
|
||||
sessionInfo->symbolWithSpace = false;
|
||||
sessionInfo->padding1[3];
|
||||
sessionInfo->thousandSeparator[0] = ',';
|
||||
sessionInfo->thousandSeparator[1] = '\0';
|
||||
sessionInfo->thousandSeparator[2] = '\0';
|
||||
sessionInfo->thousandSeparator[3] = '\0';
|
||||
sessionInfo->decimalLetter[0] = '.';
|
||||
sessionInfo->decimalLetter[1] = '\0';
|
||||
sessionInfo->decimalLetter[2] = '\0';
|
||||
sessionInfo->decimalLetter[3] = '\0';
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetCategoryContentsCreateReq(u32 ctx_id, vm::ptr<u32> req_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetCategoryContentsCreateReq(ctx_id=%d, req_id=*0x%x)", ctx_id, req_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!req_id)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetCategoryContentsStart(u32 req_id, vm::cptr<char> categoryId, u32 startPosition, u32 maxCountOfResults)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetCategoryContentsStart(req_id=%d, categoryId=%s, startPosition=%d, maxCountOfResults=%d)", req_id, categoryId, startPosition, maxCountOfResults);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!categoryId || maxCountOfResults > SCE_NP_COMMERCE2_GETCAT_MAX_COUNT)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code get_result(u32 /* req_id */, vm::ptr<void> buf, u32 buf_size, vm::ptr<u32> fill_size)
|
||||
{
|
||||
if (!buf || !buf_size || ! fill_size)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
// TODO
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetCategoryContentsGetResult(u32 req_id, vm::ptr<void> buf, u32 buf_size, vm::ptr<u32> fill_size)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetCategoryContentsGetResult(req_id=%d, buf=*0x%x, buf_size=%d, fill_size=*0x%x)", req_id, buf, buf_size, fill_size);
|
||||
return CELL_OK;
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (buf_size < SCE_NP_COMMERCE2_RECV_BUF_SIZE)
|
||||
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_BUF_SIZE;
|
||||
|
||||
return get_result(req_id, buf, buf_size, fill_size);
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2InitGetCategoryContentsResult(vm::ptr<SceNpCommerce2GetCategoryContentsResult> result, vm::ptr<void> data, u32 data_size)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2InitGetCategoryContentsResult(result=*0x%x, data=*0x%x, data_size=%d)", result, data, data_size);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!result || !data || !data_size)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
u32 internal_data_addr = 0, internal_data_size = 0;
|
||||
error_code error = check_and_get_data(data, data_size, internal_data_addr, internal_data_size);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
std::memset(result.get_ptr(), 0, sizeof(SceNpCommerce2GetCategoryContentsResult));
|
||||
initialize_common_data(&result->commonData, data, internal_data_addr, internal_data_size);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetCategoryInfo(vm::cptr<SceNpCommerce2GetCategoryContentsResult> result, vm::ptr<SceNpCommerce2CategoryInfo> categoryInfo)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetCategoryInfo(result=*0x%x, categoryInfo=*0x%x)", result, categoryInfo);
|
||||
return CELL_OK;
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!result || !categoryInfo)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
// Hack to stop crashes in some games
|
||||
return SCE_NP_COMMERCE2_ERROR_SERVER_MAINTENANCE;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetContentInfo(vm::cptr<SceNpCommerce2GetCategoryContentsResult> result, u32 index, vm::ptr<SceNpCommerce2ContentInfo> contentInfo)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetContentInfo(result=*0x%x, index=%d, contentInfo=*0x%x)", result, index, contentInfo);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!result || !contentInfo)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetCategoryInfoFromContentInfo(vm::cptr<SceNpCommerce2ContentInfo> contentInfo, vm::ptr<SceNpCommerce2CategoryInfo> categoryInfo)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetCategoryInfoFromContentInfo(contentInfo=*0x%x, categoryInfo=*0x%x)", contentInfo, categoryInfo);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!contentInfo || !categoryInfo)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetGameProductInfoFromContentInfo(vm::cptr<SceNpCommerce2ContentInfo> contentInfo, vm::ptr<SceNpCommerce2GameProductInfo> gameProductInfo)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetGameProductInfoFromContentInfo(contentInfo=*0x%x, gameProductInfo=*0x%x)", contentInfo, gameProductInfo);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!contentInfo || !gameProductInfo)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -244,30 +503,87 @@ error_code sceNpCommerce2DestroyGetCategoryContentsResult(vm::ptr<SceNpCommerce2
|
||||
error_code sceNpCommerce2GetProductInfoCreateReq(u32 ctx_id, vm::ptr<u32> req_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoCreateReq(ctx_id=%d, req_id=*0x%x)", ctx_id, req_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!req_id)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetProductInfoStart(u32 req_id, vm::cptr<char> categoryId, vm::cptr<char> productId)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoStart(req_id=%d, categoryId=%s, productId=%s)", req_id, categoryId, productId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!productId)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetProductInfoGetResult(u32 req_id, vm::ptr<void> buf, u32 buf_size, vm::ptr<u32> fill_size)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoGetResult(req_id=%d, buf=*0x%x, buf_size=%d, fill_size=*0x%x)", req_id, buf, buf_size, fill_size);
|
||||
return CELL_OK;
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (buf_size < SCE_NP_COMMERCE2_RECV_BUF_SIZE)
|
||||
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_BUF_SIZE;
|
||||
|
||||
return get_result(req_id, buf, buf_size, fill_size);
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2InitGetProductInfoResult(vm::ptr<SceNpCommerce2GetProductInfoResult> result, vm::ptr<void> data, u32 data_size)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2InitGetProductInfoResult(result=*0x%x, data=*0x%x, data_size=%d)", result, data, data_size);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!result || !data || !data_size)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
u32 internal_data_addr = 0, internal_data_size = 0;
|
||||
error_code error = check_and_get_data(data, data_size, internal_data_addr, internal_data_size);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
std::memset(result.get_ptr(), 0, sizeof(SceNpCommerce2GetProductInfoResult));
|
||||
initialize_common_data(&result->commonData, data, internal_data_addr, internal_data_size);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetGameProductInfo(vm::cptr<SceNpCommerce2GetProductInfoResult> result, vm::ptr<SceNpCommerce2GameProductInfo> gameProductInfo)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetGameProductInfo(result=*0x%x, gameProductInfo=*0x%x)", result, gameProductInfo);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!result || !gameProductInfo)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -280,30 +596,87 @@ error_code sceNpCommerce2DestroyGetProductInfoResult(vm::ptr<SceNpCommerce2GetPr
|
||||
error_code sceNpCommerce2GetProductInfoListCreateReq(u32 ctx_id, vm::ptr<u32> req_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoListCreateReq(ctx_id=%d, req_id=*0x%x)", ctx_id, req_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!req_id)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetProductInfoListStart(u32 req_id, vm::cptr<char[]> productIds, u32 productNum)
|
||||
error_code sceNpCommerce2GetProductInfoListStart(u32 req_id, vm::cpptr<char> productIds, u32 productNum)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoListStart(req_id=%d, productIds=*0x%x, productNum=%d)", req_id, productIds, productNum);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!productIds || !productNum || productNum > SCE_NP_COMMERCE2_GETPRODLIST_MAX_COUNT)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetProductInfoListGetResult(u32 req_id, vm::ptr<void> buf, u32 buf_size, vm::ptr<u32> fill_size)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoListGetResult(req_id=%d, buf=*0x%x, buf_size=%d, fill_size=*0x%x)", req_id, buf, buf_size, fill_size);
|
||||
return CELL_OK;
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (buf_size < SCE_NP_COMMERCE2_RECV_BUF_SIZE)
|
||||
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_BUF_SIZE;
|
||||
|
||||
return get_result(req_id, buf, buf_size, fill_size);
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2InitGetProductInfoListResult(vm::ptr<SceNpCommerce2GetProductInfoListResult> result, vm::ptr<void> data, u32 data_size)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2InitGetProductInfoListResult(result=*0x%x, data=*0x%x, data_size=%d)", result, data, data_size);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!result || !data || !data_size)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
u32 internal_data_addr = 0, internal_data_size = 0;
|
||||
error_code error = check_and_get_data(data, data_size, internal_data_addr, internal_data_size);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
std::memset(result.get_ptr(), 0, sizeof(SceNpCommerce2GetProductInfoListResult));
|
||||
initialize_common_data(&result->commonData, data, internal_data_addr, internal_data_size);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetGameProductInfoFromGetProductInfoListResult(vm::cptr<SceNpCommerce2GetProductInfoListResult> result, u32 index, vm::ptr<SceNpCommerce2GameProductInfo> gameProductInfo)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetGameProductInfoFromGetProductInfoListResult(result=*0x%x, index=%d, data=*0x%x)", result, index, gameProductInfo);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!result || !gameProductInfo)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -316,114 +689,381 @@ error_code sceNpCommerce2DestroyGetProductInfoListResult(vm::ptr<SceNpCommerce2G
|
||||
error_code sceNpCommerce2GetContentRatingInfoFromGameProductInfo(vm::cptr<SceNpCommerce2GameProductInfo> gameProductInfo, vm::ptr<SceNpCommerce2ContentRatingInfo> contentRatingInfo)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetContentRatingInfoFromGameProductInfo(gameProductInfo=*0x%x, contentRatingInfo=*0x%x)", gameProductInfo, contentRatingInfo);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!gameProductInfo || !contentRatingInfo)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetContentRatingInfoFromCategoryInfo(vm::cptr<SceNpCommerce2CategoryInfo> categoryInfo, vm::ptr<SceNpCommerce2ContentRatingInfo> contentRatingInfo)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetContentRatingInfoFromCategoryInfo(categoryInfo=*0x%x, contentRatingInfo=*0x%x)", categoryInfo, contentRatingInfo);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!categoryInfo || !contentRatingInfo)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetContentRatingDescriptor(vm::cptr<SceNpCommerce2ContentRatingInfo> contentRatingInfo, u32 index, vm::ptr<SceNpCommerce2ContentRatingDescriptor> contentRatingDescriptor)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetContentRatingDescriptor(contentRatingInfo=*0x%x, index=%d, contentRatingDescriptor=*0x%x)", contentRatingInfo, index, contentRatingDescriptor);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!contentRatingInfo || !contentRatingDescriptor)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetGameSkuInfoFromGameProductInfo(vm::cptr<SceNpCommerce2GameProductInfo> gameProductInfo, u32 index, vm::ptr<SceNpCommerce2GameSkuInfo> gameSkuInfo)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetGameSkuInfoFromGameProductInfo(gameProductInfo=*0x%x, index=%d, gameSkuInfo=*0x%x)", gameProductInfo, index, gameSkuInfo);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!gameProductInfo || !gameSkuInfo)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetPrice(u32 ctx_id, vm::ptr<char> buf, u32 buflen, u32 price)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetPrice(ctx_id=%d, buf=*0x%x, buflen=%d, price=%d)", ctx_id, buf, buflen, price);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DoCheckoutStartAsync(u32 ctx_id, vm::cptr<char[]> sku_ids, u32 sku_num, u32 container)
|
||||
error_code sceNpCommerce2DoCheckoutStartAsync(u32 ctx_id, vm::cpptr<char> sku_ids, u32 sku_num, u32 container)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoCheckoutStartAsync(ctx_id=%d, sku_ids=*0x%x, sku_num=%d, container=%d)", ctx_id, sku_ids, sku_num, container);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (sku_num > SCE_NP_COMMERCE2_SKU_CHECKOUT_MAX)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_SKU_NUM;
|
||||
|
||||
for (u32 i = 0; i < sku_num; i++)
|
||||
{
|
||||
if (!sku_ids[i] || !sku_ids[i][0])
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_SKUID;
|
||||
}
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_MEMORY_CONTAINER;
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_MEMORY_CONTAINER;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DoCheckoutFinishAsync(u32 ctx_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoCheckoutFinishAsync(ctx_id=%d)", ctx_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DoProductBrowseStartAsync(u32 ctx_id, vm::cptr<char> product_id, u32 container, vm::cptr<SceNpCommerce2ProductBrowseParam> param)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoProductBrowseStartAsync(ctx_id=%d, product_id=%s, container=%d, param=*0x%x)", ctx_id, product_id, container, param);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!product_id || !product_id[0])
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
if (param && param->size != sizeof(SceNpCommerce2ProductBrowseParam))
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_SIZE;
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_MEMORY_CONTAINER;
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_MEMORY_CONTAINER;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DoProductBrowseFinishAsync(u32 ctx_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoProductBrowseFinishAsync(ctx_id=%d)", ctx_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DoDlListStartAsync(u32 ctx_id, vm::cptr<char> service_id, vm::cptr<char[]> sku_ids, u32 sku_num, u32 container)
|
||||
error_code sceNpCommerce2DoDlListStartAsync(u32 ctx_id, vm::cptr<char> service_id, vm::cpptr<char> sku_ids, u32 sku_num, u32 container)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoDlListStartAsync(ctx_id=%d, service_id=%s, sku_ids=*0x%x, sku_num=%d, container=%d)", ctx_id, service_id, sku_ids, sku_num, container);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (sku_num > SCE_NP_COMMERCE2_SKU_CHECKOUT_MAX)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_SKU_NUM;
|
||||
|
||||
if (sku_num == 0)
|
||||
{
|
||||
if (!service_id)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 i = 0; i < sku_num; i++)
|
||||
{
|
||||
if (!sku_ids[i] || !sku_ids[i][0])
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_SKUID;
|
||||
}
|
||||
}
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_MEMORY_CONTAINER;
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_MEMORY_CONTAINER;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DoDlListFinishAsync(u32 ctx_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoDlListFinishAsync(ctx_id=%d)", ctx_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DoProductCodeStartAsync(u32 ctx_id, u32 container, vm::cptr<SceNpCommerce2ProductCodeParam> param)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoProductCodeStartAsync(ctx_id=%d, container=%d, param=*0x%x)", ctx_id, container, param);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!param)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
if (param->size != sizeof(SceNpCommerce2ProductCodeParam))
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_SIZE;
|
||||
|
||||
if (param->inputMode != SCE_NP_COMMERCE2_PRODUCT_CODE_INPUT_MODE_USER_INPUT)
|
||||
{
|
||||
if (param->inputMode != SCE_NP_COMMERCE2_PRODUCT_CODE_INPUT_MODE_CODE_SPECIFIED)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_TYPE;
|
||||
|
||||
if (!param->code1[0] || !param->code2[0] || !param->code3[0])
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID;
|
||||
|
||||
for (u32 i = 0; i < SCE_NP_COMMERCE2_PRODUCT_CODE_BLOCK_LEN; i++)
|
||||
{
|
||||
if (!isalnum(param->code1[i]) || !isalnum(param->code2[i]) || !isalnum(param->code3[i]))
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID;
|
||||
}
|
||||
}
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_MEMORY_CONTAINER;
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_MEMORY_CONTAINER;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DoProductCodeFinishAsync(u32 ctx_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoProductCodeFinishAsync(ctx_id=%d)", ctx_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2GetBGDLAvailability(vm::ptr<b8> bgdlAvailability)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2GetBGDLAvailability(bgdlAvailability=*0x%x)", bgdlAvailability);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!bgdlAvailability)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
*bgdlAvailability = false; // TODO
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2SetBGDLAvailability(b8 bgdlAvailability)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2SetBGDLAvailability(bgdlAvailability=%d)", bgdlAvailability);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2AbortReq(u32 req_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2AbortReq(req_id=%d)", req_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DestroyReq(u32 req_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DestroyReq(req_id=%d)", req_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DoServiceListStartAsync()
|
||||
error_code sceNpCommerce2DoServiceListStartAsync(u32 ctx_id, vm::ptr<char> serviceId, u32 container, vm::ptr<SceNpCommerce2ProductBrowseParam> param)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoServiceListStartAsync()");
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoServiceListStartAsync(ctx_id=%d, serviceId=*0x%x, container=%d, param=*0x%x)", ctx_id, serviceId, container, param);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (!param || !serviceId)
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
if (param->size != sizeof(SceNpCommerce2ProductBrowseParam))
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_SIZE;
|
||||
|
||||
if (!serviceId[0])
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID;
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_INVALID_MEMORY_CONTAINER;
|
||||
|
||||
if (false) // TODO
|
||||
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_MEMORY_CONTAINER;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpCommerce2DoServiceListFinishAsync()
|
||||
error_code sceNpCommerce2DoServiceListFinishAsync(u32 ctx_id)
|
||||
{
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoServiceListFinishAsync()");
|
||||
sceNpCommerce2.todo("sceNpCommerce2DoServiceListFinishAsync(ctx_id=%d)", ctx_id);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_Com2_init)
|
||||
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
|
||||
|
||||
const auto ctx = get_commerce2_context(ctx_id);
|
||||
if (!ctx)
|
||||
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ enum SceNpCommerce2Error
|
||||
SCE_NP_COMMERCE2_SERVER_ERROR_VOUCHER_ALREADY_CONSUMED = 0x80023130,
|
||||
SCE_NP_COMMERCE2_SERVER_ERROR_EXCEEDS_AGE_LIMIT_IN_BROWSING = 0x80023139,
|
||||
SCE_NP_COMMERCE2_SYSTEM_UTIL_ERROR_INVALID_VOUCHER = 0x80024002,
|
||||
SCE_NP_COMMERCE_ERROR_REQ_BUSY = 0x8002940d,
|
||||
};
|
||||
|
||||
// Event types
|
||||
@@ -266,6 +265,7 @@ struct SceNpCommerce2GameProductInfo
|
||||
struct SceNpCommerce2GetProductInfoListResult
|
||||
{
|
||||
SceNpCommerce2CommonData commonData;
|
||||
be_t<u32> countOfProductInfo;
|
||||
};
|
||||
|
||||
// Structure for rating information
|
||||
@@ -317,7 +317,7 @@ struct SceNpCommerce2ProductBrowseParam
|
||||
struct SceNpCommerce2ProductCodeParam
|
||||
{
|
||||
be_t<u32> size;
|
||||
be_t<u32> inputMode; // Unsigned ints go into be_t<u32>, right?
|
||||
be_t<u32> inputMode;
|
||||
s8 code1[SCE_NP_COMMERCE2_PRODUCT_CODE_BLOCK_LEN + 1];
|
||||
s8 padding1[3];
|
||||
s8 code2[SCE_NP_COMMERCE2_PRODUCT_CODE_BLOCK_LEN + 1];
|
||||
|
||||
@@ -8,7 +8,7 @@ error_code sceNpManagerIsSP()
|
||||
{
|
||||
sceNpPlus.warning("sceNpManagerIsSP()");
|
||||
// TODO seems to be cut to 1 byte by pshome likely a bool but may be more.
|
||||
return CELL_OK;
|
||||
return not_an_error(1);
|
||||
}
|
||||
|
||||
DECLARE(ppu_module_manager::sceNpPlus)("sceNpPlus", []()
|
||||
|
||||
+326
-178
@@ -288,33 +288,44 @@ error_code sceNpTusPollAsync(s32 transId, vm::ptr<s32> result)
|
||||
template<typename T>
|
||||
error_code scenp_tus_set_multislot_variable(s32 transId, T targetNpId, vm::cptr<SceNpTusSlotId> slotIdArray, vm::cptr<s64> variableArray, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
|
||||
{
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!targetNpId || !slotIdArray || !variableArray || arrayNum <= 0)
|
||||
if (!slotIdArray || !variableArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum == 0 || option)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
}
|
||||
|
||||
if (option || !is_slot_array_valid(slotIdArray, arrayNum))
|
||||
if (!is_slot_array_valid(slotIdArray, arrayNum))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!targetNpId)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
|
||||
if (!trans_ctx)
|
||||
@@ -359,29 +370,12 @@ error_code sceNpTusSetMultiSlotVariableVUserAsync(s32 transId, vm::cptr<SceNpTus
|
||||
template<typename T>
|
||||
error_code scenp_tus_get_multislot_variable(s32 transId, T targetNpId, vm::cptr<SceNpTusSlotId> slotIdArray, vm::ptr<SceNpTusVariable> variableArray, u32 variableArraySize, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
|
||||
{
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!targetNpId || !slotIdArray || !variableArray || arrayNum <= 0)
|
||||
if (!slotIdArray || !variableArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
}
|
||||
|
||||
if (option || !is_slot_array_valid(slotIdArray, arrayNum))
|
||||
if (arrayNum == 0 || option)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
@@ -391,6 +385,34 @@ error_code scenp_tus_get_multislot_variable(s32 transId, T targetNpId, vm::cptr<
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
}
|
||||
|
||||
if (!is_slot_array_valid(slotIdArray, arrayNum))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!targetNpId)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
|
||||
if (!trans_ctx)
|
||||
@@ -435,6 +457,31 @@ error_code sceNpTusGetMultiSlotVariableVUserAsync(s32 transId, vm::cptr<SceNpTus
|
||||
template<typename T>
|
||||
error_code scenp_tus_get_multiuser_variable(s32 transId, T targetNpIdArray, SceNpTusSlotId slotId, vm::ptr<SceNpTusVariable> variableArray, u32 variableArraySize, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
|
||||
{
|
||||
if (!variableArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum == 0 || option)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_SCORE_MAX_NPID_NUM_PER_TRANS)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
|
||||
}
|
||||
|
||||
if (variableArraySize != arrayNum * sizeof(SceNpTusVariable))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
if (slotId < 0)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
@@ -442,29 +489,15 @@ error_code scenp_tus_get_multiuser_variable(s32 transId, T targetNpIdArray, SceN
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!targetNpIdArray || !variableArray || arrayNum <= 0)
|
||||
if (!targetNpIdArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
}
|
||||
|
||||
if (option || slotId < 0)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (variableArraySize != arrayNum * sizeof(SceNpTusVariable))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
@@ -516,6 +549,43 @@ error_code sceNpTusGetMultiUserVariableVUserAsync(s32 transId, vm::cptr<SceNpTus
|
||||
|
||||
error_code scenp_tus_get_friends_variable(s32 transId, SceNpTusSlotId slotId, s32 includeSelf, s32 sortType, vm::ptr<SceNpTusVariable> variableArray, u32 variableArraySize, s32 arrayNum, vm::ptr<void> option, bool async)
|
||||
{
|
||||
if (!variableArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum == 0)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// Undocumented behaviour and structure unknown
|
||||
// Also checks a u32* at offset 4 of the struct for nullptr in which case it behaves like option == nullptr
|
||||
if (option && *static_cast<u32*>(option.get_ptr()) != 0xC)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (sortType < SCE_NP_TUS_VARIABLE_SORTTYPE_DESCENDING_DATE || sortType > SCE_NP_TUS_VARIABLE_SORTTYPE_ASCENDING_VALUE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_SCORE_MAX_SELECTED_FRIENDS_NUM)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
|
||||
}
|
||||
|
||||
if (variableArraySize != arrayNum * sizeof(SceNpTusVariable))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
if (slotId < 0)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
@@ -523,31 +593,12 @@ error_code scenp_tus_get_friends_variable(s32 transId, SceNpTusSlotId slotId, s3
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!variableArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
}
|
||||
|
||||
if (option || arrayNum <= 0 || slotId < 0)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (variableArraySize != arrayNum * sizeof(SceNpTusVariable))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
|
||||
if (!trans_ctx)
|
||||
@@ -587,9 +638,14 @@ error_code scenp_tus_add_and_get_variable(s32 transId, T targetNpId, SceNpTusSlo
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
if (slotId < 0)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if ((option && option->size != sizeof(SceNpTusAddAndGetVariableOptParam)) || outVariableSize != sizeof(SceNpTusVariable))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
if (!targetNpId)
|
||||
@@ -597,14 +653,10 @@ error_code scenp_tus_add_and_get_variable(s32 transId, T targetNpId, SceNpTusSlo
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (slotId < 0)
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (outVariableSize != sizeof(SceNpTusVariable))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
@@ -658,12 +710,7 @@ error_code scenp_tus_try_and_set_variable(s32 transId, T targetNpId, SceNpTusSlo
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!targetNpId || !resultVariable)
|
||||
if (!resultVariable)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
@@ -673,11 +720,22 @@ error_code scenp_tus_try_and_set_variable(s32 transId, T targetNpId, SceNpTusSlo
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (resultVariableSize != sizeof(SceNpTusVariable))
|
||||
if ((option && option->size != sizeof(SceNpTusTryAndSetVariableOptParam)) || resultVariableSize != sizeof(SceNpTusVariable))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
if (!targetNpId)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
|
||||
if (!trans_ctx)
|
||||
@@ -722,33 +780,39 @@ error_code sceNpTusTryAndSetVariableVUserAsync(s32 transId, vm::cptr<SceNpTusVir
|
||||
template<typename T>
|
||||
error_code scenp_tus_delete_multislot_variable(s32 transId, T targetNpId, vm::cptr<SceNpTusSlotId> slotIdArray, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
|
||||
{
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!targetNpId || !slotIdArray || arrayNum <= 0)
|
||||
if (!slotIdArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum == 0 || option)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
}
|
||||
|
||||
if (option || !is_slot_array_valid(slotIdArray, arrayNum))
|
||||
if (!is_slot_array_valid(slotIdArray, arrayNum))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
|
||||
if (!trans_ctx)
|
||||
@@ -793,6 +857,16 @@ error_code sceNpTusDeleteMultiSlotVariableVUserAsync(s32 transId, vm::cptr<SceNp
|
||||
template<typename T>
|
||||
error_code scenp_tus_set_data(s32 transId, T targetNpId, SceNpTusSlotId slotId, u32 totalSize, u32 sendSize, vm::cptr<void> data, vm::cptr<SceNpTusDataInfo> info, u32 infoStructSize, vm::ptr<SceNpTusSetDataOptParam> option, bool vuser, bool async)
|
||||
{
|
||||
if (slotId < 0 || !data || !totalSize)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if ((option && option->size != sizeof(SceNpTusSetDataOptParam)) || (info && infoStructSize != sizeof(SceNpTusDataInfo)))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
@@ -800,19 +874,15 @@ error_code scenp_tus_set_data(s32 transId, T targetNpId, SceNpTusSlotId slotId,
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!targetNpId || !data || totalSize == 0)
|
||||
if (!targetNpId)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (slotId < 0)
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
@@ -859,18 +929,6 @@ error_code sceNpTusSetDataVUserAsync(s32 transId, vm::cptr<SceNpTusVirtualUserId
|
||||
template<typename T>
|
||||
error_code scenp_tus_get_data(s32 transId, T targetNpId, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<void> option, bool vuser, bool async)
|
||||
{
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!targetNpId)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
@@ -881,6 +939,24 @@ error_code scenp_tus_get_data(s32 transId, T targetNpId, SceNpTusSlotId slotId,
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (dataStatusSize != sizeof(SceNpTusDataStatus))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
|
||||
if (!trans_ctx)
|
||||
@@ -925,6 +1001,31 @@ error_code sceNpTusGetDataVUserAsync(s32 transId, vm::cptr<SceNpTusVirtualUserId
|
||||
template<typename T>
|
||||
error_code scenp_tus_get_multislot_data_status(s32 transId, T targetNpId, vm::cptr<SceNpTusSlotId> slotIdArray, vm::ptr<SceNpTusDataStatus> statusArray, u32 statusArraySize, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
|
||||
{
|
||||
if (!slotIdArray || !statusArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum == 0 || option)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (statusArraySize != arrayNum * sizeof(SceNpTusDataStatus))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
}
|
||||
|
||||
if (!is_slot_array_valid(slotIdArray, arrayNum))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
@@ -932,24 +1033,15 @@ error_code scenp_tus_get_multislot_data_status(s32 transId, T targetNpId, vm::cp
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!targetNpId || !statusArray || arrayNum <= 0)
|
||||
if (!targetNpId)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (option || !is_slot_array_valid(slotIdArray, arrayNum))
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
@@ -996,6 +1088,31 @@ error_code sceNpTusGetMultiSlotDataStatusVUserAsync(s32 transId, vm::cptr<SceNpT
|
||||
template<typename T>
|
||||
error_code scenp_tus_get_multiuser_data_status(s32 transId, T targetNpIdArray, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> statusArray, u32 statusArraySize, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
|
||||
{
|
||||
if (!statusArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum == 0 || option)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_SCORE_MAX_NPID_NUM_PER_TRANS)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
|
||||
}
|
||||
|
||||
if (statusArraySize != arrayNum * sizeof(SceNpTusDataStatus))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
if (slotId < 0)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
@@ -1003,24 +1120,15 @@ error_code scenp_tus_get_multiuser_data_status(s32 transId, T targetNpIdArray, S
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!targetNpIdArray || !statusArray || arrayNum <= 0)
|
||||
if (!targetNpIdArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (option || slotId < 0)
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_TUS_MAX_USER_NUM_PER_TRANS)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
@@ -1072,6 +1180,43 @@ error_code sceNpTusGetMultiUserDataStatusVUserAsync(s32 transId, vm::cptr<SceNpT
|
||||
|
||||
error_code scenp_tus_get_friends_data_status(s32 transId, SceNpTusSlotId slotId, s32 includeSelf, s32 sortType, vm::ptr<SceNpTusDataStatus> statusArray, u32 statusArraySize, s32 arrayNum, vm::ptr<void> option, bool async)
|
||||
{
|
||||
if (!statusArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum == 0)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// Undocumented behaviour and structure unknown
|
||||
// Also checks a u32* at offset 4 of the struct for nullptr in which case it behaves like option == nullptr
|
||||
if (option && *static_cast<u32*>(option.get_ptr()) != 0xC)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (sortType != SCE_NP_TUS_DATASTATUS_SORTTYPE_DESCENDING_DATE && sortType != SCE_NP_TUS_DATASTATUS_SORTTYPE_ASCENDING_DATE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_SCORE_MAX_SELECTED_FRIENDS_NUM)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
|
||||
}
|
||||
|
||||
if (statusArraySize != arrayNum * sizeof(SceNpTusDataStatus))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
if (slotId < 0)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
@@ -1079,26 +1224,12 @@ error_code scenp_tus_get_friends_data_status(s32 transId, SceNpTusSlotId slotId,
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!statusArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (option || arrayNum < 0 || slotId < 0)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (arrayNum > SCE_NP_TUS_MAX_SELECTED_FRIENDS_NUM)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
|
||||
if (!trans_ctx)
|
||||
@@ -1131,24 +1262,12 @@ error_code sceNpTusGetFriendsDataStatusAsync(s32 transId, SceNpTusSlotId slotId,
|
||||
template<typename T>
|
||||
error_code scenp_tus_delete_multislot_data(s32 transId, T targetNpId, vm::cptr<SceNpTusSlotId> slotIdArray, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
|
||||
{
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
if (!targetNpId || !slotIdArray || arrayNum < 0)
|
||||
if (!slotIdArray)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (option || !is_slot_array_valid(slotIdArray, arrayNum))
|
||||
if (arrayNum == 0 || option)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
@@ -1158,6 +1277,24 @@ error_code scenp_tus_delete_multislot_data(s32 transId, T targetNpId, vm::cptr<S
|
||||
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
|
||||
}
|
||||
|
||||
if (!is_slot_array_valid(slotIdArray, arrayNum))
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.is_NP_TUS_init)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Probable vsh behaviour
|
||||
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
|
||||
{
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
|
||||
}
|
||||
|
||||
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
|
||||
|
||||
if (!trans_ctx)
|
||||
@@ -1200,9 +1337,16 @@ error_code sceNpTusDeleteMultiSlotDataVUserAsync(s32 transId, vm::cptr<SceNpTusV
|
||||
return scenp_tus_delete_multislot_data(transId, targetVirtualUserId, slotIdArray, arrayNum, option, true, true);
|
||||
}
|
||||
|
||||
void scenp_tss_no_file(const std::shared_ptr<tus_transaction_ctx>& trans, vm::ptr<SceNpTssDataStatus> dataStatus)
|
||||
{
|
||||
// TSS are files stored on PSN by developers, no dumps available atm
|
||||
std::memset(dataStatus.get_ptr(), 0, sizeof(SceNpTssDataStatus));
|
||||
trans->result = not_an_error(0);
|
||||
}
|
||||
|
||||
error_code sceNpTssGetData(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<SceNpTssGetDataOptParam> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTssGetData(transId=%d, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
|
||||
sceNpTus.warning("sceNpTssGetData(transId=%d, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
@@ -1228,12 +1372,14 @@ error_code sceNpTssGetData(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssD
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
scenp_tss_no_file(trans_ctx, dataStatus);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpTssGetDataAsync(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<SceNpTssGetDataOptParam> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTssGetDataAsync(transId=%d, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
|
||||
sceNpTus.warning("sceNpTssGetDataAsync(transId=%d, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
@@ -1259,6 +1405,8 @@ error_code sceNpTssGetDataAsync(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceN
|
||||
return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
|
||||
}
|
||||
|
||||
scenp_tss_no_file(trans_ctx, dataStatus);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -279,6 +279,7 @@ static void ppu_initialize_modules(ppu_linkage_info* link, utils::serial* ar = n
|
||||
&ppu_module_manager::sceNpClans,
|
||||
&ppu_module_manager::sceNpCommerce2,
|
||||
&ppu_module_manager::sceNpMatchingInt,
|
||||
&ppu_module_manager::sceNpPlus,
|
||||
&ppu_module_manager::sceNpSns,
|
||||
&ppu_module_manager::sceNpTrophy,
|
||||
&ppu_module_manager::sceNpTus,
|
||||
@@ -1339,6 +1340,11 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment&
|
||||
// Try to patch each segment, will only succeed if the address exists in SPU local storage
|
||||
for (const auto& prog : obj.progs)
|
||||
{
|
||||
if (Emu.DeserialManager())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply the patch
|
||||
applied += g_fxo->get<patch_engine>().apply(hash, [&](u32 addr, u32 /*size*/) { return addr + elf_header + prog.p_offset; }, prog.p_filesz, prog.p_vaddr);
|
||||
|
||||
@@ -1807,7 +1813,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo
|
||||
|
||||
std::basic_string<u32> applied;
|
||||
|
||||
for (usz i = 0; i < prx->segs.size(); i++)
|
||||
for (usz i = Emu.DeserialManager() ? prx->segs.size() : 0; i < prx->segs.size(); i++)
|
||||
{
|
||||
const auto& seg = prx->segs[i];
|
||||
|
||||
@@ -2856,9 +2862,9 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
|
||||
}
|
||||
|
||||
// Apply the patch
|
||||
auto applied = g_fxo->get<patch_engine>().apply(hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr<u8>(addr, size); });
|
||||
auto applied = g_fxo->get<patch_engine>().apply(!Emu.DeserialManager() ? std::string{} : hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr<u8>(addr, size); });
|
||||
|
||||
if (!Emu.GetTitleID().empty())
|
||||
if (!Emu.DeserialManager() && !Emu.GetTitleID().empty())
|
||||
{
|
||||
// Alternative patch
|
||||
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr<u8>(addr, size); });
|
||||
|
||||
@@ -3798,20 +3798,37 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
{
|
||||
mself_record rec{};
|
||||
|
||||
std::set<u64> offs;
|
||||
|
||||
if (mself.read(rec) && rec.get_pos(mself.size()))
|
||||
{
|
||||
std::string name = rec.name;
|
||||
if (rec.size <= 0x20)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!offs.emplace(rec.off).second)
|
||||
{
|
||||
// Duplicate
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read characters safely
|
||||
std::string name(sizeof(rec.name), '\0');
|
||||
|
||||
std::memcpy(name.data(), rec.name, name.size());
|
||||
name = std::string(name.c_str());
|
||||
|
||||
upper = fmt::to_upper(name);
|
||||
|
||||
if (upper.ends_with(".SPRX"))
|
||||
if (upper.find(".SPRX") != umax || upper.find(".PRX") != umax)
|
||||
{
|
||||
// .sprx inside .mself found
|
||||
file_queue.emplace_back(dir_queue[i] + entry.name, rec.off, rec.size);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (upper.ends_with(".SELF"))
|
||||
if (upper.find(".SELF") != umax || upper.find(".ELF") != umax)
|
||||
{
|
||||
// .self inside .mself found
|
||||
file_queue.emplace_back(dir_queue[i] + entry.name, rec.off, rec.size);
|
||||
@@ -3881,7 +3898,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
if (u64 off = offset)
|
||||
{
|
||||
// Adjust offset for MSELF
|
||||
src = make_file_view(std::move(src), offset);
|
||||
src = make_file_view(std::move(src), offset, file_size);
|
||||
|
||||
// Adjust path for MSELF too
|
||||
fmt::append(path, "_x%x", off);
|
||||
|
||||
@@ -276,6 +276,7 @@ public:
|
||||
const u32 stack_addr; // Stack address
|
||||
|
||||
atomic_t<ppu_join_status> joiner; // Joining thread or status
|
||||
u32 hw_sleep_time = 0; // Very specific delay for hardware threads switching, see lv2_obj::awake_unlocked for more details
|
||||
|
||||
lf_fifo<atomic_t<cmd64>, 127> cmd_queue; // Command queue for asynchronous operations.
|
||||
|
||||
|
||||
@@ -2756,16 +2756,77 @@ void spu_recompiler::FSMB(spu_opcode_t op)
|
||||
void spu_recompiler::FREST(spu_opcode_t op)
|
||||
{
|
||||
const XmmLink& va = XmmGet(op.ra, XmmType::Float);
|
||||
c->rcpps(va, va);
|
||||
c->movaps(SPU_OFF_128(gpr, op.rt), va);
|
||||
const XmmLink& v_fraction = XmmAlloc();
|
||||
const XmmLink& v_exponent = XmmAlloc();
|
||||
const XmmLink& v_sign = XmmAlloc();
|
||||
c->movdqa(v_fraction, va);
|
||||
c->movdqa(v_exponent, va);
|
||||
c->movdqa(v_sign, va);
|
||||
|
||||
c->psrld(v_fraction, 18);
|
||||
c->psrld(v_exponent, 23);
|
||||
|
||||
c->andps(v_fraction, XmmConst(v128::from32p(0x1F)));
|
||||
c->andps(v_exponent, XmmConst(v128::from32p(0xFF)));
|
||||
c->andps(v_sign, XmmConst(v128::from32p(0x80000000)));
|
||||
|
||||
const u64 fraction_lut_addr = reinterpret_cast<u64>(spu_frest_fraction_lut);
|
||||
const u64 exponent_lut_addr = reinterpret_cast<u64>(spu_frest_exponent_lut);
|
||||
|
||||
for (u32 index = 0; index < 4; index++)
|
||||
{
|
||||
c->pextrd(*qw0, v_fraction, index);
|
||||
c->mov(*qw1, asmjit::x86::dword_ptr(fraction_lut_addr, *qw0, 2));
|
||||
c->pinsrd(v_fraction, *qw1, index);
|
||||
|
||||
c->pextrd(*qw0, v_exponent, index);
|
||||
c->mov(*qw1, asmjit::x86::dword_ptr(exponent_lut_addr, *qw0, 2));
|
||||
c->pinsrd(v_exponent, *qw1, index);
|
||||
}
|
||||
|
||||
// AVX2(not working?)
|
||||
// c->mov(qw1->r64(),spu_frest_fraction_lut);
|
||||
// c->vpgatherdd(v_fraction, asmjit::x86::dword_ptr(*qw1));
|
||||
// c->mov(qw0->r64(),spu_frest_exponent_lut);
|
||||
// c->vpgatherdd(v_exponent, asmjit::x86::dword_ptr(*qw0));
|
||||
|
||||
c->orps(v_fraction, v_exponent);
|
||||
c->orps(v_sign, v_fraction);
|
||||
|
||||
c->movaps(SPU_OFF_128(gpr, op.rt), v_sign);
|
||||
}
|
||||
|
||||
void spu_recompiler::FRSQEST(spu_opcode_t op)
|
||||
{
|
||||
const XmmLink& va = XmmGet(op.ra, XmmType::Float);
|
||||
c->andps(va, XmmConst(v128::from32p(0x7fffffff))); // abs
|
||||
c->rsqrtps(va, va);
|
||||
c->movaps(SPU_OFF_128(gpr, op.rt), va);
|
||||
const XmmLink& v_fraction = XmmAlloc();
|
||||
const XmmLink& v_exponent = XmmAlloc();
|
||||
c->movdqa(v_fraction, va);
|
||||
c->movdqa(v_exponent, va);
|
||||
|
||||
c->psrld(v_fraction, 18);
|
||||
c->psrld(v_exponent, 23);
|
||||
|
||||
c->andps(v_fraction, XmmConst(v128::from32p(0x3F)));
|
||||
c->andps(v_exponent, XmmConst(v128::from32p(0xFF)));
|
||||
|
||||
const u64 fraction_lut_addr = reinterpret_cast<u64>(spu_frsqest_fraction_lut);
|
||||
const u64 exponent_lut_addr = reinterpret_cast<u64>(spu_frsqest_exponent_lut);
|
||||
|
||||
for (u32 index = 0; index < 4; index++)
|
||||
{
|
||||
c->pextrd(*qw0, v_fraction, index);
|
||||
c->mov(*qw1, asmjit::x86::dword_ptr(fraction_lut_addr, *qw0, 2));
|
||||
c->pinsrd(v_fraction, *qw1, index);
|
||||
|
||||
c->pextrd(*qw0, v_exponent, index);
|
||||
c->mov(*qw1, asmjit::x86::dword_ptr(exponent_lut_addr, *qw0, 2));
|
||||
c->pinsrd(v_exponent, *qw1, index);
|
||||
}
|
||||
|
||||
c->orps(v_fraction, v_exponent);
|
||||
|
||||
c->movaps(SPU_OFF_128(gpr, op.rt), v_fraction);
|
||||
}
|
||||
|
||||
void spu_recompiler::LQX(spu_opcode_t op)
|
||||
@@ -3922,7 +3983,49 @@ void spu_recompiler::CEQB(spu_opcode_t op)
|
||||
void spu_recompiler::FI(spu_opcode_t op)
|
||||
{
|
||||
// Floating Interpolate
|
||||
const XmmLink& va = XmmGet(op.ra, XmmType::Float);
|
||||
const XmmLink& vb = XmmGet(op.rb, XmmType::Float);
|
||||
const XmmLink& vb_base = XmmAlloc();
|
||||
const XmmLink& ymul = XmmAlloc();
|
||||
const XmmLink& temp_reg = XmmAlloc();
|
||||
const XmmLink& temp_reg2 = XmmAlloc();
|
||||
|
||||
c->movdqa(vb_base, vb);
|
||||
c->movdqa(ymul, vb);
|
||||
c->movdqa(temp_reg, va);
|
||||
|
||||
c->pand(vb_base, XmmConst(v128::from32p(0x007ffc00u)));
|
||||
c->pslld(vb_base, 9);
|
||||
|
||||
c->pand(temp_reg, XmmConst(v128::from32p(0x7ffff))); // va_fraction
|
||||
c->pand(ymul, XmmConst(v128::from32p(0x3ff)));
|
||||
c->pmulld(ymul, temp_reg);
|
||||
|
||||
c->movdqa(temp_reg, vb_base);
|
||||
c->psubd(temp_reg, ymul);
|
||||
|
||||
// Makes signed comparison unsigned and determines if we need to adjust exponent
|
||||
auto xor_const = XmmConst(v128::from32p(0x80000000));
|
||||
c->pxor(ymul, xor_const);
|
||||
c->pxor(vb_base, xor_const);
|
||||
c->pcmpgtd(ymul, vb_base);
|
||||
c->movdqa(vb_base, ymul);
|
||||
|
||||
c->movdqa(temp_reg2, temp_reg);
|
||||
c->pand(temp_reg2, vb_base);
|
||||
c->psrld(temp_reg2, 8); // only shift right by 8 if exponent is adjusted
|
||||
c->xorps(vb_base, XmmConst(v128::from32p(0xFFFFFFFF))); // Invert the mask
|
||||
c->pand(temp_reg, vb_base);
|
||||
c->psrld(temp_reg, 9); // shift right by 9 if not adjusted
|
||||
c->por(temp_reg, temp_reg2);
|
||||
|
||||
c->pand(vb, XmmConst(v128::from32p(0xff800000u)));
|
||||
c->pand(temp_reg, XmmConst(v128::from32p(~0xff800000u)));
|
||||
c->por(vb, temp_reg);
|
||||
|
||||
c->pand(ymul, XmmConst(v128::from32p(1 << 23)));
|
||||
c->psubd(vb, ymul);
|
||||
|
||||
c->movaps(SPU_OFF_128(gpr, op.rt), vb);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -689,15 +689,48 @@ bool FSMB(spu_thread& spu, spu_opcode_t op)
|
||||
template <spu_exec_bit... Flags>
|
||||
bool FREST(spu_thread& spu, spu_opcode_t op)
|
||||
{
|
||||
spu.gpr[op.rt] = _mm_rcp_ps(spu.gpr[op.ra]);
|
||||
v128 fraction_index = v128(_mm_srli_epi32(spu.gpr[op.ra], 18)) & v128(_mm_set1_epi32(0x1F));
|
||||
v128 exponent_index = v128(_mm_srli_epi32(spu.gpr[op.ra], 23)) & v128(_mm_set1_epi32(0xFF));
|
||||
v128 sign = spu.gpr[op.ra] & _mm_set1_epi32(0x80000000);
|
||||
|
||||
// AVX2
|
||||
// v128 fraction = _mm_i32gather_epi32(spu_frest_fraction_lut, fraction_index, 4);
|
||||
// v128 exponent = _mm_i32gather_epi32(spu_frest_exponent_lut, exponent_index, 4);
|
||||
|
||||
v128 result;
|
||||
|
||||
for (u32 index = 0; index < 4; index++)
|
||||
{
|
||||
u32 r = spu_frest_fraction_lut[fraction_index._u32[index]];
|
||||
r |= spu_frest_exponent_lut[exponent_index._u32[index]];
|
||||
r |= sign._u32[index];
|
||||
result._u32[index] = r;
|
||||
}
|
||||
|
||||
spu.gpr[op.rt] = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <spu_exec_bit... Flags>
|
||||
bool FRSQEST(spu_thread& spu, spu_opcode_t op)
|
||||
{
|
||||
const auto mask = _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff));
|
||||
spu.gpr[op.rt] = _mm_rsqrt_ps(_mm_and_ps(spu.gpr[op.ra], mask));
|
||||
v128 fraction_index = v128(_mm_srli_epi32(spu.gpr[op.ra], 18)) & v128(_mm_set1_epi32(0x3F));
|
||||
v128 exponent_index = v128(_mm_srli_epi32(spu.gpr[op.ra], 23)) & v128(_mm_set1_epi32(0xFF));
|
||||
|
||||
// AVX2
|
||||
// v128 fraction = _mm_i32gather_epi32(spu_frsqest_fraction_lut, fraction_index, 4);
|
||||
// v128 exponent = _mm_i32gather_epi32(spu_frsqest_exponent_lut, exponent_index, 4);
|
||||
|
||||
v128 result;
|
||||
|
||||
for (u32 index = 0; index < 4; index++)
|
||||
{
|
||||
u32 r = spu_frsqest_fraction_lut[fraction_index._u32[index]];
|
||||
r |= spu_frsqest_exponent_lut[exponent_index._u32[index]];
|
||||
result._u32[index] = r;
|
||||
}
|
||||
|
||||
spu.gpr[op.rt] = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,6 +46,64 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// LUTs for SPU instructions
|
||||
|
||||
const u32 spu_frest_fraction_lut[32] =
|
||||
{
|
||||
0x7FFBE0, 0x7F87A6, 0x70EF72, 0x708B40, 0x638B12, 0x633AEA, 0x5792C4, 0x574AA0,
|
||||
0x4CCA7E, 0x4C9262, 0x430A44, 0x42D62A, 0x3A2E12, 0x39FDFA, 0x3215E4, 0x31F1D2,
|
||||
0x2AA9BE, 0x2A85AC, 0x23D59A, 0x23BD8E, 0x1D8576, 0x1D8576, 0x17AD5A, 0x17AD5A,
|
||||
0x124543, 0x124543, 0x0D392D, 0x0D392D, 0x08851A, 0x08851A, 0x041D07, 0x041D07
|
||||
};
|
||||
|
||||
const u32 spu_frest_exponent_lut[256] =
|
||||
{
|
||||
0x7F800000, 0x7E000000, 0x7D800000, 0x7D000000, 0x7C800000, 0x7C000000, 0x7B800000, 0x7B000000, 0x7A800000, 0x7A000000, 0x79800000, 0x79000000, 0x78800000, 0x78000000, 0x77800000, 0x77000000,
|
||||
0x76800000, 0x76000000, 0x75800000, 0x75000000, 0x74800000, 0x74000000, 0x73800000, 0x73000000, 0x72800000, 0x72000000, 0x71800000, 0x71000000, 0x70800000, 0x70000000, 0x6F800000, 0x6F000000,
|
||||
0x6E800000, 0x6E000000, 0x6D800000, 0x6D000000, 0x6C800000, 0x6C000000, 0x6B800000, 0x6B000000, 0x6A800000, 0x6A000000, 0x69800000, 0x69000000, 0x68800000, 0x68000000, 0x67800000, 0x67000000,
|
||||
0x66800000, 0x66000000, 0x65800000, 0x65000000, 0x64800000, 0x64000000, 0x63800000, 0x63000000, 0x62800000, 0x62000000, 0x61800000, 0x61000000, 0x60800000, 0x60000000, 0x5F800000, 0x5F000000,
|
||||
0x5E800000, 0x5E000000, 0x5D800000, 0x5D000000, 0x5C800000, 0x5C000000, 0x5B800000, 0x5B000000, 0x5A800000, 0x5A000000, 0x59800000, 0x59000000, 0x58800000, 0x58000000, 0x57800000, 0x57000000,
|
||||
0x56800000, 0x56000000, 0x55800000, 0x55000000, 0x54800000, 0x54000000, 0x53800000, 0x53000000, 0x52800000, 0x52000000, 0x51800000, 0x51000000, 0x50800000, 0x50000000, 0x4F800000, 0x4F000000,
|
||||
0x4E800000, 0x4E000000, 0x4D800000, 0x4D000000, 0x4C800000, 0x4C000000, 0x4B800000, 0x4B000000, 0x4A800000, 0x4A000000, 0x49800000, 0x49000000, 0x48800000, 0x48000000, 0x47800000, 0x47000000,
|
||||
0x46800000, 0x46000000, 0x45800000, 0x45000000, 0x44800000, 0x44000000, 0x43800000, 0x43000000, 0x42800000, 0x42000000, 0x41800000, 0x41000000, 0x40800000, 0x40000000, 0x3F800000, 0x3F000000,
|
||||
0x3E800000, 0x3E000000, 0x3D800000, 0x3D000000, 0x3C800000, 0x3C000000, 0x3B800000, 0x3B000000, 0x3A800000, 0x3A000000, 0x39800000, 0x39000000, 0x38800000, 0x38000000, 0x37800000, 0x37000000,
|
||||
0x36800000, 0x36000000, 0x35800000, 0x35000000, 0x34800000, 0x34000000, 0x33800000, 0x33000000, 0x32800000, 0x32000000, 0x31800000, 0x31000000, 0x30800000, 0x30000000, 0x2F800000, 0x2F000000,
|
||||
0x2E800000, 0x2E000000, 0x2D800000, 0x2D000000, 0x2C800000, 0x2C000000, 0x2B800000, 0x2B000000, 0x2A800000, 0x2A000000, 0x29800000, 0x29000000, 0x28800000, 0x28000000, 0x27800000, 0x27000000,
|
||||
0x26800000, 0x26000000, 0x25800000, 0x25000000, 0x24800000, 0x24000000, 0x23800000, 0x23000000, 0x22800000, 0x22000000, 0x21800000, 0x21000000, 0x20800000, 0x20000000, 0x1F800000, 0x1F000000,
|
||||
0x1E800000, 0x1E000000, 0x1D800000, 0x1D000000, 0x1C800000, 0x1C000000, 0x1B800000, 0x1B000000, 0x1A800000, 0x1A000000, 0x19800000, 0x19000000, 0x18800000, 0x18000000, 0x17800000, 0x17000000,
|
||||
0x16800000, 0x16000000, 0x15800000, 0x15000000, 0x14800000, 0x14000000, 0x13800000, 0x13000000, 0x12800000, 0x12000000, 0x11800000, 0x11000000, 0x10800000, 0x10000000, 0x0F800000, 0x0F000000,
|
||||
0x0E800000, 0x0E000000, 0x0D800000, 0x0D000000, 0x0C800000, 0x0C000000, 0x0B800000, 0x0B000000, 0x0A800000, 0x0A000000, 0x09800000, 0x09000000, 0x08800000, 0x08000000, 0x07800000, 0x07000000,
|
||||
0x06800000, 0x06000000, 0x05800000, 0x05000000, 0x04800000, 0x04000000, 0x03800000, 0x03000000, 0x02800000, 0x02000000, 0x01800000, 0x01000000, 0x00800000, 0x00000000, 0x00000000, 0x00000000
|
||||
};
|
||||
|
||||
const u32 spu_frsqest_fraction_lut[64] =
|
||||
{
|
||||
0x350160, 0x34E954, 0x2F993D, 0x2F993D, 0x2AA523, 0x2AA523, 0x26190D, 0x26190D, 0x21E4F9, 0x21E4F9, 0x1E00E9, 0x1E00E9, 0x1A5CD9, 0x1A5CD9, 0x16F8CB, 0x16F8CB,
|
||||
0x13CCC0, 0x13CCC0, 0x10CCB3, 0x10CCB3, 0x0E00AA, 0x0E00AA, 0x0B58A1, 0x0B58A1, 0x08D498, 0x08D498, 0x067491, 0x067491, 0x043089, 0x043089, 0x020C83, 0x020C83,
|
||||
0x7FFDF4, 0x7FD1DE, 0x7859C8, 0x783DBA, 0x71559C, 0x71559C, 0x6AE57C, 0x6AE57C, 0x64F561, 0x64F561, 0x5F7149, 0x5F7149, 0x5A4D33, 0x5A4D33, 0x55811F, 0x55811F,
|
||||
0x51050F, 0x51050F, 0x4CC8FE, 0x4CC8FE, 0x48D0F0, 0x48D0F0, 0x4510E4, 0x4510E4, 0x4180D7, 0x4180D7, 0x3E24CC, 0x3E24CC, 0x3AF4C3, 0x3AF4C3, 0x37E8BA, 0x37E8BA
|
||||
};
|
||||
|
||||
const u32 spu_frsqest_exponent_lut[256] =
|
||||
{
|
||||
0x7F800000, 0x5E800000, 0x5E800000, 0x5E000000, 0x5E000000, 0x5D800000, 0x5D800000, 0x5D000000, 0x5D000000, 0x5C800000, 0x5C800000, 0x5C000000, 0x5C000000, 0x5B800000, 0x5B800000, 0x5B000000,
|
||||
0x5B000000, 0x5A800000, 0x5A800000, 0x5A000000, 0x5A000000, 0x59800000, 0x59800000, 0x59000000, 0x59000000, 0x58800000, 0x58800000, 0x58000000, 0x58000000, 0x57800000, 0x57800000, 0x57000000,
|
||||
0x57000000, 0x56800000, 0x56800000, 0x56000000, 0x56000000, 0x55800000, 0x55800000, 0x55000000, 0x55000000, 0x54800000, 0x54800000, 0x54000000, 0x54000000, 0x53800000, 0x53800000, 0x53000000,
|
||||
0x53000000, 0x52800000, 0x52800000, 0x52000000, 0x52000000, 0x51800000, 0x51800000, 0x51000000, 0x51000000, 0x50800000, 0x50800000, 0x50000000, 0x50000000, 0x4F800000, 0x4F800000, 0x4F000000,
|
||||
0x4F000000, 0x4E800000, 0x4E800000, 0x4E000000, 0x4E000000, 0x4D800000, 0x4D800000, 0x4D000000, 0x4D000000, 0x4C800000, 0x4C800000, 0x4C000000, 0x4C000000, 0x4B800000, 0x4B800000, 0x4B000000,
|
||||
0x4B000000, 0x4A800000, 0x4A800000, 0x4A000000, 0x4A000000, 0x49800000, 0x49800000, 0x49000000, 0x49000000, 0x48800000, 0x48800000, 0x48000000, 0x48000000, 0x47800000, 0x47800000, 0x47000000,
|
||||
0x47000000, 0x46800000, 0x46800000, 0x46000000, 0x46000000, 0x45800000, 0x45800000, 0x45000000, 0x45000000, 0x44800000, 0x44800000, 0x44000000, 0x44000000, 0x43800000, 0x43800000, 0x43000000,
|
||||
0x43000000, 0x42800000, 0x42800000, 0x42000000, 0x42000000, 0x41800000, 0x41800000, 0x41000000, 0x41000000, 0x40800000, 0x40800000, 0x40000000, 0x40000000, 0x3F800000, 0x3F800000, 0x3F000000,
|
||||
0x3F000000, 0x3E800000, 0x3E800000, 0x3E000000, 0x3E000000, 0x3D800000, 0x3D800000, 0x3D000000, 0x3D000000, 0x3C800000, 0x3C800000, 0x3C000000, 0x3C000000, 0x3B800000, 0x3B800000, 0x3B000000,
|
||||
0x3B000000, 0x3A800000, 0x3A800000, 0x3A000000, 0x3A000000, 0x39800000, 0x39800000, 0x39000000, 0x39000000, 0x38800000, 0x38800000, 0x38000000, 0x38000000, 0x37800000, 0x37800000, 0x37000000,
|
||||
0x37000000, 0x36800000, 0x36800000, 0x36000000, 0x36000000, 0x35800000, 0x35800000, 0x35000000, 0x35000000, 0x34800000, 0x34800000, 0x34000000, 0x34000000, 0x33800000, 0x33800000, 0x33000000,
|
||||
0x33000000, 0x32800000, 0x32800000, 0x32000000, 0x32000000, 0x31800000, 0x31800000, 0x31000000, 0x31000000, 0x30800000, 0x30800000, 0x30000000, 0x30000000, 0x2F800000, 0x2F800000, 0x2F000000,
|
||||
0x2F000000, 0x2E800000, 0x2E800000, 0x2E000000, 0x2E000000, 0x2D800000, 0x2D800000, 0x2D000000, 0x2D000000, 0x2C800000, 0x2C800000, 0x2C000000, 0x2C000000, 0x2B800000, 0x2B800000, 0x2B000000,
|
||||
0x2B000000, 0x2A800000, 0x2A800000, 0x2A000000, 0x2A000000, 0x29800000, 0x29800000, 0x29000000, 0x29000000, 0x28800000, 0x28800000, 0x28000000, 0x28000000, 0x27800000, 0x27800000, 0x27000000,
|
||||
0x27000000, 0x26800000, 0x26800000, 0x26000000, 0x26000000, 0x25800000, 0x25800000, 0x25000000, 0x25000000, 0x24800000, 0x24800000, 0x24000000, 0x24000000, 0x23800000, 0x23800000, 0x23000000,
|
||||
0x23000000, 0x22800000, 0x22800000, 0x22000000, 0x22000000, 0x21800000, 0x21800000, 0x21000000, 0x21000000, 0x20800000, 0x20800000, 0x20000000, 0x20000000, 0x1F800000, 0x1F800000, 0x1F000000
|
||||
};
|
||||
|
||||
using spu_rdata_t = decltype(spu_thread::rdata);
|
||||
|
||||
template <>
|
||||
@@ -3721,7 +3779,7 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args)
|
||||
|
||||
if (!vm::check_addr(addr, vm::page_writable))
|
||||
{
|
||||
vm::_ref<atomic_t<u8>>(addr) += 0; // Access violate
|
||||
utils::trigger_write_page_fault(vm::base(addr));
|
||||
}
|
||||
|
||||
raddr = 0;
|
||||
@@ -3823,7 +3881,7 @@ void do_cell_atomic_128_store(u32 addr, const void* to_write)
|
||||
else if (!g_use_rtm)
|
||||
{
|
||||
// Provoke page fault
|
||||
vm::_ref<atomic_t<u32>>(addr) += 0;
|
||||
utils::trigger_write_page_fault(vm::base(addr));
|
||||
|
||||
// Hard lock
|
||||
auto spu = cpu ? cpu->try_get<spu_thread>() : nullptr;
|
||||
|
||||
@@ -24,6 +24,12 @@ namespace utils
|
||||
class shm;
|
||||
}
|
||||
|
||||
// LUTs for SPU
|
||||
extern const u32 spu_frest_fraction_lut[32];
|
||||
extern const u32 spu_frest_exponent_lut[256];
|
||||
extern const u32 spu_frsqest_fraction_lut[64];
|
||||
extern const u32 spu_frsqest_exponent_lut[256];
|
||||
|
||||
// JIT Block
|
||||
using spu_function_t = void(*)(spu_thread&, void*, u8*);
|
||||
|
||||
|
||||
@@ -1662,12 +1662,16 @@ bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio)
|
||||
// Yield changed the queue before
|
||||
bool changed_queue = prio == yield_cmd;
|
||||
|
||||
s32 lowest_new_priority = smax;
|
||||
const bool has_free_hw_thread_space = count_non_sleeping_threads().onproc_count < g_cfg.core.ppu_threads + 0u;
|
||||
|
||||
if (cpu && prio != yield_cmd)
|
||||
{
|
||||
// Emplace current thread
|
||||
if (emplace_thread(cpu))
|
||||
{
|
||||
changed_queue = true;
|
||||
lowest_new_priority = std::min<s32>(static_cast<ppu_thread*>(cpu)->prio.load().prio, lowest_new_priority);
|
||||
}
|
||||
}
|
||||
else for (const auto _cpu : g_to_awake)
|
||||
@@ -1676,13 +1680,15 @@ bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio)
|
||||
if (emplace_thread(_cpu))
|
||||
{
|
||||
changed_queue = true;
|
||||
lowest_new_priority = std::min<s32>(static_cast<ppu_thread*>(_cpu)->prio.load().prio, lowest_new_priority);
|
||||
}
|
||||
}
|
||||
|
||||
auto target = +g_ppu;
|
||||
usz i = 0;
|
||||
|
||||
// Suspend threads if necessary
|
||||
for (usz i = 0, thread_count = g_cfg.core.ppu_threads; target; target = target->next_ppu, i++)
|
||||
for (usz thread_count = g_cfg.core.ppu_threads; target; target = target->next_ppu, i++)
|
||||
{
|
||||
if (i >= thread_count && cpu_flag::suspend - target->state)
|
||||
{
|
||||
@@ -1709,6 +1715,31 @@ bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio)
|
||||
}
|
||||
}
|
||||
|
||||
// In real PS3 (it seems), when a thread with a higher priority than the caller is signaled and -
|
||||
// - that there is available space on the running queue for the other hardware thread to start
|
||||
// It prioritizes signaled thread - caller's hardware thread switches instantly to the new thread code
|
||||
// While signaling to the other hardware thread to execute the caller's code.
|
||||
// Resulting in a delay to the caller after such thread is signaled
|
||||
|
||||
if (current_ppu && changed_queue && has_free_hw_thread_space)
|
||||
{
|
||||
if (current_ppu->prio.load().prio > lowest_new_priority)
|
||||
{
|
||||
// When not being set to All timers - activate only for sys_ppu_thread_start
|
||||
if (current_ppu->gpr[11] == 0x35 || g_cfg.core.sleep_timers_accuracy == sleep_timers_accuracy_level::_all_timers)
|
||||
{
|
||||
if (!current_ppu->state.test_and_set(cpu_flag::yield) || current_ppu->hw_sleep_time != 0)
|
||||
{
|
||||
current_ppu->hw_sleep_time += 35; // Seems like 35us after extensive testing
|
||||
}
|
||||
else
|
||||
{
|
||||
current_ppu->hw_sleep_time = 30000; // In addition to another flag's use (TODO: Refactor and clean this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed_queue;
|
||||
}
|
||||
|
||||
@@ -1736,9 +1767,13 @@ void lv2_obj::schedule_all(u64 current_time)
|
||||
if (target->state & cpu_flag::suspend)
|
||||
{
|
||||
ppu_log.trace("schedule(): %s", target->id);
|
||||
|
||||
// Remove yield if it was sleeping until now
|
||||
const bs_t<cpu_flag> remove_yield = target->start_time == 0 ? +cpu_flag::suspend : (cpu_flag::yield + cpu_flag::preempt);
|
||||
|
||||
target->start_time = 0;
|
||||
|
||||
if ((target->state.fetch_op(FN(x += cpu_flag::signal, x -= cpu_flag::suspend, void())) & (cpu_flag::wait + cpu_flag::signal)) != cpu_flag::wait)
|
||||
if ((target->state.fetch_op(FN(x += cpu_flag::signal, x -= cpu_flag::suspend, x-= remove_yield, void())) & (cpu_flag::wait + cpu_flag::signal)) != cpu_flag::wait)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1920,19 +1955,24 @@ bool lv2_obj::is_scheduler_ready()
|
||||
return g_to_sleep.empty();
|
||||
}
|
||||
|
||||
bool lv2_obj::has_ppus_in_running_state()
|
||||
ppu_non_sleeping_count_t lv2_obj::count_non_sleeping_threads()
|
||||
{
|
||||
ppu_non_sleeping_count_t total{};
|
||||
|
||||
auto target = atomic_storage<ppu_thread*>::load(g_ppu);
|
||||
|
||||
for (usz i = 0, thread_count = g_cfg.core.ppu_threads; target; target = atomic_storage<ppu_thread*>::load(target->next_ppu), i++)
|
||||
for (usz thread_count = g_cfg.core.ppu_threads; target; target = atomic_storage<ppu_thread*>::load(target->next_ppu))
|
||||
{
|
||||
if (i >= thread_count)
|
||||
if (total.onproc_count == thread_count)
|
||||
{
|
||||
return true;
|
||||
total.has_running = true;
|
||||
break;
|
||||
}
|
||||
|
||||
total.onproc_count++;
|
||||
}
|
||||
|
||||
return false;
|
||||
return total;
|
||||
}
|
||||
|
||||
void lv2_obj::set_yield_frequency(u64 freq, u64 max_allowed_tsc)
|
||||
|
||||
+137
-58
@@ -90,6 +90,23 @@ void fmt_class_string<lv2_dir>::format(std::string& out, u64 arg)
|
||||
fmt::append(out, u8"Directory, “%s”, Entries: %u/%u", dir.name.data(), std::min<u64>(dir.pos, dir.entries.size()), dir.entries.size());
|
||||
}
|
||||
|
||||
bool has_fs_write_rights(std::string_view vpath)
|
||||
{
|
||||
// VSH has access to everything
|
||||
if (g_ps3_process_info.has_root_perm())
|
||||
return true;
|
||||
|
||||
const auto norm_vpath = lv2_fs_object::get_normalized_path(vpath);
|
||||
const auto parent_dir = fs::get_parent_dir_view(norm_vpath);
|
||||
|
||||
// This is not exhaustive, PS3 has a unix filesystem with rights for each directory and files
|
||||
// This is mostly meant to protect against games doing insane things(ie NPUB30003 => NPUB30008)
|
||||
if (parent_dir == "/dev_hdd0" || parent_dir == "/dev_hdd0/game")
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool verify_mself(const fs::file& mself_file)
|
||||
{
|
||||
FsMselfHeader mself_header;
|
||||
@@ -160,7 +177,7 @@ bool lv2_fs_mount_info_map::remove(std::string_view path)
|
||||
return false;
|
||||
}
|
||||
|
||||
const lv2_fs_mount_info& lv2_fs_mount_info_map::lookup(std::string_view path, bool no_cell_fs_path) const
|
||||
const lv2_fs_mount_info& lv2_fs_mount_info_map::lookup(std::string_view path, bool no_cell_fs_path, std::string* mount_path) const
|
||||
{
|
||||
if (path.starts_with("/"sv))
|
||||
{
|
||||
@@ -177,7 +194,9 @@ const lv2_fs_mount_info& lv2_fs_mount_info_map::lookup(std::string_view path, bo
|
||||
if (iterator->second == &g_mp_sys_dev_root && parent_level > 1)
|
||||
break;
|
||||
if (no_cell_fs_path && iterator->second.device.starts_with(cell_fs_path))
|
||||
return lookup(iterator->second.device.substr(cell_fs_path.size()), no_cell_fs_path); // Recursively look up the parent mount info
|
||||
return lookup(iterator->second.device.substr(cell_fs_path.size()), no_cell_fs_path, mount_path); // Recursively look up the parent mount info
|
||||
if (mount_path)
|
||||
*mount_path = iterator->first;
|
||||
return iterator->second;
|
||||
}
|
||||
} while (parent_dir.length() > 1); // Exit the loop when parent_dir == "/" or empty
|
||||
@@ -191,38 +210,30 @@ u64 lv2_fs_mount_info_map::get_all(CellFsMountInfo* info, u64 len) const
|
||||
if (!info)
|
||||
return map.size();
|
||||
|
||||
struct mount_info
|
||||
{
|
||||
const std::string_view path, filesystem, dev_name;
|
||||
const be_t<u32> unk1 = 0, unk2 = 0, unk3 = 0, unk4 = 0, unk5 = 0;
|
||||
};
|
||||
|
||||
u64 count = 0;
|
||||
|
||||
auto push_info = [&](mount_info&& data)
|
||||
for (const auto& [path, mount_info] : map)
|
||||
{
|
||||
if (count >= len)
|
||||
return;
|
||||
break;
|
||||
|
||||
strcpy_trunc(info->mount_path, data.path);
|
||||
strcpy_trunc(info->filesystem, data.filesystem);
|
||||
strcpy_trunc(info->dev_name, data.dev_name);
|
||||
std::memcpy(&info->unk1, &data.unk1, sizeof(be_t<u32>) * 5);
|
||||
strcpy_trunc(info[count].mount_path, path);
|
||||
strcpy_trunc(info[count].filesystem, mount_info.file_system);
|
||||
strcpy_trunc(info[count].dev_name, mount_info.device);
|
||||
if (mount_info.read_only)
|
||||
info[count].unk[4] |= 0x10000000;
|
||||
|
||||
info++, count++;
|
||||
};
|
||||
|
||||
for (const auto& [path, mi] : map)
|
||||
{
|
||||
if (mi == &g_mp_sys_dev_root || mi == &g_mp_sys_dev_flash)
|
||||
push_info(mount_info{.path = path, .filesystem = mi.file_system, .dev_name = mi.device, .unk5 = 0x10000000});
|
||||
else
|
||||
push_info(mount_info{.path = path, .filesystem = mi.file_system, .dev_name = mi.device});
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool lv2_fs_mount_info_map::is_device_mounted(std::string_view device_name) const
|
||||
{
|
||||
return std::any_of(map.begin(), map.end(), [&](const decltype(map)::value_type& info) { return info.second.device == device_name; });
|
||||
}
|
||||
|
||||
bool lv2_fs_mount_info_map::vfs_unmount(std::string_view vpath, bool remove_from_map)
|
||||
{
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
@@ -264,13 +275,15 @@ std::string lv2_fs_object::get_normalized_path(std::string_view path)
|
||||
return normalized_path.empty() ? "/" : normalized_path;
|
||||
}
|
||||
|
||||
std::string_view lv2_fs_object::get_device_root(std::string_view path)
|
||||
std::string lv2_fs_object::get_device_root(std::string_view filename)
|
||||
{
|
||||
std::string path = get_normalized_path(filename); // Prevent getting fooled by ".." trick such as "/dev_usb000/../dev_flash"
|
||||
|
||||
if (const auto first = path.find_first_not_of("/"sv); first != umax)
|
||||
{
|
||||
if (const auto pos = path.substr(first).find_first_of("/"sv); pos != umax)
|
||||
path = path.substr(0, first + pos);
|
||||
path.remove_prefix(std::max<usz>(0, first - 1)); // Remove duplicate leading '/' while keeping only one
|
||||
path = path.substr(std::max<std::make_signed_t<usz>>(0, first - 1)); // Remove duplicate leading '/' while keeping only one
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -289,14 +302,12 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
|
||||
filename.remove_prefix(cell_fs_path.size());
|
||||
|
||||
const bool is_path = filename.starts_with("/"sv);
|
||||
std::string mp_name = std::string(is_path ? get_device_root(filename) : filename);
|
||||
std::string mp_name = is_path ? get_device_root(filename) : std::string(filename);
|
||||
|
||||
const auto check_mp = [&]()
|
||||
{
|
||||
for (auto mp = &g_mp_sys_dev_root; mp; mp = mp->next)
|
||||
{
|
||||
const auto pos = mp->root.find_first_not_of('/');
|
||||
const auto mp_root = pos != umax ? mp->root.substr(pos) : mp->root;
|
||||
const auto& device_alias_check = !is_path && (
|
||||
(mp == &g_mp_sys_dev_hdd0 && mp_name == "CELL_FS_IOS:PATA0_HDD_DRIVE"sv) ||
|
||||
(mp == &g_mp_sys_dev_hdd1 && mp_name == "CELL_FS_IOS:PATA1_HDD_DRIVE"sv) ||
|
||||
@@ -304,20 +315,17 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
|
||||
|
||||
if (mp == &g_mp_sys_dev_usb)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (mp_name.starts_with(is_path ? mp->root : mp->device))
|
||||
{
|
||||
if (fmt::format("%s%03d", is_path ? mp_root : mp->device, i) == mp_name)
|
||||
{
|
||||
if (!is_path)
|
||||
mp_name = fmt::format("%s%03d", mp_root, i);
|
||||
return mp;
|
||||
}
|
||||
if (!is_path)
|
||||
mp_name = fmt::format("%s%s", mp->root, mp_name.substr(mp->device.size()));
|
||||
return mp;
|
||||
}
|
||||
}
|
||||
else if ((is_path ? mp_root : mp->device) == mp_name || device_alias_check)
|
||||
else if ((is_path ? mp->root : mp->device) == mp_name || device_alias_check)
|
||||
{
|
||||
if (!is_path)
|
||||
mp_name = mp_root;
|
||||
mp_name = mp->root;
|
||||
return mp;
|
||||
}
|
||||
}
|
||||
@@ -335,7 +343,7 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
|
||||
else if (result == &g_mp_sys_dev_hdd1)
|
||||
*vfs_path = g_cfg_vfs.get(g_cfg_vfs.dev_hdd1, rpcs3::utils::get_emu_dir());
|
||||
else if (result == &g_mp_sys_dev_usb)
|
||||
*vfs_path = g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, fmt::format("/%s", mp_name), rpcs3::utils::get_emu_dir()).path;
|
||||
*vfs_path = g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, mp_name, rpcs3::utils::get_emu_dir()).path;
|
||||
else if (result == &g_mp_sys_dev_bdvd)
|
||||
*vfs_path = g_cfg_vfs.get(g_cfg_vfs.dev_bdvd, rpcs3::utils::get_emu_dir());
|
||||
else if (result == &g_mp_sys_dev_dvd)
|
||||
@@ -354,7 +362,7 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
|
||||
*vfs_path = {};
|
||||
|
||||
if (is_path && !is_cell_fs_path && !vfs_path->empty())
|
||||
vfs_path->append(filename.substr(mp_name.size() + 1)); // substr: remove leading "/mp_name"
|
||||
vfs_path->append(filename.substr(mp_name.size()));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -374,6 +382,12 @@ lv2_fs_object::lv2_fs_object(utils::serial& ar, bool)
|
||||
|
||||
u64 lv2_file::op_read(const fs::file& file, vm::ptr<void> buf, u64 size, u64 opt_pos)
|
||||
{
|
||||
if (u64 region = buf.addr() >> 28, region_end = (buf.addr() & 0xfff'ffff) + (size & 0xfff'ffff); region == region_end && ((region >> 28) == 0 || region >= 0xC))
|
||||
{
|
||||
// Optimize reads from safe memory
|
||||
return (opt_pos == umax ? file.read(buf.get_ptr(), size) : file.read_at(opt_pos, buf.get_ptr(), size));
|
||||
}
|
||||
|
||||
// Copy data from intermediate buffer (avoid passing vm pointer to a native API)
|
||||
std::vector<uchar> local_buf(std::min<u64>(size, 65536));
|
||||
|
||||
@@ -425,6 +439,8 @@ lv2_file::lv2_file(utils::serial& ar)
|
||||
, flags(ar)
|
||||
, type(ar)
|
||||
{
|
||||
[[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(lv2_fs);
|
||||
|
||||
ar(lock);
|
||||
|
||||
be_t<u64> arg = 0;
|
||||
@@ -437,13 +453,19 @@ lv2_file::lv2_file(utils::serial& ar)
|
||||
case lv2_file_type::edata: arg = 0x2, size = 8; break;
|
||||
}
|
||||
|
||||
const std::string retrieve_real = ar;
|
||||
const std::string retrieve_real = ar.pop<std::string>();
|
||||
|
||||
if (type == lv2_file_type::edata && version >= 2)
|
||||
{
|
||||
ar(g_fxo->get<loaded_npdrm_keys>().one_time_key);
|
||||
}
|
||||
|
||||
open_result_t res = lv2_file::open(retrieve_real, flags & CELL_FS_O_ACCMODE, mode, size ? &arg : nullptr, size);
|
||||
file = std::move(res.file);
|
||||
real_path = std::move(res.real_path);
|
||||
|
||||
g_fxo->get<loaded_npdrm_keys>().npdrm_fds.raw() += type != lv2_file_type::regular;
|
||||
g_fxo->get<loaded_npdrm_keys>().one_time_key = {};
|
||||
|
||||
if (ar.pop<bool>()) // see lv2_file::save in_mem
|
||||
{
|
||||
@@ -475,6 +497,13 @@ void lv2_file::save(utils::serial& ar)
|
||||
USING_SERIALIZATION_VERSION(lv2_fs);
|
||||
ar(name, mode, flags, type, lock, ensure(vfs::retrieve(real_path), FN(!x.empty())));
|
||||
|
||||
if (type == lv2_file_type::edata)
|
||||
{
|
||||
auto file_ptr = file.release();
|
||||
ar(static_cast<EDATADecrypter*>(file_ptr.get())->get_key());
|
||||
file.reset(std::move(file_ptr));
|
||||
}
|
||||
|
||||
if (!mp.read_only && flags & CELL_FS_O_ACCMODE)
|
||||
{
|
||||
// Ensure accurate timestamps and content on disk
|
||||
@@ -603,7 +632,13 @@ struct lv2_file::file_view : fs::file_base
|
||||
|
||||
fs::stat_t get_stat() override
|
||||
{
|
||||
return m_file->file.get_stat();
|
||||
fs::stat_t stat = m_file->file.get_stat();
|
||||
|
||||
// TODO: Check this on realhw
|
||||
//stat.size = utils::sub_saturate<u64>(stat.size, m_off);
|
||||
|
||||
stat.is_writable = false;
|
||||
return stat;
|
||||
}
|
||||
|
||||
bool trunc(u64) override
|
||||
@@ -613,7 +648,7 @@ struct lv2_file::file_view : fs::file_base
|
||||
|
||||
u64 read(void* buffer, u64 size) override
|
||||
{
|
||||
const u64 result = m_file->file.read_at(m_off + m_pos, buffer, size);
|
||||
const u64 result = file_view::read_at(m_pos, buffer, size);
|
||||
|
||||
m_pos += result;
|
||||
return result;
|
||||
@@ -621,7 +656,7 @@ struct lv2_file::file_view : fs::file_base
|
||||
|
||||
u64 read_at(u64 offset, void* buffer, u64 size) override
|
||||
{
|
||||
return m_file->file.read_at(offset, buffer, size);
|
||||
return m_file->file.read_at(m_off + offset, buffer, size);
|
||||
}
|
||||
|
||||
u64 write(const void*, u64) override
|
||||
@@ -648,7 +683,7 @@ struct lv2_file::file_view : fs::file_base
|
||||
|
||||
u64 size() override
|
||||
{
|
||||
return m_file->file.size();
|
||||
return utils::sub_saturate<u64>(m_file->file.size(), m_off);
|
||||
}
|
||||
|
||||
fs::file_id get_id() override
|
||||
@@ -905,6 +940,21 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s3
|
||||
const auto& dec_keys = edatkeys.dec_keys;
|
||||
const u64 max_i = std::min<u64>(std::size(dec_keys), init_pos);
|
||||
|
||||
if (edatkeys.one_time_key)
|
||||
{
|
||||
auto edata_file = std::make_unique<EDATADecrypter>(std::move(file), edatkeys.one_time_key);
|
||||
edatkeys.one_time_key = {};
|
||||
|
||||
if (!edata_file->ReadHeader())
|
||||
{
|
||||
// Read failure
|
||||
return {CELL_EFSSPECIFIC};
|
||||
}
|
||||
|
||||
file.reset(std::move(edata_file));
|
||||
break;
|
||||
}
|
||||
|
||||
for (u64 i = 0;; i++)
|
||||
{
|
||||
if (i == max_i)
|
||||
@@ -918,7 +968,7 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s3
|
||||
if (!edata_file->ReadHeader())
|
||||
{
|
||||
// Prepare file for the next iteration
|
||||
file = std::move(edata_file->edata_file);
|
||||
file = std::move(edata_file->m_edata_file);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -959,6 +1009,11 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
if (flags & CELL_FS_O_CREAT && !has_fs_write_rights(vpath) && !fs::is_dir(local_path))
|
||||
{
|
||||
return {CELL_EACCES};
|
||||
}
|
||||
|
||||
lv2_file_type type = lv2_file_type::regular;
|
||||
|
||||
if (size == 8)
|
||||
@@ -1481,7 +1536,9 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
// This syscall can be used by games and VSH to test the presence of dev_usb000 ~ dev_usb127
|
||||
// Thus there is no need to fuss about CELL_ENOTMOUNTED in this case
|
||||
return {sys_fs.warning, CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
std::unique_lock lock(mp->mutex);
|
||||
@@ -1639,6 +1696,11 @@ error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr<char> path, s32 mode)
|
||||
return {CELL_EROFS, path};
|
||||
}
|
||||
|
||||
if (!fs::exists(local_path) && !has_fs_write_rights(path.get_ptr()))
|
||||
{
|
||||
return {CELL_EACCES, path};
|
||||
}
|
||||
|
||||
std::lock_guard lock(mp->mutex);
|
||||
|
||||
if (!fs::create_dir(local_path))
|
||||
@@ -1762,6 +1824,11 @@ error_code sys_fs_rmdir(ppu_thread& ppu, vm::cptr<char> path)
|
||||
return {CELL_EROFS, path};
|
||||
}
|
||||
|
||||
if (fs::is_dir(local_path) && !has_fs_write_rights(path.get_ptr()))
|
||||
{
|
||||
return {CELL_EACCES};
|
||||
}
|
||||
|
||||
std::lock_guard lock(mp->mutex);
|
||||
|
||||
if (!fs::remove_dir(local_path))
|
||||
@@ -1796,7 +1863,8 @@ error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr<char> path)
|
||||
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
|
||||
const auto& mp = g_fxo->get<lv2_fs_mount_info_map>().lookup(vpath);
|
||||
std::string mount_path = fs::get_parent_dir(vpath); // Use its parent directory as fallback
|
||||
const auto& mp = g_fxo->get<lv2_fs_mount_info_map>().lookup(vpath, true, &mount_path);
|
||||
|
||||
if (mp == &g_mp_sys_dev_root)
|
||||
{
|
||||
@@ -1820,8 +1888,7 @@ error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr<char> path)
|
||||
|
||||
std::lock_guard lock(mp->mutex);
|
||||
|
||||
// Provide default mp root or use parent directory if not available (such as host_root)
|
||||
if (!vfs::host::unlink(local_path, vfs::get(mp->root.empty() ? vpath.substr(0, vpath.find_last_of('/')) : mp->root)))
|
||||
if (!vfs::host::unlink(local_path, vfs::get(mount_path)))
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
{
|
||||
@@ -1993,21 +2060,23 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
||||
|
||||
if (!file)
|
||||
{
|
||||
return CELL_EBADF;
|
||||
return {CELL_EBADF, "fd=%u", fd};
|
||||
}
|
||||
|
||||
sys_fs.warning("sys_fs_fcntl(0x80000009): fd=%d, arg->offset=0x%x, size=0x%x (file: %s)", fd, arg->offset, _size, *file);
|
||||
|
||||
std::lock_guard lock(file->mp->mutex);
|
||||
|
||||
if (!file->file)
|
||||
{
|
||||
return CELL_EBADF;
|
||||
return {CELL_EBADF, "fd=%u", fd};
|
||||
}
|
||||
|
||||
auto sdata_file = std::make_unique<EDATADecrypter>(lv2_file::make_view(file, arg->offset));
|
||||
|
||||
if (!sdata_file->ReadHeader())
|
||||
{
|
||||
return CELL_EFSSPECIFIC;
|
||||
return {CELL_EFSSPECIFIC, "fd=%u", fd};
|
||||
}
|
||||
|
||||
fs::file stream;
|
||||
@@ -2044,6 +2113,16 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
case 0xc0000003: // cellFsUtilitySetFakeSize
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xc0000004: // cellFsUtilityGetFakeSize
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xc0000006: // Unknown
|
||||
{
|
||||
const auto arg = vm::static_ptr_cast<lv2_file_c0000006>(_arg);
|
||||
@@ -2543,7 +2622,7 @@ error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
{
|
||||
case fs::error::inval: return CELL_EINVAL;
|
||||
case fs::error::inval: return {CELL_EINVAL, "fd=%u, offset=0x%x, whence=%d", fd, offset, whence};
|
||||
default: sys_fs.error("sys_fs_lseek(): unknown error %s", error);
|
||||
}
|
||||
|
||||
@@ -3152,7 +3231,7 @@ error_code sys_fs_newfs(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
|
||||
if (mp == &g_mp_sys_no_device)
|
||||
return {CELL_ENXIO, device_name};
|
||||
|
||||
if (mp == &g_mp_sys_dev_root || !lock.try_lock())
|
||||
if (g_fxo->get<lv2_fs_mount_info_map>().is_device_mounted(device_name) || !lock.try_lock())
|
||||
return {CELL_EBUSY, device_name};
|
||||
|
||||
if (vfs_path.empty())
|
||||
@@ -3216,7 +3295,7 @@ error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
|
||||
if (mp == &g_mp_sys_no_device)
|
||||
return {CELL_ENXIO, device_name};
|
||||
|
||||
if (mp == &g_mp_sys_dev_root || !lock.try_lock())
|
||||
if (g_fxo->get<lv2_fs_mount_info_map>().is_device_mounted(device_name) || !lock.try_lock())
|
||||
return {CELL_EBUSY, device_name};
|
||||
|
||||
if (vfs_path.empty())
|
||||
@@ -3280,11 +3359,11 @@ error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 unk2)
|
||||
error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 force)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_fs.warning("sys_fs_unmount(path=%s, unk1=0x%x, unk2=0x%x)", path, unk1, unk2);
|
||||
sys_fs.warning("sys_fs_unmount(path=%s, unk1=0x%x, force=%d)", path, unk1, force);
|
||||
|
||||
const auto [path_error, vpath] = translate_to_str(path);
|
||||
|
||||
@@ -3302,7 +3381,7 @@ error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 un
|
||||
if (mp == &g_mp_sys_no_device)
|
||||
return {CELL_EINVAL, vpath};
|
||||
|
||||
if (mp == &g_mp_sys_dev_root || !lock.try_lock())
|
||||
if (mp == &g_mp_sys_dev_root || (!lock.try_lock() && !force))
|
||||
return {CELL_EBUSY, vpath};
|
||||
|
||||
if (!lv2_fs_mount_info_map::vfs_unmount(vpath))
|
||||
|
||||
@@ -213,8 +213,9 @@ public:
|
||||
return map.try_emplace(std::forward<Args>(args)...).second;
|
||||
}
|
||||
bool remove(std::string_view path);
|
||||
const lv2_fs_mount_info& lookup(std::string_view path, bool no_cell_fs_path = false) const;
|
||||
const lv2_fs_mount_info& lookup(std::string_view path, bool no_cell_fs_path = false, std::string* mount_path = nullptr) const;
|
||||
u64 get_all(CellFsMountInfo* info = nullptr, u64 len = 0) const;
|
||||
bool is_device_mounted(std::string_view device_name) const;
|
||||
|
||||
static bool vfs_unmount(std::string_view vpath, bool remove_from_map = true);
|
||||
|
||||
@@ -249,7 +250,7 @@ public:
|
||||
static std::string get_normalized_path(std::string_view path);
|
||||
|
||||
// Get the device's root path (e.g. "/dev_hdd0") from a given path
|
||||
static std::string_view get_device_root(std::string_view path);
|
||||
static std::string get_device_root(std::string_view filename);
|
||||
|
||||
// Filename can be either a path starting with '/' or a CELL_FS device name
|
||||
// This should be used only when handling devices that are not mounted
|
||||
@@ -620,11 +621,7 @@ struct CellFsMountInfo
|
||||
char mount_path[0x20]; // 0x0
|
||||
char filesystem[0x20]; // 0x20
|
||||
char dev_name[0x40]; // 0x40
|
||||
be_t<u32> unk1; // 0x80
|
||||
be_t<u32> unk2; // 0x84
|
||||
be_t<u32> unk3; // 0x88
|
||||
be_t<u32> unk4; // 0x8C
|
||||
be_t<u32> unk5; // 0x90
|
||||
be_t<u32> unk[5]; // 0x80, probably attributes
|
||||
};
|
||||
|
||||
CHECK_SIZE(CellFsMountInfo, 0x94);
|
||||
@@ -682,6 +679,6 @@ error_code sys_fs_mapped_free(ppu_thread& ppu, u32 fd, vm::ptr<void> ptr);
|
||||
error_code sys_fs_truncate2(ppu_thread& ppu, u32 fd, u64 size);
|
||||
error_code sys_fs_newfs(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char> file_system, s32 unk1, vm::cptr<char> str1);
|
||||
error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char> file_system, vm::cptr<char> path, s32 unk1, s32 prot, s32 unk3, vm::cptr<char> str1, u32 str_len);
|
||||
error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 unk2);
|
||||
error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 force);
|
||||
error_code sys_fs_get_mount_info_size(ppu_thread& ppu, vm::ptr<u64> len);
|
||||
error_code sys_fs_get_mount_info(ppu_thread& ppu, vm::ptr<CellFsMountInfo> info, u64 len, vm::ptr<u64> out_len);
|
||||
|
||||
@@ -126,7 +126,7 @@ error_code sys_memory_allocate(cpu_thread& cpu, u32 size, u64 flags, vm::ptr<u32
|
||||
// Try to get "physical memory"
|
||||
if (!dct.take(size))
|
||||
{
|
||||
return CELL_ENOMEM;
|
||||
return {CELL_ENOMEM, dct.size - dct.used};
|
||||
}
|
||||
|
||||
if (const auto area = reserve_map(size, align))
|
||||
@@ -200,7 +200,7 @@ error_code sys_memory_allocate_from_container(cpu_thread& cpu, u32 size, u32 cid
|
||||
|
||||
if (ct.ret)
|
||||
{
|
||||
return ct.ret;
|
||||
return {ct.ret, ct->size - ct->used};
|
||||
}
|
||||
|
||||
if (const auto area = reserve_map(size, align))
|
||||
|
||||
@@ -74,6 +74,7 @@ CellError lv2_memory::on_id_create()
|
||||
{
|
||||
if (!exists && !ct->take(size))
|
||||
{
|
||||
sys_mmapper.error("lv2_memory::on_id_create(): Cannot allocate 0x%x bytes (0x%x available)", size, ct->size - ct->used);
|
||||
return CELL_ENOMEM;
|
||||
}
|
||||
|
||||
|
||||
@@ -329,9 +329,14 @@ void lv2_socket::save(utils::serial& ar, bool save_only_this_class)
|
||||
}
|
||||
}
|
||||
|
||||
void sys_net_dump_data(std::string_view desc, const u8* data, s32 len)
|
||||
void sys_net_dump_data(std::string_view desc, const u8* data, s32 len, const void* addr)
|
||||
{
|
||||
sys_net_dump.trace("%s:%s", desc, fmt::buf_to_hexstring(data, len));
|
||||
const sys_net_sockaddr_in_p2p* p2p_addr = reinterpret_cast<const sys_net_sockaddr_in_p2p*>(addr);
|
||||
|
||||
if (p2p_addr)
|
||||
sys_net_dump.trace("%s(%s:%d:%d): %s", desc, np::ip_to_string(std::bit_cast<u32>(p2p_addr->sin_addr)), p2p_addr->sin_port, p2p_addr->sin_vport, fmt::buf_to_hexstring(data, len));
|
||||
else
|
||||
sys_net_dump.trace("%s: %s", desc, fmt::buf_to_hexstring(data, len));
|
||||
}
|
||||
|
||||
error_code sys_net_bnet_accept(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sockaddr> addr, vm::ptr<u32> paddrlen)
|
||||
@@ -770,7 +775,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_net.warning("sys_net_bnet_recvfrom(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, paddrlen=*0x%x)", s, buf, len, flags, addr, paddrlen);
|
||||
sys_net.trace("sys_net_bnet_recvfrom(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, paddrlen=*0x%x)", s, buf, len, flags, addr, paddrlen);
|
||||
|
||||
// If addr is null, paddrlen must be null as well
|
||||
if (!buf || !len || addr.operator bool() != paddrlen.operator bool())
|
||||
@@ -797,7 +802,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32
|
||||
{
|
||||
sn_addr = res_addr;
|
||||
std::memcpy(buf.get_ptr(), vec.data(), res);
|
||||
sys_net_dump_data("recvfrom", vec.data(), res);
|
||||
sys_net_dump_data("recvfrom", vec.data(), res, &res_addr);
|
||||
}
|
||||
|
||||
result = res;
|
||||
@@ -819,7 +824,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32
|
||||
{
|
||||
sn_addr = res_addr;
|
||||
std::memcpy(buf.get_ptr(), vec.data(), res);
|
||||
sys_net_dump_data("recvfrom", vec.data(), res);
|
||||
sys_net_dump_data("recvfrom", vec.data(), res, &res_addr);
|
||||
}
|
||||
result = res;
|
||||
lv2_obj::awake(&ppu);
|
||||
@@ -984,7 +989,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_net.warning("sys_net_bnet_sendto(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, addrlen=%u)", s, buf, len, flags, addr, addrlen);
|
||||
sys_net.trace("sys_net_bnet_sendto(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, addrlen=%u)", s, buf, len, flags, addr, addrlen);
|
||||
|
||||
if (flags & ~(SYS_NET_MSG_DONTWAIT | SYS_NET_MSG_WAITALL | SYS_NET_MSG_USECRYPTO | SYS_NET_MSG_USESIGNATURE))
|
||||
{
|
||||
@@ -1003,7 +1008,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
|
||||
return -SYS_NET_EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
sys_net_dump_data("sendto", static_cast<const u8 *>(buf.get_ptr()), len);
|
||||
sys_net_dump_data("sendto", static_cast<const u8*>(buf.get_ptr()), len, addr ? addr.get_ptr() : nullptr);
|
||||
|
||||
const std::optional<sys_net_sockaddr> sn_addr = addr ? std::optional<sys_net_sockaddr>(*addr) : std::nullopt;
|
||||
const std::vector<u8> buf_copy(vm::_ptr<const char>(buf.addr()), vm::_ptr<const char>(buf.addr()) + len);
|
||||
@@ -1256,7 +1261,7 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_net.warning("sys_net_bnet_poll(fds=*0x%x, nfds=%d, ms=%d)", fds, nfds, ms);
|
||||
sys_net.trace("sys_net_bnet_poll(fds=*0x%x, nfds=%d, ms=%d)", fds, nfds, ms);
|
||||
|
||||
if (nfds <= 0)
|
||||
{
|
||||
@@ -1424,13 +1429,13 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
|
||||
}
|
||||
}
|
||||
|
||||
std::memcpy(fds.get_ptr(), fds_buf.data(), nfds * sizeof(fds[0]));
|
||||
|
||||
if (!has_timedout && !signaled)
|
||||
{
|
||||
return -SYS_NET_EINTR;
|
||||
}
|
||||
|
||||
std::memcpy(fds.get_ptr(), fds_buf.data(), nfds * sizeof(fds[0]));
|
||||
|
||||
return not_an_error(signaled);
|
||||
}
|
||||
|
||||
@@ -1438,7 +1443,7 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_net.warning("sys_net_bnet_select(nfds=%d, readfds=*0x%x, writefds=*0x%x, exceptfds=*0x%x, timeout=*0x%x)", nfds, readfds, writefds, exceptfds, _timeout);
|
||||
sys_net.trace("sys_net_bnet_select(nfds=%d, readfds=*0x%x, writefds=*0x%x, exceptfds=*0x%x, timeout=*0x%x)", nfds, readfds, writefds, exceptfds, _timeout);
|
||||
|
||||
atomic_t<s32> signaled{0};
|
||||
|
||||
@@ -1664,6 +1669,11 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_timedout && !signaled)
|
||||
{
|
||||
return -SYS_NET_EINTR;
|
||||
}
|
||||
|
||||
if (readfds)
|
||||
*readfds = rread;
|
||||
if (writefds)
|
||||
@@ -1671,11 +1681,6 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
|
||||
if (exceptfds)
|
||||
*exceptfds = rexcept;
|
||||
|
||||
if (!has_timedout && !signaled)
|
||||
{
|
||||
return -SYS_NET_EINTR;
|
||||
}
|
||||
|
||||
return not_an_error(signaled);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ lv2_socket_native::lv2_socket_native(lv2_socket_family family, lv2_socket_type t
|
||||
}
|
||||
|
||||
lv2_socket_native::lv2_socket_native(utils::serial& ar, lv2_socket_type type)
|
||||
: lv2_socket(ar, type)
|
||||
: lv2_socket(stx::make_exact(ar), type)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ar(so_reuseaddr, so_reuseport);
|
||||
@@ -37,7 +37,7 @@ lv2_socket_native::lv2_socket_native(utils::serial& ar, lv2_socket_type type)
|
||||
|
||||
void lv2_socket_native::save(utils::serial& ar)
|
||||
{
|
||||
static_cast<lv2_socket*>(this)->save(ar, true);
|
||||
lv2_socket::save(ar, true);
|
||||
#ifdef _WIN32
|
||||
ar(so_reuseaddr, so_reuseport);
|
||||
#else
|
||||
|
||||
@@ -9,33 +9,36 @@ LOG_CHANNEL(sys_net);
|
||||
lv2_socket_p2p::lv2_socket_p2p(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol)
|
||||
: lv2_socket(family, type, protocol)
|
||||
{
|
||||
sockopt_cache cache_type;
|
||||
cache_type.data._int = SYS_NET_SOCK_DGRAM_P2P;
|
||||
cache_type.len = 4;
|
||||
|
||||
sockopts[(static_cast<u64>(SYS_NET_SOL_SOCKET) << 32ull) | SYS_NET_SO_TYPE] = cache_type;
|
||||
}
|
||||
|
||||
lv2_socket_p2p::lv2_socket_p2p(utils::serial& ar, lv2_socket_type type)
|
||||
: lv2_socket(ar, type)
|
||||
: lv2_socket(stx::make_exact(ar), type)
|
||||
{
|
||||
ar(port, vport, bound_addr);
|
||||
|
||||
std::deque<std::pair<sys_net_sockaddr_in_p2p, std::vector<u8>>> data_dequeue{ar};
|
||||
auto data_dequeue = ar.pop<std::deque<std::pair<sys_net_sockaddr_in_p2p, std::vector<u8>>>>();
|
||||
|
||||
for (; !data_dequeue.empty(); data_dequeue.pop_front())
|
||||
{
|
||||
data.push(data_dequeue.front());
|
||||
data.push(std::move(data_dequeue.front()));
|
||||
}
|
||||
|
||||
ar(data_dequeue);
|
||||
}
|
||||
|
||||
void lv2_socket_p2p::save(utils::serial& ar)
|
||||
{
|
||||
static_cast<lv2_socket*>(this)->save(ar, true);
|
||||
lv2_socket::save(ar, true);
|
||||
ar(port, vport, bound_addr);
|
||||
|
||||
std::deque<std::pair<sys_net_sockaddr_in_p2p, std::vector<u8>>> data_dequeue;
|
||||
|
||||
for (; !data.empty(); data.pop())
|
||||
for (auto save_data = ::as_rvalue(data); !save_data.empty(); save_data.pop())
|
||||
{
|
||||
data_dequeue.push_back(data.front());
|
||||
data_dequeue.push_back(std::move(save_data.front()));
|
||||
}
|
||||
|
||||
ar(data_dequeue);
|
||||
@@ -238,8 +241,6 @@ std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_p2p
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", vport, data.size());
|
||||
|
||||
if (data.empty())
|
||||
{
|
||||
if (so_nbio || (flags & SYS_NET_MSG_DONTWAIT))
|
||||
@@ -248,6 +249,8 @@ std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_p2p
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", vport, data.size());
|
||||
|
||||
std::vector<u8> res_buf(len);
|
||||
|
||||
const auto& p2p_data = data.front();
|
||||
@@ -285,9 +288,11 @@ std::optional<s32> lv2_socket_p2p::sendto(s32 flags, const std::vector<u8>& buf,
|
||||
|
||||
std::vector<u8> p2p_data(buf.size() + VPORT_P2P_HEADER_SIZE);
|
||||
const le_t<u16> p2p_vport_le = p2p_vport;
|
||||
const le_t<u16> src_vport_le = vport;
|
||||
const le_t<u16> p2p_flags_le = P2P_FLAG_P2P;
|
||||
memcpy(p2p_data.data(), &p2p_vport_le, sizeof(u16));
|
||||
memcpy(p2p_data.data() + sizeof(u16), &p2p_flags_le, sizeof(u16));
|
||||
memcpy(p2p_data.data() + sizeof(u16), &src_vport_le, sizeof(u16));
|
||||
memcpy(p2p_data.data() + sizeof(u16) + sizeof(u16), &p2p_flags_le, sizeof(u16));
|
||||
memcpy(p2p_data.data() + VPORT_P2P_HEADER_SIZE, buf.data(), buf.size());
|
||||
|
||||
int native_flags = 0;
|
||||
@@ -360,7 +365,7 @@ s32 lv2_socket_p2p::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
ensure(vport);
|
||||
sys_net.trace("[P2P] poll checking for 0x%X", sn_pfd.events);
|
||||
|
||||
// Check if it's a bound P2P socket
|
||||
if ((sn_pfd.events & SYS_NET_POLLIN) && !data.empty())
|
||||
{
|
||||
@@ -393,7 +398,6 @@ std::tuple<bool, bool, bool> lv2_socket_p2p::select(bs_t<lv2_socket::poll_t> sel
|
||||
|
||||
if (selected & lv2_socket::poll_t::write)
|
||||
{
|
||||
sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", vport, data.size());
|
||||
write_set = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -225,10 +225,12 @@ std::vector<u8> generate_u2s_packet(const p2ps_encapsulated_tcp& header, const u
|
||||
std::vector<u8> packet(packet_size);
|
||||
u8* packet_data = packet.data();
|
||||
le_t<u16> dst_port_le = +header.dst_port;
|
||||
le_t<u16> src_port_le = +header.src_port;
|
||||
le_t<u16> p2p_flags_le = P2P_FLAG_P2PS;
|
||||
|
||||
memcpy(packet_data, &dst_port_le, sizeof(u16));
|
||||
memcpy(packet_data + sizeof(u16), &p2p_flags_le, sizeof(u16));
|
||||
memcpy(packet_data + sizeof(u16), &src_port_le, sizeof(u16));
|
||||
memcpy(packet_data + sizeof(u16) + sizeof(u16), &p2p_flags_le, sizeof(u16));
|
||||
memcpy(packet_data + VPORT_P2P_HEADER_SIZE, &header, sizeof(p2ps_encapsulated_tcp));
|
||||
if (datasize)
|
||||
memcpy(packet_data + VPORT_P2P_HEADER_SIZE + sizeof(p2ps_encapsulated_tcp), data, datasize);
|
||||
@@ -243,6 +245,11 @@ std::vector<u8> generate_u2s_packet(const p2ps_encapsulated_tcp& header, const u
|
||||
lv2_socket_p2ps::lv2_socket_p2ps(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol)
|
||||
: lv2_socket_p2p(family, type, protocol)
|
||||
{
|
||||
sockopt_cache cache_type;
|
||||
cache_type.data._int = SYS_NET_SOCK_STREAM_P2P;
|
||||
cache_type.len = 4;
|
||||
|
||||
sockopts[(static_cast<u64>(SYS_NET_SOL_SOCKET) << 32ull) | SYS_NET_SO_TYPE] = cache_type;
|
||||
}
|
||||
|
||||
lv2_socket_p2ps::lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq, s32 so_nbio)
|
||||
|
||||
@@ -10,13 +10,13 @@ lv2_socket_raw::lv2_socket_raw(lv2_socket_family family, lv2_socket_type type, l
|
||||
}
|
||||
|
||||
lv2_socket_raw::lv2_socket_raw(utils::serial& ar, lv2_socket_type type)
|
||||
: lv2_socket(ar, type)
|
||||
: lv2_socket(stx::make_exact(ar), type)
|
||||
{
|
||||
}
|
||||
|
||||
void lv2_socket_raw::save(utils::serial& ar)
|
||||
{
|
||||
static_cast<lv2_socket*>(this)->save(ar, true);
|
||||
lv2_socket::save(ar, true);
|
||||
}
|
||||
|
||||
std::tuple<bool, s32, std::shared_ptr<lv2_socket>, sys_net_sockaddr> lv2_socket_raw::accept([[maybe_unused]] bool is_lock)
|
||||
|
||||
@@ -19,6 +19,9 @@ s32 send_packet_from_p2p_port(const std::vector<u8>& data, const sockaddr_in& ad
|
||||
{
|
||||
auto& def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT);
|
||||
res = ::sendto(def_port.p2p_socket, reinterpret_cast<const char*>(data.data()), ::size32(data), 0, reinterpret_cast<const sockaddr*>(&addr), sizeof(sockaddr_in));
|
||||
|
||||
if (res == -1)
|
||||
sys_net.error("Failed to send signaling packet: %s", get_last_error(false, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -204,7 +204,8 @@ bool nt_p2p_port::recv_data()
|
||||
return true;
|
||||
}
|
||||
|
||||
const u16 vport_flags = *reinterpret_cast<le_t<u16>*>(p2p_recv_data.data() + sizeof(u16));
|
||||
const u16 src_vport = *reinterpret_cast<le_t<u16>*>(p2p_recv_data.data() + sizeof(u16));
|
||||
const u16 vport_flags = *reinterpret_cast<le_t<u16>*>(p2p_recv_data.data() + sizeof(u16) + sizeof(u16));
|
||||
std::vector<u8> p2p_data(recv_res - VPORT_P2P_HEADER_SIZE);
|
||||
memcpy(p2p_data.data(), p2p_recv_data.data() + VPORT_P2P_HEADER_SIZE, p2p_data.size());
|
||||
|
||||
@@ -218,7 +219,7 @@ bool nt_p2p_port::recv_data()
|
||||
p2p_addr.sin_len = sizeof(sys_net_sockaddr_in);
|
||||
p2p_addr.sin_family = SYS_NET_AF_INET;
|
||||
p2p_addr.sin_addr = std::bit_cast<be_t<u32>, u32>(reinterpret_cast<struct sockaddr_in*>(&native_addr)->sin_addr.s_addr);
|
||||
p2p_addr.sin_vport = dst_vport;
|
||||
p2p_addr.sin_vport = src_vport;
|
||||
p2p_addr.sin_port = std::bit_cast<be_t<u16>, u16>(reinterpret_cast<struct sockaddr_in*>(&native_addr)->sin_port);
|
||||
|
||||
auto& bound_sockets = ::at32(bound_p2p_vports, dst_vport);
|
||||
@@ -313,6 +314,6 @@ bool nt_p2p_port::recv_data()
|
||||
}
|
||||
}
|
||||
|
||||
sys_net.notice("Received a STREAM-P2P packet with no bound target");
|
||||
sys_net.notice("Received a P2P packet with no bound target(dst_vport = %d)", dst_vport);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
constexpr s32 VPORT_P2P_HEADER_SIZE = sizeof(u16) + sizeof(u16);
|
||||
// dst_vport src_vport flags
|
||||
constexpr s32 VPORT_P2P_HEADER_SIZE = sizeof(u16) + sizeof(u16) + sizeof(u16);
|
||||
|
||||
enum VPORT_P2P_FLAGS
|
||||
{
|
||||
|
||||
@@ -36,7 +36,15 @@ static error_code overlay_load_module(vm::ptr<u32> ovlmid, const std::string& vp
|
||||
|
||||
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
|
||||
|
||||
ppu_exec_object obj = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
|
||||
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
|
||||
|
||||
if (!src)
|
||||
{
|
||||
return {CELL_ENOEXEC, +"Failed to decrypt file"};
|
||||
}
|
||||
|
||||
ppu_exec_object obj = std::move(src);
|
||||
src.close();
|
||||
|
||||
if (obj != elf_error::ok)
|
||||
{
|
||||
@@ -72,8 +80,11 @@ fs::file make_file_view(fs::file&& file, u64 offset, u64 size);
|
||||
|
||||
std::shared_ptr<void> lv2_overlay::load(utils::serial& ar)
|
||||
{
|
||||
const std::string path = vfs::get(ar.pop<std::string>());
|
||||
const s64 offset = ar;
|
||||
const std::string vpath = ar.pop<std::string>();
|
||||
const std::string path = vfs::get(vpath);
|
||||
const s64 offset = ar.pop<s64>();
|
||||
|
||||
sys_overlay.success("lv2_overlay::load(): vpath='%s', path='%s', offset=0x%x", vpath, path, offset);
|
||||
|
||||
std::shared_ptr<lv2_overlay> ovlm;
|
||||
|
||||
@@ -84,11 +95,19 @@ std::shared_ptr<void> lv2_overlay::load(utils::serial& ar)
|
||||
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
|
||||
file = make_file_view(std::move(file), offset, umax);
|
||||
ovlm = ppu_load_overlay(ppu_exec_object{ decrypt_self(std::move(file), reinterpret_cast<u8*>(&klic)) }, false, path, 0, &ar).first;
|
||||
ensure(ovlm);
|
||||
|
||||
if (!ovlm)
|
||||
{
|
||||
fmt::throw_exception("lv2_overlay::load(): ppu_load_overlay() failed. (vpath='%s', offset=0x%x)", vpath, offset);
|
||||
}
|
||||
}
|
||||
else if (!g_cfg.savestate.state_inspection_mode.get())
|
||||
{
|
||||
fmt::throw_exception("lv2_overlay::load(): Failed to find file. (vpath='%s', offset=0x%x)", vpath, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure(g_cfg.savestate.state_inspection_mode.get());
|
||||
sys_overlay.error("lv2_overlay::load(): Failed to find file. (vpath='%s', offset=0x%x)", vpath, offset);
|
||||
}
|
||||
|
||||
return ovlm;
|
||||
@@ -97,7 +116,11 @@ std::shared_ptr<void> lv2_overlay::load(utils::serial& ar)
|
||||
void lv2_overlay::save(utils::serial& ar)
|
||||
{
|
||||
USING_SERIALIZATION_VERSION(lv2_prx_overlay);
|
||||
ar(vfs::retrieve(path), offset);
|
||||
|
||||
const std::string vpath = vfs::retrieve(path);
|
||||
(vpath.empty() ? sys_overlay.error : sys_overlay.success)("lv2_overlay::save(): vpath='%s', offset=0x%x", vpath, offset);
|
||||
|
||||
ar(vpath, offset);
|
||||
}
|
||||
|
||||
error_code sys_overlay_load_module(vm::ptr<u32> ovlmid, vm::cptr<char> path, u64 flags, vm::ptr<u32> entry)
|
||||
|
||||
@@ -507,7 +507,7 @@ error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr<u64> thread_id, vm::p
|
||||
// Try to obtain "physical memory" from the default container
|
||||
if (!dct.take(stack_size))
|
||||
{
|
||||
return CELL_ENOMEM;
|
||||
return {CELL_ENOMEM, dct.size - dct.used};
|
||||
}
|
||||
|
||||
const vm::addr_t stack_base{vm::alloc(stack_size, vm::stack, 4096)};
|
||||
|
||||
@@ -340,6 +340,8 @@ error_code sys_process_detach_child(u64 unk)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
extern void signal_system_cache_can_stay();
|
||||
|
||||
void _sys_process_exit(ppu_thread& ppu, s32 status, u32 arg2, u32 arg3)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
@@ -349,6 +351,7 @@ void _sys_process_exit(ppu_thread& ppu, s32 status, u32 arg2, u32 arg3)
|
||||
Emu.CallFromMainThread([]()
|
||||
{
|
||||
sys_process.success("Process finished");
|
||||
signal_system_cache_can_stay();
|
||||
Emu.Kill();
|
||||
});
|
||||
|
||||
@@ -481,6 +484,7 @@ void lv2_exitspawn(ppu_thread& ppu, std::vector<std::string>& argv, std::vector<
|
||||
}
|
||||
};
|
||||
|
||||
signal_system_cache_can_stay();
|
||||
Emu.Kill(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ extern const std::map<std::string_view, int> g_prx_list
|
||||
{ "libmvcdec.sprx", 0 },
|
||||
{ "libnet.sprx", 0 },
|
||||
{ "libnetctl.sprx", 1 },
|
||||
{ "libpamf.sprx", 0 },
|
||||
{ "libpamf.sprx", 1 },
|
||||
{ "libpngdec.sprx", 0 },
|
||||
{ "libpngenc.sprx", 0 },
|
||||
{ "libresc.sprx", 0 },
|
||||
@@ -263,11 +263,19 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
|
||||
|
||||
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
|
||||
|
||||
ppu_prx_object obj = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
|
||||
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
|
||||
|
||||
if (!src)
|
||||
{
|
||||
return {CELL_PRX_ERROR_UNSUPPORTED_PRX_TYPE, +"Failed to decrypt file"};
|
||||
}
|
||||
|
||||
ppu_prx_object obj = std::move(src);
|
||||
src.close();
|
||||
|
||||
if (obj != elf_error::ok)
|
||||
{
|
||||
return CELL_PRX_ERROR_UNSUPPORTED_PRX_TYPE;
|
||||
return {CELL_PRX_ERROR_UNSUPPORTED_PRX_TYPE, obj.get_error()};
|
||||
}
|
||||
|
||||
const auto prx = ppu_load_prx(obj, false, path, file_offset);
|
||||
|
||||
@@ -266,6 +266,7 @@ error_code sys_ss_appliance_info_manager(u32 code, vm::ptr<u8> buffer)
|
||||
case 0x19006:
|
||||
{
|
||||
// qa values (dex only) ??
|
||||
[[fallthrough]];
|
||||
}
|
||||
default: sys_ss.todo("sys_ss_appliance_info_manager(code=0x%x, buffer=*0x%x)", code, buffer);
|
||||
}
|
||||
@@ -497,7 +498,7 @@ error_code sys_ss_update_manager(u64 pkg_id, u64 a1, u64 a2, u64 a3, u64 a4, u64
|
||||
if (!addr_ptr)
|
||||
return CELL_EFAULT;
|
||||
|
||||
vm::write64(addr_ptr, 0x340ULL << 40); // 3.40
|
||||
vm::write64(addr_ptr, 0x30040ULL << 32); // 3.40
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user