Merge branch 'shadps4-emu:main' into main

This commit is contained in:
kalaposfos13 2024-12-14 07:15:06 +01:00 committed by GitHub
commit 0793e063ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
236 changed files with 5659 additions and 1303 deletions

View File

@ -89,7 +89,7 @@ jobs:
arch: amd64
- name: Configure CMake
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS
@ -143,7 +143,7 @@ jobs:
arch: amd64
- name: Configure CMake
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS
@ -201,7 +201,7 @@ jobs:
variant: sccache
- name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)
@ -265,7 +265,7 @@ jobs:
variant: sccache
- name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)
@ -312,7 +312,7 @@ jobs:
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
@ -368,7 +368,7 @@ jobs:
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)

2
.gitmodules vendored
View File

@ -102,6 +102,8 @@
[submodule "externals/LibAtrac9"]
path = externals/LibAtrac9
url = https://github.com/shadps4-emu/ext-LibAtrac9.git
shallow = true
[submodule "externals/libpng"]
path = externals/libpng
url = https://github.com/pnggroup/libpng
shallow = true

View File

@ -113,13 +113,14 @@ find_package(FFmpeg 5.1.2 MODULE)
find_package(fmt 10.2.0 CONFIG)
find_package(glslang 15 CONFIG)
find_package(half 1.12.0 MODULE)
find_package(magic_enum 0.9.6 CONFIG)
find_package(magic_enum 0.9.7 CONFIG)
find_package(PNG 1.6 MODULE)
find_package(RenderDoc 1.6.0 MODULE)
find_package(SDL3 3.1.2 CONFIG)
find_package(stb MODULE)
find_package(toml11 4.2.0 CONFIG)
find_package(tsl-robin-map 1.3.0 CONFIG)
find_package(VulkanHeaders 1.3.289 CONFIG)
find_package(VulkanHeaders 1.4.303 CONFIG)
find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE)
@ -209,7 +210,10 @@ set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp
src/core/libraries/gnmdriver/gnm_error.h
)
set(KERNEL_LIB src/core/libraries/kernel/threads/condvar.cpp
set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp
src/core/libraries/kernel/sync/mutex.h
src/core/libraries/kernel/sync/semaphore.h
src/core/libraries/kernel/threads/condvar.cpp
src/core/libraries/kernel/threads/event_flag.cpp
src/core/libraries/kernel/threads/exception.cpp
src/core/libraries/kernel/threads/exception.h
@ -435,10 +439,14 @@ set(DEV_TOOLS src/core/devtools/layer.cpp
src/core/devtools/widget/frame_graph.cpp
src/core/devtools/widget/frame_graph.h
src/core/devtools/widget/imgui_memory_editor.h
src/core/devtools/widget/memory_map.cpp
src/core/devtools/widget/memory_map.h
src/core/devtools/widget/reg_popup.cpp
src/core/devtools/widget/reg_popup.h
src/core/devtools/widget/reg_view.cpp
src/core/devtools/widget/reg_view.h
src/core/devtools/widget/shader_list.cpp
src/core/devtools/widget/shader_list.h
src/core/devtools/widget/text_editor.cpp
src/core/devtools/widget/text_editor.h
)
@ -491,6 +499,8 @@ set(COMMON src/common/logging/backend.cpp
src/common/slot_vector.h
src/common/spin_lock.cpp
src/common/spin_lock.h
src/common/stb.cpp
src/common/stb.h
src/common/string_util.cpp
src/common/string_util.h
src/common/thread.cpp
@ -498,6 +508,7 @@ set(COMMON src/common/logging/backend.cpp
src/common/types.h
src/common/uint128.h
src/common/unique_function.h
src/common/va_ctx.h
src/common/version.h
src/common/ntapi.h
src/common/ntapi.cpp
@ -522,6 +533,12 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/crypto/crypto.cpp
src/core/crypto/crypto.h
src/core/crypto/keys.h
src/core/devices/base_device.cpp
src/core/devices/base_device.h
src/core/devices/ioccom.h
src/core/devices/logger.cpp
src/core/devices/logger.h
src/core/devices/nop_device.h
src/core/file_format/pfs.h
src/core/file_format/pkg.cpp
src/core/file_format/pkg.h
@ -721,6 +738,8 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/renderer_vulkan/vk_resource_pool.h
src/video_core/renderer_vulkan/vk_scheduler.cpp
src/video_core/renderer_vulkan/vk_scheduler.h
src/video_core/renderer_vulkan/vk_shader_hle.cpp
src/video_core/renderer_vulkan/vk_shader_hle.h
src/video_core/renderer_vulkan/vk_shader_util.cpp
src/video_core/renderer_vulkan/vk_shader_util.h
src/video_core/renderer_vulkan/vk_swapchain.cpp
@ -862,11 +881,19 @@ endif()
create_target_directory_groups(shadps4)
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers)
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
if (ENABLE_DISCORD_RPC)
target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD)
endif()
if (APPLE)
option(USE_SYSTEM_VULKAN_LOADER "Enables using the system Vulkan loader instead of directly linking with MoltenVK. Useful for loading validation layers." OFF)
if (USE_SYSTEM_VULKAN_LOADER)
@ -995,4 +1022,4 @@ if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
install(FILES "dist/net.shadps4.shadPS4.metainfo.xml" DESTINATION "share/metainfo")
install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps" RENAME "net.shadps4.shadPS4.png")
install(FILES "src/images/net.shadps4.shadPS4.svg" DESTINATION "share/icons/hicolor/scalable/apps")
endif()
endif()

View File

@ -12,12 +12,13 @@ path = [
"dist/net.shadps4.shadPS4_metadata.pot",
"dist/net.shadps4.shadPS4.metainfo.xml",
"dist/net.shadps4.shadPS4.releases.xml",
"documents/changelog.txt",
"documents/changelog.md",
"documents/Quickstart/2.png",
"documents/Screenshots/*",
"scripts/ps4_names.txt",
"src/images/about_icon.png",
"src/images/controller_icon.png",
"src/images/discord.png",
"src/images/dump_icon.png",
"src/images/exit_icon.png",
"src/images/file_icon.png",
@ -28,8 +29,10 @@ path = [
"src/images/flag_us.png",
"src/images/flag_world.png",
"src/images/folder_icon.png",
"src/images/github.png",
"src/images/grid_icon.png",
"src/images/iconsize_icon.png",
"src/images/ko-fi.png",
"src/images/list_icon.png",
"src/images/list_mode_icon.png",
"src/images/pause_icon.png",
@ -43,6 +46,8 @@ path = [
"src/images/net.shadps4.shadPS4.svg",
"src/images/themes_icon.png",
"src/images/update_icon.png",
"src/images/youtube.png",
"src/images/website.png",
"src/shadps4.qrc",
"src/shadps4.rc",
]
@ -63,7 +68,7 @@ SPDX-FileCopyrightText = "2019-2024 Baldur Karlsson"
SPDX-License-Identifier = "MIT"
[[annotations]]
path = "externals/stb_image.h"
path = "externals/stb/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2017 Sean Barrett"
SPDX-License-Identifier = "MIT"

19
cmake/Findstb.cmake Normal file
View File

@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
find_path(stb_image_INCLUDE_DIR stb_image.h PATH_SUFFIXES stb)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(stb
REQUIRED_VARS stb_image_INCLUDE_DIR
)
if (stb_FOUND AND NOT TARGET stb::headers)
add_library(stb::headers INTERFACE IMPORTED)
set_property(TARGET stb::headers PROPERTY
INTERFACE_INCLUDE_DIRECTORIES
"${stb_image_INCLUDE_DIR}"
)
endif()
mark_as_advanced(stb_image_INCLUDE_DIR)

View File

@ -25,8 +25,8 @@ Once you are within the installer:
Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead.
1. Under the current, non beta version of Qt (at the time of writing 6.7.2), select the option `MSVC 2019 64-bit` or similar.
If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2019 ARM64` instead.
1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar.
If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead.
Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space.
@ -35,7 +35,7 @@ Beware, this requires you to create a Qt account. If you do not want to do this,
Once you are finished, you will have to configure Qt within Visual Studio:
1. Tools -> Options -> Qt -> Versions
2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.2\msvc2019_64`
2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.3\msvc2022_64`
3. Enable the default checkmark on the new version you just created.
### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win)
@ -55,16 +55,16 @@ Go through the Git for Windows installation as normal
3. If you want to build shadPS4 with the Qt Gui:
1. Click x64-Clang-Release and select "Manage Configurations"
2. Look for "CMake command arguments" and add to the text field
`-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.2\msvc2019_64`
`-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.3\msvc2022_64`
(Change Qt path if you've installed it to non-default path)
3. Press CTRL+S to save and wait a moment for CMake generation
4. Change the project to build to shadps4.exe
5. Build -> Build All
Your shadps4.exe will be in `c:\path\to\source\Build\x64-Clang-Release\`
Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\`
To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal:
`C:\Qt\6.7.2\msvc2019_64\bin\windeployqt.exe "c:\path\to\shadps4.exe"`
`C:\Qt\6.7.3\msvc2022_64\bin\windeployqt.exe "C:\path\to\shadps4.exe"`
(Change Qt path if you've installed it to non-default path)
## Option 2: MSYS2/MinGW
@ -79,7 +79,7 @@ Normal x86-based computers, follow:
1. Open "MSYS2 MINGW64" from your new applications
2. Run `pacman -Syu`, let it complete;
3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg`
3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-rapidjson mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg`
1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools mingw-w64-x86_64-qt6-multimedia`
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4`
@ -93,7 +93,7 @@ ARM64-based computers, follow:
1. Open "MSYS2 CLANGARM64" from your new applications
2. Run `pacman -Syu`, let it complete;
3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg`
3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-rapidjson mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg`
1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia`
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4`

View File

@ -1,3 +1,47 @@
v0.4.0 31/10/2024 - codename divicius
=================
- Shader recompiler fixes
- Emulated support for cpus that doesn't have SSE4.2a (intel cpus)
- Frame graph + Precise 60 fps timing
- Save data: fix nullptr & concurrent file write
- Auto Update
- Error dialog implementation
- Swapchain recreation and window resizing
- Add playback of background/title music in game list
- Kernel: Quiet sceKernelWaitEventFlag error log on timeout
- Improve keyboard navigation in game list
- core/memory: Pooled memory implementation
- Fix PKG loading
- replace trophy xml assert with error
- Refactor audio handling with range checks, buffer threshold, and lock
- audio_core: Fix return value types and shift some error handling to library
- Devtools: PM4 Explorer
- Initial support of Geometry shaders
- Working touchpad support
- net: Stub sceNetErrnoLoc
- Add support to click touchpad using back button on non PS4/5 controllers
- Multiple Install Folders
- Using a more standard data directory for linux
- video_core: Implement sceGnmInsertPushColorMarker
- ime_dialog: Initial implementation
- Network libs fixes
- Use GetSystemTimePreciseAsFileTime to fix fps timing issues
- Added adaptive mutex initializer
- Small Np + trophy fixes
- Separate Updates from Game Folder
- Minor Fixes for Separate Update Folder
- AvPlayer: Do not align w/h to 16 with vdec2
- Improve sceSystemServiceReceiveEvent stub
- renderer_vulkan: Commize and adjust buffer bindings
- Add poll interval to libScePad
- Add more surface format mappings.
- vulkan: Report only missing format feature flags.
- IME implementation
- Videodec2 implementation
- path_util: Make sure macOS has current directory set and clean up path code.
- Load LLE modules from sys_modules/GAMEID folder
v0.3.0 23/09/2024 - codename broamic
=================

View File

@ -8,6 +8,9 @@ set_directory_properties(PROPERTIES
SYSTEM ON
)
# Set CMP0069 policy to "NEW" in order to ensure consistent behavior when building external targets with LTO enabled
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
if (MSVC)
# Silence "deprecation" warnings
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS)
@ -35,7 +38,7 @@ else()
if (NOT TARGET cryptopp::cryptopp)
set(CRYPTOPP_INSTALL OFF)
set(CRYPTOPP_BUILD_TESTING OFF)
set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/)
set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp)
add_subdirectory(cryptopp-cmake)
file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h")
# remove externals/cryptopp from include directories because it contains a conflicting zlib.h file
@ -69,7 +72,7 @@ if (NOT TARGET ZLIB::ZLIB)
FetchContent_MakeAvailable(ZLIB)
add_library(ZLIB::ZLIB ALIAS zlib)
# libpng expects this variable to exist after its find_package(ZLIB)
get_target_property(ZLIB_INCLUDE_DIRS zlib INTERFACE_INCLUDE_DIRECTORIES)
set(ZLIB_INCLUDE_DIRS "${FETCHCONTENT_BASE_DIR}/zlib-build")
endif()
# SDL3
@ -216,9 +219,16 @@ endif()
# Discord RPC
if (ENABLE_DISCORD_RPC)
set(BUILD_EXAMPLES OFF)
add_subdirectory(discord-rpc/)
add_subdirectory(discord-rpc)
target_include_directories(discord-rpc INTERFACE discord-rpc/include)
endif()
# GCN Headers
add_subdirectory(gcn)
# stb
if (NOT TARGET stb::headers)
add_library(stb INTERFACE)
target_include_directories(stb INTERFACE stb)
add_library(stb::headers ALIAS stb)
endif()

2
externals/LibAtrac9 vendored

@ -1 +1 @@
Subproject commit 3acdcdc78f129c2e6145331ff650fa76dd88d62c
Subproject commit 9640129dc6f2afbca6ceeca3019856e8653a5fb2

2
externals/date vendored

@ -1 +1 @@
Subproject commit dd8affc6de5755e07638bf0a14382d29549d6ee9
Subproject commit 28b7b232521ace2c8ef3f2ad4126daec3569c14f

2
externals/ext-boost vendored

@ -1 +1 @@
Subproject commit f2474e1b584fb7a3ed6f85ba875e6eacd742ec8a
Subproject commit ca6f230e67be7cc45fc919057f07b2aee64dadc1

2
externals/glslang vendored

@ -1 +1 @@
Subproject commit e61d7bb3006f451968714e2f653412081871e1ee
Subproject commit a0995c49ebcaca2c6d3b03efbabf74f3843decdb

@ -1 +1 @@
Subproject commit 126539e13cccdc2e75ce770e94f3c26403099fa5
Subproject commit 1a1824df7ac798177a521eed952720681b0bf482

2
externals/pugixml vendored

@ -1 +1 @@
Subproject commit 3b17184379fcaaeb7f1fbe08018b7fedf2640b3b
Subproject commit 4bc14418d12d289dd9978fdce9490a45deeb653e

2
externals/sdl3 vendored

@ -1 +1 @@
Subproject commit 54e622c2e6af456bfef382fae44c17682d5ac88a
Subproject commit 3a1d76d298db023f6cf37fb08ee766f20a4e12ab

2
externals/sirit vendored

@ -1 +1 @@
Subproject commit 6cecb95d679c82c413d1f989e0b7ad9af130600d
Subproject commit e12b6b592ce9917a85303c555259488643c56f47

2
externals/toml11 vendored

@ -1 +1 @@
Subproject commit f925e7f287c0008813c2294798cf9ca167fd9ffd
Subproject commit 7f6c574ff5aa1053534e7e19c0a4f22bf4c6aaca

2
externals/vma vendored

@ -1 +1 @@
Subproject commit 1c35ba99ce775f8342d87a83a3f0f696f99c2a39
Subproject commit 5a53a198945ba8260fbc58fadb788745ce6aa263

@ -1 +1 @@
Subproject commit d91597a82f881d473887b560a03a7edf2720b72c
Subproject commit 6a74a7d65cafa19e38ec116651436cce6efd5b2e

2
externals/xbyak vendored

@ -1 +1 @@
Subproject commit d067f0d3f55696ae8bc9a25ad7012ee80f221d54
Subproject commit 4e44f4614ddbf038f2a6296f5b906d5c72691e0f

2
externals/xxhash vendored

@ -1 +1 @@
Subproject commit d4ad85e4afaad5c780f54db1dc967fff5a869ffd
Subproject commit 2bf8313b934633b2a5b7e8fd239645b85e10c852

2
externals/zydis vendored

@ -1 +1 @@
Subproject commit 9d298eb8067ff62a237203d1e1470785033e185c
Subproject commit bffbb610cfea643b98e87658b9058382f7522807

View File

@ -47,6 +47,7 @@ static std::string backButtonBehavior = "left";
static bool useSpecialPad = false;
static int specialPadClass = 1;
static bool isDebugDump = false;
static bool isShaderDebug = false;
static bool isShowSplash = false;
static bool isAutoUpdate = false;
static bool isNullGpu = false;
@ -159,6 +160,10 @@ bool debugDump() {
return isDebugDump;
}
bool collectShadersForDebug() {
return isShaderDebug;
}
bool showSplash() {
return isShowSplash;
}
@ -235,6 +240,10 @@ void setDebugDump(bool enable) {
isDebugDump = enable;
}
void setCollectShaderForDebug(bool enable) {
isShaderDebug = enable;
}
void setShowSplash(bool enable) {
isShowSplash = enable;
}
@ -413,6 +422,10 @@ void setEmulatorLanguage(std::string language) {
emulator_language = language;
}
void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_install_dirs_config) {
settings_install_dirs = settings_install_dirs_config;
}
u32 getMainWindowGeometryX() {
return main_window_geometry_x;
}
@ -571,6 +584,7 @@ void load(const std::filesystem::path& path) {
const toml::value& debug = data.at("Debug");
isDebugDump = toml::find_or<bool>(debug, "DebugDump", false);
isShaderDebug = toml::find_or<bool>(debug, "CollectShader", false);
}
if (data.contains("GUI")) {
@ -662,14 +676,7 @@ void save(const std::filesystem::path& path) {
data["Vulkan"]["rdocMarkersEnable"] = vkMarkers;
data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic;
data["Debug"]["DebugDump"] = isDebugDump;
data["GUI"]["theme"] = mw_themes;
data["GUI"]["iconSize"] = m_icon_size;
data["GUI"]["sliderPos"] = m_slider_pos;
data["GUI"]["iconSizeGrid"] = m_icon_size_grid;
data["GUI"]["sliderPosGrid"] = m_slider_pos_grid;
data["GUI"]["gameTableMode"] = m_table_mode;
data["GUI"]["mw_width"] = m_window_size_W;
data["GUI"]["mw_height"] = m_window_size_H;
data["Debug"]["CollectShader"] = isShaderDebug;
std::vector<std::string> install_dirs;
for (const auto& dirString : settings_install_dirs) {
@ -679,6 +686,43 @@ void save(const std::filesystem::path& path) {
data["GUI"]["addonInstallDir"] =
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
data["GUI"]["emulatorLanguage"] = emulator_language;
data["Settings"]["consoleLanguage"] = m_language;
std::ofstream file(path, std::ios::binary);
file << data;
file.close();
}
void saveMainWindow(const std::filesystem::path& path) {
toml::value data;
std::error_code error;
if (std::filesystem::exists(path, error)) {
try {
std::ifstream ifs;
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
ifs.open(path, std::ios_base::binary);
data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data});
} catch (const std::exception& ex) {
fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what());
return;
}
} else {
if (error) {
fmt::print("Filesystem error: {}\n", error.message());
}
fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string()));
}
data["GUI"]["mw_width"] = m_window_size_W;
data["GUI"]["mw_height"] = m_window_size_H;
data["GUI"]["theme"] = mw_themes;
data["GUI"]["iconSize"] = m_icon_size;
data["GUI"]["sliderPos"] = m_slider_pos;
data["GUI"]["iconSizeGrid"] = m_icon_size_grid;
data["GUI"]["sliderPosGrid"] = m_slider_pos_grid;
data["GUI"]["gameTableMode"] = m_table_mode;
data["GUI"]["geometry_x"] = main_window_geometry_x;
data["GUI"]["geometry_y"] = main_window_geometry_y;
data["GUI"]["geometry_w"] = main_window_geometry_w;
@ -686,9 +730,6 @@ void save(const std::filesystem::path& path) {
data["GUI"]["pkgDirs"] = m_pkg_viewer;
data["GUI"]["elfDirs"] = m_elf_viewer;
data["GUI"]["recentFiles"] = m_recent_files;
data["GUI"]["emulatorLanguage"] = emulator_language;
data["Settings"]["consoleLanguage"] = m_language;
std::ofstream file(path, std::ios::binary);
file << data;
@ -717,6 +758,7 @@ void setDefaultValues() {
useSpecialPad = false;
specialPadClass = 1;
isDebugDump = false;
isShaderDebug = false;
isShowSplash = false;
isAutoUpdate = false;
isNullGpu = false;
@ -731,6 +773,7 @@ void setDefaultValues() {
emulator_language = "en";
m_language = 1;
gpuId = -1;
separateupdatefolder = false;
}
constexpr std::string_view GetDefaultKeyboardConfig() {

View File

@ -13,6 +13,7 @@ enum HideCursorState : s16 { Never, Idle, Always };
void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path);
void saveMainWindow(const std::filesystem::path& path);
bool isNeoMode();
bool isFullscreenMode();
@ -37,6 +38,7 @@ u32 getScreenHeight();
s32 getGpuId();
bool debugDump();
bool collectShadersForDebug();
bool showSplash();
bool autoUpdate();
bool nullGpu();
@ -47,6 +49,7 @@ bool isRdocEnabled();
u32 vblankDiv();
void setDebugDump(bool enable);
void setCollectShaderForDebug(bool enable);
void setShowSplash(bool enable);
void setAutoUpdate(bool enable);
void setNullGpu(bool enable);
@ -65,6 +68,7 @@ void setNeoMode(bool enable);
void setUserName(const std::string& type);
void setUpdateChannel(const std::string& type);
void setSeparateUpdateEnabled(bool use);
void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_install_dirs_config);
void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout);
@ -129,4 +133,4 @@ std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id = "")
// settings
u32 GetLanguage();
}; // namespace Config
}; // namespace Config

View File

@ -377,16 +377,18 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
return false;
}
u64 size = GetSize();
if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) {
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
return false;
} else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) {
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
return false;
} else if (origin == SeekOrigin::End && offset > 0) {
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
return false;
if (False(file_access_mode & (FileAccessMode::Write | FileAccessMode::Append))) {
u64 size = GetSize();
if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) {
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
return false;
} else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) {
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
return false;
} else if (origin == SeekOrigin::End && offset > 0) {
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
return false;
}
}
errno = 0;

View File

@ -10,6 +10,7 @@
#include "common/concepts.h"
#include "common/types.h"
#include "enum.h"
namespace Common::FS {
@ -42,6 +43,7 @@ enum class FileAccessMode {
*/
ReadAppend = Read | Append,
};
DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode);
enum class FileType {
BinaryFile,

View File

@ -69,6 +69,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Common, Memory) \
CLS(Core) \
SUB(Core, Linker) \
SUB(Core, Devices) \
CLS(Config) \
CLS(Debug) \
CLS(Kernel) \

View File

@ -35,6 +35,7 @@ enum class Class : u8 {
Common_Memory, ///< Memory mapping and management functions
Core, ///< LLE emulation core
Core_Linker, ///< The module linker
Core_Devices, ///< Devices emulation
Config, ///< Emulator configuration (including commandline)
Debug, ///< Debugging tools
Kernel, ///< The HLE implementation of the PS4 kernel.

View File

@ -5,8 +5,11 @@
#include "ntapi.h"
NtDelayExecution_t NtDelayExecution = nullptr;
NtClose_t NtClose = nullptr;
NtSetInformationFile_t NtSetInformationFile = nullptr;
NtCreateThread_t NtCreateThread = nullptr;
NtTerminateThread_t NtTerminateThread = nullptr;
NtQueueApcThreadEx_t NtQueueApcThreadEx = nullptr;
namespace Common::NtApi {
@ -14,9 +17,12 @@ void Initialize() {
HMODULE nt_handle = GetModuleHandleA("ntdll.dll");
// http://stackoverflow.com/a/31411628/4725495
NtDelayExecution = (NtDelayExecution_t)GetProcAddress(nt_handle, "NtDelayExecution");
NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose");
NtSetInformationFile =
(NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile");
NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread");
NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread");
NtQueueApcThreadEx = (NtQueueApcThreadEx_t)GetProcAddress(nt_handle, "NtQueueApcThreadEx");
}
} // namespace Common::NtApi

View File

@ -108,14 +108,444 @@ typedef struct _FILE_DISPOSITION_INFORMATION {
BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
typedef u32(__stdcall* NtDelayExecution_t)(BOOL Alertable, PLARGE_INTEGER DelayInterval);
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWCH Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef u32(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
typedef const UNICODE_STRING* PCUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PCUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR;
PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
typedef const OBJECT_ATTRIBUTES* PCOBJECT_ATTRIBUTES;
typedef struct _CLIENT_ID {
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
typedef struct _INITIAL_TEB {
struct {
PVOID OldStackBase;
PVOID OldStackLimit;
} OldInitialTeb;
PVOID StackBase;
PVOID StackLimit;
PVOID StackAllocationBase;
} INITIAL_TEB, *PINITIAL_TEB;
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID EntryInProgress;
BOOLEAN ShutdownInProgress;
HANDLE ShutdownThreadId;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
typedef struct _CURDIR {
UNICODE_STRING DosPath;
PVOID Handle;
} CURDIR, *PCURDIR;
typedef struct RTL_DRIVE_LETTER_CURDIR {
USHORT Flags;
USHORT Length;
ULONG TimeStamp;
UNICODE_STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
typedef struct _RTL_USER_PROCESS_PARAMETERS {
ULONG AllocationSize;
ULONG Size;
ULONG Flags;
ULONG DebugFlags;
HANDLE ConsoleHandle;
ULONG ConsoleFlags;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
CURDIR CurrentDirectory;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
PWSTR Environment;
ULONG dwX;
ULONG dwY;
ULONG dwXSize;
ULONG dwYSize;
ULONG dwXCountChars;
ULONG dwYCountChars;
ULONG dwFillAttribute;
ULONG dwFlags;
ULONG wShowWindow;
UNICODE_STRING WindowTitle;
UNICODE_STRING Desktop;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeInfo;
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
ULONG_PTR EnvironmentSize;
ULONG_PTR EnvironmentVersion;
PVOID PackageDependencyData;
ULONG ProcessGroupId;
ULONG LoaderThreads;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
typedef struct tagRTL_BITMAP {
ULONG SizeOfBitMap;
PULONG Buffer;
} RTL_BITMAP, *PRTL_BITMAP;
typedef struct {
UINT next;
UINT id;
ULONGLONG addr;
ULONGLONG size;
UINT args[4];
} CROSS_PROCESS_WORK_ENTRY;
typedef union {
struct {
UINT first;
UINT counter;
};
volatile LONGLONG hdr;
} CROSS_PROCESS_WORK_HDR;
typedef struct {
CROSS_PROCESS_WORK_HDR free_list;
CROSS_PROCESS_WORK_HDR work_list;
ULONGLONG unknown[4];
CROSS_PROCESS_WORK_ENTRY entries[1];
} CROSS_PROCESS_WORK_LIST;
typedef struct _CHPEV2_PROCESS_INFO {
ULONG Wow64ExecuteFlags; /* 000 */
USHORT NativeMachineType; /* 004 */
USHORT EmulatedMachineType; /* 006 */
HANDLE SectionHandle; /* 008 */
CROSS_PROCESS_WORK_LIST* CrossProcessWorkList; /* 010 */
void* unknown; /* 018 */
} CHPEV2_PROCESS_INFO, *PCHPEV2_PROCESS_INFO;
typedef u64(__stdcall* KERNEL_CALLBACK_PROC)(void*, ULONG);
typedef struct _PEB { /* win32/win64 */
BOOLEAN InheritedAddressSpace; /* 000/000 */
BOOLEAN ReadImageFileExecOptions; /* 001/001 */
BOOLEAN BeingDebugged; /* 002/002 */
UCHAR ImageUsedLargePages : 1; /* 003/003 */
UCHAR IsProtectedProcess : 1;
UCHAR IsImageDynamicallyRelocated : 1;
UCHAR SkipPatchingUser32Forwarders : 1;
UCHAR IsPackagedProcess : 1;
UCHAR IsAppContainer : 1;
UCHAR IsProtectedProcessLight : 1;
UCHAR IsLongPathAwareProcess : 1;
HANDLE Mutant; /* 004/008 */
HMODULE ImageBaseAddress; /* 008/010 */
PPEB_LDR_DATA LdrData; /* 00c/018 */
RTL_USER_PROCESS_PARAMETERS* ProcessParameters; /* 010/020 */
PVOID SubSystemData; /* 014/028 */
HANDLE ProcessHeap; /* 018/030 */
PRTL_CRITICAL_SECTION FastPebLock; /* 01c/038 */
PVOID AtlThunkSListPtr; /* 020/040 */
PVOID IFEOKey; /* 024/048 */
ULONG ProcessInJob : 1; /* 028/050 */
ULONG ProcessInitializing : 1;
ULONG ProcessUsingVEH : 1;
ULONG ProcessUsingVCH : 1;
ULONG ProcessUsingFTH : 1;
ULONG ProcessPreviouslyThrottled : 1;
ULONG ProcessCurrentlyThrottled : 1;
ULONG ProcessImagesHotPatched : 1;
ULONG ReservedBits0 : 24;
KERNEL_CALLBACK_PROC* KernelCallbackTable; /* 02c/058 */
ULONG Reserved; /* 030/060 */
ULONG AtlThunkSListPtr32; /* 034/064 */
PVOID ApiSetMap; /* 038/068 */
ULONG TlsExpansionCounter; /* 03c/070 */
PRTL_BITMAP TlsBitmap; /* 040/078 */
ULONG TlsBitmapBits[2]; /* 044/080 */
PVOID ReadOnlySharedMemoryBase; /* 04c/088 */
PVOID SharedData; /* 050/090 */
PVOID* ReadOnlyStaticServerData; /* 054/098 */
PVOID AnsiCodePageData; /* 058/0a0 */
PVOID OemCodePageData; /* 05c/0a8 */
PVOID UnicodeCaseTableData; /* 060/0b0 */
ULONG NumberOfProcessors; /* 064/0b8 */
ULONG NtGlobalFlag; /* 068/0bc */
LARGE_INTEGER CriticalSectionTimeout; /* 070/0c0 */
SIZE_T HeapSegmentReserve; /* 078/0c8 */
SIZE_T HeapSegmentCommit; /* 07c/0d0 */
SIZE_T HeapDeCommitTotalFreeThreshold; /* 080/0d8 */
SIZE_T HeapDeCommitFreeBlockThreshold; /* 084/0e0 */
ULONG NumberOfHeaps; /* 088/0e8 */
ULONG MaximumNumberOfHeaps; /* 08c/0ec */
PVOID* ProcessHeaps; /* 090/0f0 */
PVOID GdiSharedHandleTable; /* 094/0f8 */
PVOID ProcessStarterHelper; /* 098/100 */
PVOID GdiDCAttributeList; /* 09c/108 */
PVOID LoaderLock; /* 0a0/110 */
ULONG OSMajorVersion; /* 0a4/118 */
ULONG OSMinorVersion; /* 0a8/11c */
ULONG OSBuildNumber; /* 0ac/120 */
ULONG OSPlatformId; /* 0b0/124 */
ULONG ImageSubSystem; /* 0b4/128 */
ULONG ImageSubSystemMajorVersion; /* 0b8/12c */
ULONG ImageSubSystemMinorVersion; /* 0bc/130 */
KAFFINITY ActiveProcessAffinityMask; /* 0c0/138 */
#ifdef _WIN64
ULONG GdiHandleBuffer[60]; /* /140 */
#else
ULONG GdiHandleBuffer[34]; /* 0c4/ */
#endif
PVOID PostProcessInitRoutine; /* 14c/230 */
PRTL_BITMAP TlsExpansionBitmap; /* 150/238 */
ULONG TlsExpansionBitmapBits[32]; /* 154/240 */
ULONG SessionId; /* 1d4/2c0 */
ULARGE_INTEGER AppCompatFlags; /* 1d8/2c8 */
ULARGE_INTEGER AppCompatFlagsUser; /* 1e0/2d0 */
PVOID ShimData; /* 1e8/2d8 */
PVOID AppCompatInfo; /* 1ec/2e0 */
UNICODE_STRING CSDVersion; /* 1f0/2e8 */
PVOID ActivationContextData; /* 1f8/2f8 */
PVOID ProcessAssemblyStorageMap; /* 1fc/300 */
PVOID SystemDefaultActivationData; /* 200/308 */
PVOID SystemAssemblyStorageMap; /* 204/310 */
SIZE_T MinimumStackCommit; /* 208/318 */
PVOID* FlsCallback; /* 20c/320 */
LIST_ENTRY FlsListHead; /* 210/328 */
union {
PRTL_BITMAP FlsBitmap; /* 218/338 */
#ifdef _WIN64
CHPEV2_PROCESS_INFO* ChpeV2ProcessInfo; /* /338 */
#endif
};
ULONG FlsBitmapBits[4]; /* 21c/340 */
ULONG FlsHighIndex; /* 22c/350 */
PVOID WerRegistrationData; /* 230/358 */
PVOID WerShipAssertPtr; /* 234/360 */
PVOID EcCodeBitMap; /* 238/368 */
PVOID pImageHeaderHash; /* 23c/370 */
ULONG HeapTracingEnabled : 1; /* 240/378 */
ULONG CritSecTracingEnabled : 1;
ULONG LibLoaderTracingEnabled : 1;
ULONG SpareTracingBits : 29;
ULONGLONG CsrServerReadOnlySharedMemoryBase; /* 248/380 */
ULONG TppWorkerpListLock; /* 250/388 */
LIST_ENTRY TppWorkerpList; /* 254/390 */
PVOID WaitOnAddressHashTable[0x80]; /* 25c/3a0 */
PVOID TelemetryCoverageHeader; /* 45c/7a0 */
ULONG CloudFileFlags; /* 460/7a8 */
ULONG CloudFileDiagFlags; /* 464/7ac */
CHAR PlaceholderCompatibilityMode; /* 468/7b0 */
CHAR PlaceholderCompatibilityModeReserved[7]; /* 469/7b1 */
PVOID LeapSecondData; /* 470/7b8 */
ULONG LeapSecondFlags; /* 474/7c0 */
ULONG NtGlobalFlag2; /* 478/7c4 */
} PEB, *PPEB;
typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME {
struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous;
struct _ACTIVATION_CONTEXT* ActivationContext;
ULONG Flags;
} RTL_ACTIVATION_CONTEXT_STACK_FRAME, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME;
typedef struct _ACTIVATION_CONTEXT_STACK {
RTL_ACTIVATION_CONTEXT_STACK_FRAME* ActiveFrame;
LIST_ENTRY FrameListCache;
ULONG Flags;
ULONG NextCookieSequenceNumber;
ULONG_PTR StackId;
} ACTIVATION_CONTEXT_STACK, *PACTIVATION_CONTEXT_STACK;
typedef struct _GDI_TEB_BATCH {
ULONG Offset;
HANDLE HDC;
ULONG Buffer[0x136];
} GDI_TEB_BATCH;
typedef struct _TEB_ACTIVE_FRAME_CONTEXT {
ULONG Flags;
const char* FrameName;
} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT;
typedef struct _TEB_ACTIVE_FRAME {
ULONG Flags;
struct _TEB_ACTIVE_FRAME* Previous;
TEB_ACTIVE_FRAME_CONTEXT* Context;
} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME;
typedef struct _TEB { /* win32/win64 */
NT_TIB Tib; /* 000/0000 */
PVOID EnvironmentPointer; /* 01c/0038 */
CLIENT_ID ClientId; /* 020/0040 */
PVOID ActiveRpcHandle; /* 028/0050 */
PVOID ThreadLocalStoragePointer; /* 02c/0058 */
PPEB Peb; /* 030/0060 */
ULONG LastErrorValue; /* 034/0068 */
ULONG CountOfOwnedCriticalSections; /* 038/006c */
PVOID CsrClientThread; /* 03c/0070 */
PVOID Win32ThreadInfo; /* 040/0078 */
ULONG User32Reserved[26]; /* 044/0080 */
ULONG UserReserved[5]; /* 0ac/00e8 */
PVOID WOW32Reserved; /* 0c0/0100 */
ULONG CurrentLocale; /* 0c4/0108 */
ULONG FpSoftwareStatusRegister; /* 0c8/010c */
PVOID ReservedForDebuggerInstrumentation[16]; /* 0cc/0110 */
#ifdef _WIN64
PVOID SystemReserved1[30]; /* /0190 */
#else
PVOID SystemReserved1[26]; /* 10c/ */
#endif
char PlaceholderCompatibilityMode; /* 174/0280 */
BOOLEAN PlaceholderHydrationAlwaysExplicit; /* 175/0281 */
char PlaceholderReserved[10]; /* 176/0282 */
DWORD ProxiedProcessId; /* 180/028c */
ACTIVATION_CONTEXT_STACK ActivationContextStack; /* 184/0290 */
UCHAR WorkingOnBehalfOfTicket[8]; /* 19c/02b8 */
LONG ExceptionCode; /* 1a4/02c0 */
ACTIVATION_CONTEXT_STACK* ActivationContextStackPointer; /* 1a8/02c8 */
ULONG_PTR InstrumentationCallbackSp; /* 1ac/02d0 */
ULONG_PTR InstrumentationCallbackPreviousPc; /* 1b0/02d8 */
ULONG_PTR InstrumentationCallbackPreviousSp; /* 1b4/02e0 */
#ifdef _WIN64
ULONG TxFsContext; /* /02e8 */
BOOLEAN InstrumentationCallbackDisabled; /* /02ec */
BOOLEAN UnalignedLoadStoreExceptions; /* /02ed */
#else
BOOLEAN InstrumentationCallbackDisabled; /* 1b8/ */
BYTE SpareBytes1[23]; /* 1b9/ */
ULONG TxFsContext; /* 1d0/ */
#endif
GDI_TEB_BATCH GdiTebBatch; /* 1d4/02f0 */
CLIENT_ID RealClientId; /* 6b4/07d8 */
HANDLE GdiCachedProcessHandle; /* 6bc/07e8 */
ULONG GdiClientPID; /* 6c0/07f0 */
ULONG GdiClientTID; /* 6c4/07f4 */
PVOID GdiThreadLocaleInfo; /* 6c8/07f8 */
ULONG_PTR Win32ClientInfo[62]; /* 6cc/0800 */
PVOID glDispatchTable[233]; /* 7c4/09f0 */
PVOID glReserved1[29]; /* b68/1138 */
PVOID glReserved2; /* bdc/1220 */
PVOID glSectionInfo; /* be0/1228 */
PVOID glSection; /* be4/1230 */
PVOID glTable; /* be8/1238 */
PVOID glCurrentRC; /* bec/1240 */
PVOID glContext; /* bf0/1248 */
ULONG LastStatusValue; /* bf4/1250 */
UNICODE_STRING StaticUnicodeString; /* bf8/1258 */
WCHAR StaticUnicodeBuffer[261]; /* c00/1268 */
PVOID DeallocationStack; /* e0c/1478 */
PVOID TlsSlots[64]; /* e10/1480 */
LIST_ENTRY TlsLinks; /* f10/1680 */
PVOID Vdm; /* f18/1690 */
PVOID ReservedForNtRpc; /* f1c/1698 */
PVOID DbgSsReserved[2]; /* f20/16a0 */
ULONG HardErrorMode; /* f28/16b0 */
#ifdef _WIN64
PVOID Instrumentation[11]; /* /16b8 */
#else
PVOID Instrumentation[9]; /* f2c/ */
#endif
GUID ActivityId; /* f50/1710 */
PVOID SubProcessTag; /* f60/1720 */
PVOID PerflibData; /* f64/1728 */
PVOID EtwTraceData; /* f68/1730 */
PVOID WinSockData; /* f6c/1738 */
ULONG GdiBatchCount; /* f70/1740 */
ULONG IdealProcessorValue; /* f74/1744 */
ULONG GuaranteedStackBytes; /* f78/1748 */
PVOID ReservedForPerf; /* f7c/1750 */
PVOID ReservedForOle; /* f80/1758 */
ULONG WaitingOnLoaderLock; /* f84/1760 */
PVOID SavedPriorityState; /* f88/1768 */
ULONG_PTR ReservedForCodeCoverage; /* f8c/1770 */
PVOID ThreadPoolData; /* f90/1778 */
PVOID* TlsExpansionSlots; /* f94/1780 */
#ifdef _WIN64
union {
PVOID DeallocationBStore; /* /1788 */
PVOID* ChpeV2CpuAreaInfo; /* /1788 */
} DUMMYUNIONNAME;
PVOID BStoreLimit; /* /1790 */
#endif
ULONG MuiGeneration; /* f98/1798 */
ULONG IsImpersonating; /* f9c/179c */
PVOID NlsCache; /* fa0/17a0 */
PVOID ShimData; /* fa4/17a8 */
ULONG HeapVirtualAffinity; /* fa8/17b0 */
PVOID CurrentTransactionHandle; /* fac/17b8 */
TEB_ACTIVE_FRAME* ActiveFrame; /* fb0/17c0 */
PVOID* FlsSlots; /* fb4/17c8 */
PVOID PreferredLanguages; /* fb8/17d0 */
PVOID UserPrefLanguages; /* fbc/17d8 */
PVOID MergedPrefLanguages; /* fc0/17e0 */
ULONG MuiImpersonation; /* fc4/17e8 */
USHORT CrossTebFlags; /* fc8/17ec */
USHORT SameTebFlags; /* fca/17ee */
PVOID TxnScopeEnterCallback; /* fcc/17f0 */
PVOID TxnScopeExitCallback; /* fd0/17f8 */
PVOID TxnScopeContext; /* fd4/1800 */
ULONG LockCount; /* fd8/1808 */
LONG WowTebOffset; /* fdc/180c */
PVOID ResourceRetValue; /* fe0/1810 */
PVOID ReservedForWdf; /* fe4/1818 */
ULONGLONG ReservedForCrt; /* fe8/1820 */
GUID EffectiveContainerId; /* ff0/1828 */
} TEB, *PTEB;
static_assert(offsetof(TEB, DeallocationStack) ==
0x1478); /* The only member we care about at the moment */
typedef enum _QUEUE_USER_APC_FLAGS {
QueueUserApcFlagsNone,
QueueUserApcFlagsSpecialUserApc,
QueueUserApcFlagsMaxValue
} QUEUE_USER_APC_FLAGS;
typedef union _USER_APC_OPTION {
ULONG_PTR UserApcFlags;
HANDLE MemoryReserveHandle;
} USER_APC_OPTION, *PUSER_APC_OPTION;
using PPS_APC_ROUTINE = void (*)(PVOID ApcArgument1, PVOID ApcArgument2, PVOID ApcArgument3,
PCONTEXT Context);
typedef u64(__stdcall* NtClose_t)(HANDLE Handle);
typedef u64(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
PVOID FileInformation, ULONG Length,
FILE_INFORMATION_CLASS FileInformationClass);
extern NtDelayExecution_t NtDelayExecution;
typedef u64(__stdcall* NtCreateThread_t)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess,
PCOBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle,
PCLIENT_ID ClientId, PCONTEXT ThreadContext,
PINITIAL_TEB InitialTeb, BOOLEAN CreateSuspended);
typedef u64(__stdcall* NtTerminateThread_t)(HANDLE ThreadHandle, u64 ExitStatus);
typedef u64(__stdcall* NtQueueApcThreadEx_t)(HANDLE ThreadHandle,
USER_APC_OPTION UserApcReserveHandle,
PPS_APC_ROUTINE ApcRoutine, PVOID ApcArgument1,
PVOID ApcArgument2, PVOID ApcArgument3);
extern NtClose_t NtClose;
extern NtSetInformationFile_t NtSetInformationFile;
extern NtCreateThread_t NtCreateThread;
extern NtTerminateThread_t NtTerminateThread;
extern NtQueueApcThreadEx_t NtQueueApcThreadEx;
namespace Common::NtApi {
void Initialize();

7
src/common/stb.cpp Normal file
View File

@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#define STBI_NO_STDIO
#include "common/stb.h"

6
src/common/stb.h Normal file
View File

@ -0,0 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stb_image.h>

View File

@ -37,6 +37,10 @@ std::vector<std::string> SplitString(const std::string& str, char delimiter) {
return output;
}
std::string_view U8stringToString(std::u8string_view u8str) {
return std::string_view{reinterpret_cast<const char*>(u8str.data()), u8str.size()};
}
#ifdef _WIN32
static std::wstring CPToUTF16(u32 code_page, std::string_view input) {
const auto size =

View File

@ -16,6 +16,8 @@ void ToLowerInPlace(std::string& str);
std::vector<std::string> SplitString(const std::string& str, char delimiter);
std::string_view U8stringToString(std::u8string_view u8str);
#ifdef _WIN32
[[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input);
[[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str);

View File

@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// support header file for libav
// The av_err2str macro in libavutil/error.h does not play nice with C++
#ifdef av_err2str
#undef av_err2str
#include <string>
av_always_inline std::string av_err2string(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum);
}
#define av_err2str(err) av_err2string(err).c_str()
#endif // av_err2str

View File

@ -147,6 +147,10 @@ void SetCurrentThreadName(const char* name) {
SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data());
}
void SetThreadName(void* thread, const char* name) {
SetThreadDescription(thread, UTF8ToUTF16W(name).data());
}
#else // !MSVC_VER, so must be POSIX threads
// MinGW with the POSIX threading model does not support pthread_setname_np
@ -170,11 +174,19 @@ void SetCurrentThreadName(const char* name) {
pthread_setname_np(pthread_self(), name);
#endif
}
void SetThreadName(void* thread, const char* name) {
// TODO
}
#endif
#if defined(_WIN32)
void SetCurrentThreadName(const char*) {
// Do Nothing on MingW
// Do Nothing on MinGW
}
void SetThreadName(void* thread, const char* name) {
// Do Nothing on MinGW
}
#endif

View File

@ -23,6 +23,8 @@ void SetCurrentThreadPriority(ThreadPriority new_priority);
void SetCurrentThreadName(const char* name);
void SetThreadName(void* thread, const char* name);
class AccurateTimer {
std::chrono::nanoseconds target_interval{};
std::chrono::nanoseconds total_wait{};

111
src/common/va_ctx.h Normal file
View File

@ -0,0 +1,111 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <xmmintrin.h>
#include "common/types.h"
#define VA_ARGS \
uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, \
uint64_t overflow_arg_area, __m128 xmm0, __m128 xmm1, __m128 xmm2, __m128 xmm3, \
__m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7, ...
#define VA_CTX(ctx) \
alignas(16)::Common::VaCtx ctx{}; \
(ctx).reg_save_area.gp[0] = rdi; \
(ctx).reg_save_area.gp[1] = rsi; \
(ctx).reg_save_area.gp[2] = rdx; \
(ctx).reg_save_area.gp[3] = rcx; \
(ctx).reg_save_area.gp[4] = r8; \
(ctx).reg_save_area.gp[5] = r9; \
(ctx).reg_save_area.fp[0] = xmm0; \
(ctx).reg_save_area.fp[1] = xmm1; \
(ctx).reg_save_area.fp[2] = xmm2; \
(ctx).reg_save_area.fp[3] = xmm3; \
(ctx).reg_save_area.fp[4] = xmm4; \
(ctx).reg_save_area.fp[5] = xmm5; \
(ctx).reg_save_area.fp[6] = xmm6; \
(ctx).reg_save_area.fp[7] = xmm7; \
(ctx).va_list.reg_save_area = &(ctx).reg_save_area; \
(ctx).va_list.gp_offset = offsetof(::Common::VaRegSave, gp); \
(ctx).va_list.fp_offset = offsetof(::Common::VaRegSave, fp); \
(ctx).va_list.overflow_arg_area = &overflow_arg_area;
namespace Common {
// https://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure
struct VaList {
u32 gp_offset;
u32 fp_offset;
void* overflow_arg_area;
void* reg_save_area;
};
struct VaRegSave {
u64 gp[6];
__m128 fp[8];
};
struct VaCtx {
VaRegSave reg_save_area;
VaList va_list;
};
template <class T, uint32_t Size>
T vaArgRegSaveAreaGp(VaList* l) {
auto* addr = reinterpret_cast<T*>(static_cast<u8*>(l->reg_save_area) + l->gp_offset);
l->gp_offset += Size;
return *addr;
}
template <class T, u64 Align, u64 Size>
T vaArgOverflowArgArea(VaList* l) {
auto ptr = ((reinterpret_cast<u64>(l->overflow_arg_area) + (Align - 1)) & ~(Align - 1));
auto* addr = reinterpret_cast<T*>(ptr);
l->overflow_arg_area = reinterpret_cast<void*>(ptr + Size);
return *addr;
}
template <class T, uint32_t Size>
T vaArgRegSaveAreaFp(VaList* l) {
auto* addr = reinterpret_cast<T*>(static_cast<u8*>(l->reg_save_area) + l->fp_offset);
l->fp_offset += Size;
return *addr;
}
inline int vaArgInteger(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<int, 8>(l);
}
return vaArgOverflowArgArea<int, 1, 8>(l);
}
inline long long vaArgLongLong(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<long long, 8>(l);
}
return vaArgOverflowArgArea<long long, 1, 8>(l);
}
inline long vaArgLong(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<long, 8>(l);
}
return vaArgOverflowArgArea<long, 1, 8>(l);
}
inline double vaArgDouble(VaList* l) {
if (l->fp_offset <= 160) {
return vaArgRegSaveAreaFp<double, 16>(l);
}
return vaArgOverflowArgArea<double, 1, 8>(l);
}
template <class T>
T* vaArgPtr(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<T*, 8>(l);
}
return vaArgOverflowArgArea<T*, 1, 8>(l);
}
} // namespace Common

View File

@ -157,7 +157,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
if (is_compute) {
dump.is_compute = true;
const auto& cs = dump.regs.cs_program;
dump.cs_data = ComputerShaderDump{
dump.cs_data = PipelineComputerProgramDump{
.cs_program = cs,
.code = std::vector<u32>{cs.Code().begin(), cs.Code().end()},
};
@ -167,7 +167,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
auto stage = regs.ProgramForStage(i);
if (stage->address_lo != 0) {
auto code = stage->Code();
dump.stages[i] = ShaderDump{
dump.stages[i] = PipelineShaderProgramDump{
.user_data = *stage,
.code = std::vector<u32>{code.begin(), code.end()},
};
@ -176,3 +176,11 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
}
}
}
void DebugStateImpl::CollectShader(const std::string& name, vk::ShaderModule module,
std::span<const u32> spv, std::span<const u32> raw_code,
std::span<const u32> patch_spv, bool is_patched) {
shader_dump_list.emplace_back(name, module, std::vector<u32>{spv.begin(), spv.end()},
std::vector<u32>{raw_code.begin(), raw_code.end()},
std::vector<u32>{patch_spv.begin(), patch_spv.end()}, is_patched);
}

View File

@ -12,7 +12,7 @@
#include "common/types.h"
#include "video_core/amdgpu/liverpool.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
@ -30,7 +30,8 @@ namespace Core::Devtools {
class Layer;
namespace Widget {
class FrameGraph;
}
class ShaderList;
} // namespace Widget
} // namespace Core::Devtools
namespace DebugStateType {
@ -49,12 +50,12 @@ struct QueueDump {
uintptr_t base_addr;
};
struct ShaderDump {
struct PipelineShaderProgramDump {
Vulkan::Liverpool::ShaderProgram user_data{};
std::vector<u32> code{};
};
struct ComputerShaderDump {
struct PipelineComputerProgramDump {
Vulkan::Liverpool::ComputeProgram cs_program{};
std::vector<u32> code{};
};
@ -63,8 +64,8 @@ struct RegDump {
bool is_compute{false};
static constexpr size_t MaxShaderStages = 5;
Vulkan::Liverpool::Regs regs{};
std::array<ShaderDump, MaxShaderStages> stages{};
ComputerShaderDump cs_data{};
std::array<PipelineShaderProgramDump, MaxShaderStages> stages{};
PipelineComputerProgramDump cs_data{};
};
struct FrameDump {
@ -73,9 +74,58 @@ struct FrameDump {
std::unordered_map<uintptr_t, RegDump> regs; // address -> reg dump
};
struct ShaderDump {
std::string name;
vk::ShaderModule module;
std::vector<u32> spv;
std::vector<u32> isa;
std::vector<u32> patch_spv;
std::string patch_source{};
bool loaded_data = false;
bool is_patched = false;
std::string cache_spv_disasm{};
std::string cache_isa_disasm{};
std::string cache_patch_disasm{};
ShaderDump(std::string name, vk::ShaderModule module, std::vector<u32> spv,
std::vector<u32> isa, std::vector<u32> patch_spv, bool is_patched)
: name(std::move(name)), module(module), spv(std::move(spv)), isa(std::move(isa)),
patch_spv(std::move(patch_spv)), is_patched(is_patched) {}
ShaderDump(const ShaderDump& other) = delete;
ShaderDump(ShaderDump&& other) noexcept
: name{std::move(other.name)}, module{std::move(other.module)}, spv{std::move(other.spv)},
isa{std::move(other.isa)}, patch_spv{std::move(other.patch_spv)},
patch_source{std::move(other.patch_source)},
cache_spv_disasm{std::move(other.cache_spv_disasm)},
cache_isa_disasm{std::move(other.cache_isa_disasm)},
cache_patch_disasm{std::move(other.cache_patch_disasm)} {}
ShaderDump& operator=(const ShaderDump& other) = delete;
ShaderDump& operator=(ShaderDump&& other) noexcept {
if (this == &other)
return *this;
name = std::move(other.name);
module = std::move(other.module);
spv = std::move(other.spv);
isa = std::move(other.isa);
patch_spv = std::move(other.patch_spv);
patch_source = std::move(other.patch_source);
cache_spv_disasm = std::move(other.cache_spv_disasm);
cache_isa_disasm = std::move(other.cache_isa_disasm);
cache_patch_disasm = std::move(other.cache_patch_disasm);
return *this;
}
};
class DebugStateImpl {
friend class Core::Devtools::Layer;
friend class Core::Devtools::Widget::FrameGraph;
friend class Core::Devtools::Widget::ShaderList;
std::queue<std::string> debug_message_popup;
std::mutex guest_threads_mutex{};
std::vector<ThreadID> guest_threads{};
@ -94,7 +144,7 @@ class DebugStateImpl {
std::shared_mutex frame_dump_list_mutex;
std::vector<FrameDump> frame_dump_list{};
std::queue<std::string> debug_message_popup;
std::vector<ShaderDump> shader_dump_list{};
public:
void ShowDebugMessage(std::string message) {
@ -152,6 +202,10 @@ public:
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
const AmdGpu::Liverpool::Regs& regs, bool is_compute = false);
void CollectShader(const std::string& name, vk::ShaderModule module, std::span<const u32> spv,
std::span<const u32> raw_code, std::span<const u32> patch_spv,
bool is_patched);
};
} // namespace DebugStateType

View File

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "base_device.h"
namespace Core::Devices {
BaseDevice::BaseDevice() = default;
BaseDevice::~BaseDevice() = default;
} // namespace Core::Devices

View File

@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <core/libraries/kernel/orbis_error.h>
#include "common/types.h"
#include "common/va_ctx.h"
namespace Libraries::Kernel {
struct OrbisKernelStat;
struct SceKernelIovec;
} // namespace Libraries::Kernel
namespace Core::Devices {
class BaseDevice {
public:
explicit BaseDevice();
virtual ~BaseDevice() = 0;
virtual int ioctl(u64 cmd, Common::VaCtx* args) {
return ORBIS_KERNEL_ERROR_ENOTTY;
}
virtual s64 write(const void* buf, size_t nbytes) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 lseek(s64 offset, int whence) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 read(void* buf, size_t nbytes) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual int fstat(Libraries::Kernel::OrbisKernelStat* sb) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s32 fsync() {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual int ftruncate(s64 length) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual int getdents(void* buf, u32 nbytes, s64* basep) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 pwrite(const void* buf, size_t nbytes, u64 offset) {
return ORBIS_KERNEL_ERROR_EBADF;
}
};
} // namespace Core::Devices

67
src/core/devices/ioccom.h Normal file
View File

@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
/*-
* Copyright (c) 1982, 1986, 1990, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ioccom.h 8.2 (Berkeley) 3/28/94
* $FreeBSD$
*/
#define IOCPARM_SHIFT 13 /* number of bits for ioctl size */
#define IOCPARM_MASK ((1 << IOCPARM_SHIFT) - 1) /* parameter length mask */
#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
#define IOCBASECMD(x) ((x) & ~(IOCPARM_MASK << 16))
#define IOCGROUP(x) (((x) >> 8) & 0xff)
#define IOCPARM_MAX (1 << IOCPARM_SHIFT) /* max size of ioctl */
#define IOC_VOID 0x20000000 /* no parameters */
#define IOC_OUT 0x40000000 /* copy out parameters */
#define IOC_IN 0x80000000 /* copy in parameters */
#define IOC_INOUT (IOC_IN | IOC_OUT)
#define IOC_DIRMASK (IOC_VOID | IOC_OUT | IOC_IN)
#define _IOC(inout, group, num, len) \
((unsigned long)((inout) | (((len) & IOCPARM_MASK) << 16) | ((group) << 8) | (num)))
#define _IO(g, n) _IOC(IOC_VOID, (g), (n), 0)
#define _IOWINT(g, n) _IOC(IOC_VOID, (g), (n), sizeof(int))
#define _IOR(g, n, t) _IOC(IOC_OUT, (g), (n), sizeof(t))
#define _IOW(g, n, t) _IOC(IOC_IN, (g), (n), sizeof(t))
/* this should be _IORW, but stdio got there first */
#define _IOWR(g, n, t) _IOC(IOC_INOUT, (g), (n), sizeof(t))
/*
# Simple parse of ioctl cmd
def parse(v):
print('inout', (v >> 24 & 0xFF))
print('len', hex(v >> 16 & 0xFF))
print('group', chr(v >> 8 & 0xFF))
print('num', hex(v & 0xFF))
*/

View File

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/libraries/kernel/file_system.h"
#include "logger.h"
namespace Core::Devices {
Logger::Logger(std::string prefix, bool is_err) : prefix(std::move(prefix)), is_err(is_err) {}
Logger::~Logger() = default;
s64 Logger::write(const void* buf, size_t nbytes) {
log(static_cast<const char*>(buf), nbytes);
return nbytes;
}
size_t Logger::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
for (int i = 0; i < iovcnt; i++) {
log(static_cast<const char*>(iov[i].iov_base), iov[i].iov_len);
}
return iovcnt;
}
s64 Logger::pwrite(const void* buf, size_t nbytes, u64 offset) {
log(static_cast<const char*>(buf), nbytes);
return nbytes;
}
s32 Logger::fsync() {
log_flush();
return 0;
}
void Logger::log(const char* buf, size_t nbytes) {
std::scoped_lock lock{mtx};
const char* end = buf + nbytes;
for (const char* it = buf; it < end; ++it) {
char c = *it;
if (c == '\r') {
continue;
}
if (c == '\n') {
log_flush();
continue;
}
buffer.push_back(c);
}
}
void Logger::log_flush() {
std::scoped_lock lock{mtx};
if (buffer.empty()) {
return;
}
if (is_err) {
LOG_ERROR(Tty, "[{}] {}", prefix, std::string_view{buffer});
} else {
LOG_INFO(Tty, "[{}] {}", prefix, std::string_view{buffer});
}
buffer.clear();
}
} // namespace Core::Devices

37
src/core/devices/logger.h Normal file
View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "base_device.h"
#include <mutex>
#include <string>
#include <vector>
namespace Core::Devices {
class Logger final : BaseDevice {
std::string prefix;
bool is_err;
std::recursive_mutex mtx;
std::vector<char> buffer;
public:
explicit Logger(std::string prefix, bool is_err);
~Logger() override;
s64 write(const void* buf, size_t nbytes) override;
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
s32 fsync() override;
private:
void log(const char* buf, size_t nbytes);
void log_flush();
};
} // namespace Core::Devices

View File

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "base_device.h"
namespace Core::Devices {
class NopDevice final : BaseDevice {
u32 handle;
public:
explicit NopDevice(u32 handle) : handle(handle) {}
~NopDevice() override = default;
int ioctl(u64 cmd, Common::VaCtx* args) override {
return 0;
}
s64 write(const void* buf, size_t nbytes) override {
return 0;
}
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override {
return 0;
}
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override {
return 0;
}
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override {
return 0;
}
s64 lseek(s64 offset, int whence) override {
return 0;
}
s64 read(void* buf, size_t nbytes) override {
return 0;
}
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override {
return 0;
}
s32 fsync() override {
return 0;
}
int ftruncate(s64 length) override {
return 0;
}
int getdents(void* buf, u32 nbytes, s64* basep) override {
return 0;
}
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override {
return 0;
}
};
} // namespace Core::Devices

View File

@ -289,6 +289,16 @@ const char* GetContextRegName(u32 reg_offset) {
return "mmSPI_PS_INPUT_CNTL_2";
case mmSPI_PS_INPUT_CNTL_3:
return "mmSPI_PS_INPUT_CNTL_3";
case mmPA_SU_POLY_OFFSET_FRONT_SCALE:
return "mmPA_SU_POLY_OFFSET_FRONT_SCALE";
case mmPA_SU_POLY_OFFSET_FRONT_OFFSET:
return "mmPA_SU_POLY_OFFSET_FRONT_OFFSET";
case mmPA_SU_POLY_OFFSET_BACK_SCALE:
return "mmPA_SU_POLY_OFFSET_BACK_SCALE";
case mmPA_SU_POLY_OFFSET_BACK_OFFSET:
return "mmPA_SU_POLY_OFFSET_BACK_OFFSET";
case mmPA_SU_POLY_OFFSET_CLAMP:
return "mmPA_SU_POLY_OFFSET_CLAMP";
default:
break;
}

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "layer.h"
#include <imgui.h>
#include "common/config.h"
@ -9,11 +11,12 @@
#include "core/debug_state.h"
#include "imgui/imgui_std.h"
#include "imgui_internal.h"
#include "layer.h"
#include "options.h"
#include "video_core/renderer_vulkan/vk_presenter.h"
#include "widget/frame_dump.h"
#include "widget/frame_graph.h"
#include "widget/memory_map.h"
#include "widget/shader_list.h"
extern std::unique_ptr<Vulkan::Presenter> presenter;
@ -35,6 +38,9 @@ static float debug_popup_timing = 3.0f;
static bool just_opened_options = false;
static Widget::MemoryMapViewer memory_map;
static Widget::ShaderList shader_list;
// clang-format off
static std::string help_text =
#include "help.txt"
@ -63,6 +69,7 @@ void L::DrawMenuBar() {
}
if (BeginMenu("GPU Tools")) {
MenuItem("Show frame info", nullptr, &frame_graph.is_open);
MenuItem("Show loaded shaders", nullptr, &shader_list.open);
if (BeginMenu("Dump frames")) {
SliderInt("Count", &dump_frame_count, 1, 5);
if (MenuItem("Dump", "Ctrl+Alt+F9", nullptr, !DebugState.DumpingCurrentFrame())) {
@ -81,6 +88,12 @@ void L::DrawMenuBar() {
}
ImGui::EndMenu();
}
if (BeginMenu("Debug")) {
if (MenuItem("Memory map")) {
memory_map.open = true;
}
ImGui::EndMenu();
}
EndMainMenuBar();
}
@ -175,19 +188,29 @@ void L::DrawAdvanced() {
bool close_popup_options = true;
if (BeginPopupModal("GPU Tools Options", &close_popup_options,
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) {
static char disassembly_cli[512];
static char disassembler_cli_isa[512];
static char disassembler_cli_spv[512];
static bool frame_dump_render_on_collapse;
if (just_opened_options) {
just_opened_options = false;
auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1);
disassembly_cli[s] = '\0';
auto s = Options.disassembler_cli_isa.copy(disassembler_cli_isa,
sizeof(disassembler_cli_isa) - 1);
disassembler_cli_isa[s] = '\0';
s = Options.disassembler_cli_spv.copy(disassembler_cli_spv,
sizeof(disassembler_cli_spv) - 1);
disassembler_cli_spv[s] = '\0';
frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse;
}
InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli));
InputText("Shader isa disassembler: ", disassembler_cli_isa, sizeof(disassembler_cli_isa));
if (IsItemHovered()) {
SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")");
SetTooltip(R"(Command to disassemble shaders. Example: dis.exe --raw "{src}")");
}
InputText("Shader SPIRV disassembler: ", disassembler_cli_spv,
sizeof(disassembler_cli_spv));
if (IsItemHovered()) {
SetTooltip(R"(Command to disassemble shaders. Example: spirv-cross -V "{src}")");
}
Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse);
if (IsItemHovered()) {
@ -196,7 +219,8 @@ void L::DrawAdvanced() {
}
if (Button("Save")) {
Options.disassembly_cli = disassembly_cli;
Options.disassembler_cli_isa = disassembler_cli_isa;
Options.disassembler_cli_spv = disassembler_cli_spv;
Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse;
SaveIniSettingsToDisk(io.IniFilename);
CloseCurrentPopup();
@ -219,11 +243,18 @@ void L::DrawAdvanced() {
EndPopup();
}
if (memory_map.open) {
memory_map.Draw();
}
if (shader_list.open) {
shader_list.Draw();
}
}
void L::DrawSimple() {
const auto io = GetIO();
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
Text("%.1f FPS (%.2f ms)", io.Framerate, 1000.0f / io.Framerate);
}
static void LoadSettings(const char* line) {
@ -307,6 +338,7 @@ void L::Draw() {
const auto fn = DebugState.flip_frame_count.load();
frame_graph.AddFrame(fn, io.DeltaTime);
}
if (IsKeyPressed(ImGuiKey_F10, false)) {
if (io.KeyCtrl) {
show_advanced_debug = !show_advanced_debug;

View File

@ -12,8 +12,12 @@ TOptions Options;
void LoadOptionsConfig(const char* line) {
char str[512];
int i;
if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) {
Options.disassembly_cli = str;
if (sscanf(line, "disassembler_cli_isa=%511[^\n]", str) == 1) {
Options.disassembler_cli_isa = str;
return;
}
if (sscanf(line, "disassembler_cli_spv=%511[^\n]", str) == 1) {
Options.disassembler_cli_spv = str;
return;
}
if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) {
@ -23,7 +27,8 @@ void LoadOptionsConfig(const char* line) {
}
void SerializeOptionsConfig(ImGuiTextBuffer* buf) {
buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str());
buf->appendf("disassembler_cli_isa=%s\n", Options.disassembler_cli_isa.c_str());
buf->appendf("disassembler_cli_spv=%s\n", Options.disassembler_cli_spv.c_str());
buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse);
}

View File

@ -10,7 +10,8 @@ struct ImGuiTextBuffer;
namespace Core::Devtools {
struct TOptions {
std::string disassembly_cli{};
std::string disassembler_cli_isa{"clrxdisasm --raw {src}"};
std::string disassembler_cli_spv{"spirv-cross -V {src}"};
bool frame_dump_render_on_collapse{false};
};

View File

@ -1306,7 +1306,7 @@ void CmdListViewer::Draw(bool only_batches_view) {
if (batch.id == batch_bp) { // highlight batch at breakpoint
PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f});
}
if (batch.id == highlight_batch) {
if (batch.id == highlight_batch && !group_batches) {
PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
}
@ -1459,7 +1459,7 @@ void CmdListViewer::Draw(bool only_batches_view) {
}
}
if (batch.id == highlight_batch) {
if (batch.id == highlight_batch && !group_batches) {
PopStyleColor();
}

View File

@ -3,16 +3,24 @@
#pragma once
#include <filesystem>
#include <string>
#include <type_traits>
#include <variant>
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
#include "common/bit_field.h"
#include "common/io_file.h"
#include "common/types.h"
#include "core/debug_state.h"
#include "video_core/amdgpu/pm4_opcodes.h"
#if defined(_WIN32)
#define popen _popen
#define pclose _pclose
#endif
namespace Core::Devtools::Widget {
/*
* Generic PM4 header
@ -106,4 +114,66 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
}
}
inline std::optional<std::string> exec_cli(const char* cli) {
std::array<char, 64> buffer{};
std::string output;
const auto f = popen(cli, "rt");
if (!f) {
pclose(f);
return {};
}
while (fgets(buffer.data(), buffer.size(), f)) {
output += buffer.data();
}
pclose(f);
return output;
}
template <typename T>
inline std::string RunDisassembler(const std::string& disassembler_cli, const T& shader_code,
bool* success = nullptr) {
std::string shader_dis;
if (disassembler_cli.empty()) {
shader_dis = "No disassembler set";
if (success) {
*success = false;
}
} else {
auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin";
constexpr std::string_view src_arg = "{src}";
std::string cli = disassembler_cli + " 2>&1";
const auto pos = cli.find(src_arg);
if (pos == std::string::npos) {
shader_dis = "Disassembler CLI does not contain {src} argument";
if (success) {
*success = false;
}
} else {
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
file.Write(shader_code);
file.Close();
auto result = exec_cli(cli.c_str());
if (result) {
shader_dis = result.value();
if (success) {
*success = true;
}
} else {
if (success) {
*success = false;
}
shader_dis = "Could not disassemble shader";
}
std::filesystem::remove(bin_path);
}
}
return shader_dis;
}
} // namespace Core::Devtools::Widget

View File

@ -4,7 +4,7 @@
#include <cstdio>
#include <fmt/chrono.h>
#include <imgui.h>
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
#include "common/io_file.h"
#include "core/devtools/options.h"

View File

@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <imgui.h>
#include <magic_enum/magic_enum.hpp>
#include "core/debug_state.h"
#include "core/memory.h"
#include "memory_map.h"
using namespace ImGui;
namespace Core::Devtools::Widget {
bool MemoryMapViewer::Iterator::DrawLine() {
if (is_vma) {
if (vma.it == vma.end) {
return false;
}
auto m = vma.it->second;
if (m.type == VMAType::Free) {
++vma.it;
return DrawLine();
}
TableNextColumn();
Text("%" PRIXPTR, m.base);
TableNextColumn();
Text("%zX", m.size);
TableNextColumn();
Text("%s", magic_enum::enum_name(m.type).data());
TableNextColumn();
Text("%s", magic_enum::enum_name(m.prot).data());
TableNextColumn();
if (m.is_exec) {
Text("X");
}
TableNextColumn();
Text("%s", m.name.c_str());
++vma.it;
return true;
}
if (dmem.it == dmem.end) {
return false;
}
auto m = dmem.it->second;
if (m.is_free) {
++dmem.it;
return DrawLine();
}
TableNextColumn();
Text("%" PRIXPTR, m.base);
TableNextColumn();
Text("%zX", m.size);
TableNextColumn();
auto type = static_cast<::Libraries::Kernel::MemoryTypes>(m.memory_type);
Text("%s", magic_enum::enum_name(type).data());
TableNextColumn();
Text("%d", m.is_pooled);
++dmem.it;
return true;
}
void MemoryMapViewer::Draw() {
SetNextWindowSize({600.0f, 500.0f}, ImGuiCond_FirstUseEver);
if (!Begin("Memory map", &open)) {
End();
return;
}
auto mem = Memory::Instance();
std::scoped_lock lck{mem->mutex};
{
bool next_showing_vma = showing_vma;
if (showing_vma) {
PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
}
if (Button("VMem")) {
next_showing_vma = true;
}
if (showing_vma) {
PopStyleColor();
}
SameLine();
if (!showing_vma) {
PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
}
if (Button("DMem")) {
next_showing_vma = false;
}
if (!showing_vma) {
PopStyleColor();
}
showing_vma = next_showing_vma;
}
Iterator it{};
if (showing_vma) {
it.is_vma = true;
it.vma.it = mem->vma_map.begin();
it.vma.end = mem->vma_map.end();
} else {
it.is_vma = false;
it.dmem.it = mem->dmem_map.begin();
it.dmem.end = mem->dmem_map.end();
}
if (BeginTable("memory_view_table", showing_vma ? 6 : 4,
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg |
ImGuiTableFlags_SizingFixedFit)) {
if (showing_vma) {
TableSetupColumn("Address");
TableSetupColumn("Size");
TableSetupColumn("Type");
TableSetupColumn("Prot");
TableSetupColumn("Is Exec");
TableSetupColumn("Name");
} else {
TableSetupColumn("Address");
TableSetupColumn("Size");
TableSetupColumn("Type");
TableSetupColumn("Pooled");
}
TableHeadersRow();
while (it.DrawLine())
;
EndTable();
}
End();
}
} // namespace Core::Devtools::Widget

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/memory.h"
namespace Core::Devtools::Widget {
class MemoryMapViewer {
struct Iterator {
bool is_vma;
struct {
MemoryManager::DMemMap::iterator it;
MemoryManager::DMemMap::iterator end;
} dmem;
struct {
MemoryManager::VMAMap::iterator it;
MemoryManager::VMAMap::iterator end;
} vma;
bool DrawLine();
};
bool showing_vma = true;
public:
bool open = false;
void Draw();
};
} // namespace Core::Devtools::Widget

View File

@ -5,7 +5,7 @@
#include <cstdio>
#include <imgui.h>
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
#include "cmd_list.h"
#include "common.h"

View File

@ -5,7 +5,7 @@
#include <optional>
#include <string>
#include <imgui.h>
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
#include <stdio.h>
#include "common.h"
@ -25,21 +25,6 @@ using magic_enum::enum_name;
constexpr auto depth_id = 0xF3;
static std::optional<std::string> exec_cli(const char* cli) {
std::array<char, 64> buffer{};
std::string output;
const auto f = popen(cli, "r");
if (!f) {
pclose(f);
return {};
}
while (fgets(buffer.data(), buffer.size(), f)) {
output += buffer.data();
}
pclose(f);
return output;
}
namespace Core::Devtools::Widget {
void RegView::ProcessShader(int shader_id) {
@ -54,38 +39,12 @@ void RegView::ProcessShader(int shader_id) {
user_data = s.user_data.user_data;
}
std::string shader_dis;
if (Options.disassembly_cli.empty()) {
shader_dis = "No disassembler set";
} else {
auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin";
constexpr std::string_view src_arg = "{src}";
std::string cli = Options.disassembly_cli;
const auto pos = cli.find(src_arg);
if (pos == std::string::npos) {
DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument");
} else {
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
file.Write(shader_code);
file.Close();
auto result = exec_cli(cli.c_str());
shader_dis = result.value_or("Could not disassemble shader");
if (shader_dis.empty()) {
shader_dis = "Disassembly empty or failed";
}
std::filesystem::remove(bin_path);
}
}
std::string shader_dis = RunDisassembler(Options.disassembler_cli_isa, shader_code);
MemoryEditor hex_view;
hex_view.Open = true;
hex_view.ReadOnly = true;
hex_view.Cols = 8;
hex_view.Cols = 16;
hex_view.OptShowAscii = false;
hex_view.OptShowOptions = false;
@ -376,7 +335,9 @@ void RegView::Draw() {
if (!shader) {
Text("Stage not selected");
} else {
shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size());
shader->hex_view.DrawContents(shader->user_data.data(),
shader->user_data.size() *
sizeof(Vulkan::Liverpool::UserData::value_type));
}
}
End();

View File

@ -0,0 +1,251 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include "shader_list.h"
#include <imgui.h>
#include "common.h"
#include "common/config.h"
#include "common/path_util.h"
#include "common/string_util.h"
#include "core/debug_state.h"
#include "core/devtools/options.h"
#include "imgui/imgui_std.h"
#include "sdl_window.h"
#include "video_core/renderer_vulkan/vk_presenter.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
extern std::unique_ptr<Vulkan::Presenter> presenter;
using namespace ImGui;
namespace Core::Devtools::Widget {
ShaderList::Selection::Selection(int index) : index(index) {
isa_editor.SetPalette(TextEditor::GetDarkPalette());
isa_editor.SetReadOnly(true);
glsl_editor.SetPalette(TextEditor::GetDarkPalette());
glsl_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
presenter->GetWindow().RequestKeyboard();
}
ShaderList::Selection::~Selection() {
presenter->GetWindow().ReleaseKeyboard();
}
void ShaderList::Selection::ReloadShader(DebugStateType::ShaderDump& value) {
auto& spv = value.is_patched ? value.patch_spv : value.spv;
if (spv.empty()) {
return;
}
auto& cache = presenter->GetRasterizer().GetPipelineCache();
if (const auto m = cache.ReplaceShader(value.module, spv); m) {
value.module = *m;
}
}
bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
if (!value.loaded_data) {
value.loaded_data = true;
if (value.cache_isa_disasm.empty()) {
value.cache_isa_disasm = RunDisassembler(Options.disassembler_cli_isa, value.isa);
}
if (value.cache_spv_disasm.empty()) {
value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv);
}
if (!value.patch_spv.empty() && value.cache_patch_disasm.empty()) {
value.cache_patch_disasm = RunDisassembler("spirv-dis {src}", value.patch_spv);
}
patch_path =
Common::FS::GetUserPath(Common::FS::PathType::ShaderDir) / "patch" / value.name;
patch_bin_path = patch_path;
patch_bin_path += ".spv";
patch_path += ".glsl";
if (std::filesystem::exists(patch_path)) {
std::ifstream file{patch_path};
value.patch_source =
std::string{std::istreambuf_iterator{file}, std::istreambuf_iterator<char>{}};
}
value.is_patched = !value.patch_spv.empty();
if (!value.is_patched) { // No patch
isa_editor.SetText(value.cache_isa_disasm);
glsl_editor.SetText(value.cache_spv_disasm);
} else {
isa_editor.SetText(value.cache_patch_disasm);
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
glsl_editor.SetText(value.patch_source);
glsl_editor.SetReadOnly(false);
}
}
char name[64];
snprintf(name, sizeof(name), "Shader %s", value.name.c_str());
SetNextWindowSize({450.0f, 600.0f}, ImGuiCond_FirstUseEver);
if (!Begin(name, &open, ImGuiWindowFlags_NoNav)) {
End();
return open;
}
Text("%s", value.name.c_str());
SameLine(0.0f, 7.0f);
if (Checkbox("Enable patch", &value.is_patched)) {
if (value.is_patched) {
if (value.patch_source.empty()) {
value.patch_source = value.cache_spv_disasm;
}
isa_editor.SetText(value.cache_patch_disasm);
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
glsl_editor.SetText(value.patch_source);
glsl_editor.SetReadOnly(false);
if (!value.patch_spv.empty()) {
ReloadShader(value);
}
} else {
isa_editor.SetText(value.cache_isa_disasm);
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition());
glsl_editor.SetText(value.cache_spv_disasm);
glsl_editor.SetReadOnly(true);
ReloadShader(value);
}
}
if (value.is_patched) {
if (BeginCombo("Shader type", showing_bin ? "SPIRV" : "GLSL",
ImGuiComboFlags_WidthFitPreview)) {
if (Selectable("GLSL")) {
showing_bin = false;
}
if (Selectable("SPIRV")) {
showing_bin = true;
}
EndCombo();
}
} else {
if (BeginCombo("Shader type", showing_bin ? "ISA" : "GLSL",
ImGuiComboFlags_WidthFitPreview)) {
if (Selectable("GLSL")) {
showing_bin = false;
}
if (Selectable("ISA")) {
showing_bin = true;
}
EndCombo();
}
}
if (value.is_patched) {
bool save = false;
bool compile = false;
SameLine(0.0f, 3.0f);
if (Button("Save")) {
save = true;
}
SameLine();
if (Button("Save & Compile")) {
save = true;
compile = true;
}
if (save) {
value.patch_source = glsl_editor.GetText();
std::ofstream file{patch_path, std::ios::binary | std::ios::trunc};
file << value.patch_source;
std::string msg = "Patch saved to ";
msg += Common::U8stringToString(patch_path.u8string());
DebugState.ShowDebugMessage(msg);
}
if (compile) {
static std::map<std::string, std::string> stage_arg = {
{"vs", "vert"},
{"gs", "geom"},
{"fs", "frag"},
{"cs", "comp"},
};
auto stage = stage_arg.find(value.name.substr(0, 2));
if (stage == stage_arg.end()) {
DebugState.ShowDebugMessage(std::string{"Invalid shader stage: "} +
value.name.substr(0, 2));
} else {
std::string cmd =
fmt::format("glslc --target-env=vulkan1.3 --target-spv=spv1.6 "
"-fshader-stage={} {{src}} -o \"{}\"",
stage->second, Common::U8stringToString(patch_bin_path.u8string()));
bool success = false;
auto res = RunDisassembler(cmd, value.patch_source, &success);
if (!res.empty() || !success) {
DebugState.ShowDebugMessage("Compilation failed:\n" + res);
} else {
Common::FS::IOFile file{patch_bin_path, Common::FS::FileAccessMode::Read};
value.patch_spv.resize(file.GetSize() / sizeof(u32));
file.Read(value.patch_spv);
value.cache_patch_disasm =
RunDisassembler("spirv-dis {src}", value.patch_spv, &success);
if (!success) {
DebugState.ShowDebugMessage("Decompilation failed (Compile was ok):\n" +
res);
} else {
isa_editor.SetText(value.cache_patch_disasm);
ReloadShader(value);
}
}
}
}
}
if (showing_bin) {
isa_editor.Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail());
} else {
glsl_editor.Render("GLSL", GetContentRegionAvail());
}
End();
return open;
}
void ShaderList::Draw() {
for (auto it = open_shaders.begin(); it != open_shaders.end();) {
auto& selection = *it;
auto& shader = DebugState.shader_dump_list[selection.index];
if (!selection.DrawShader(shader)) {
it = open_shaders.erase(it);
} else {
++it;
}
}
SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver);
if (!Begin("Shader list", &open)) {
End();
return;
}
if (!Config::collectShadersForDebug()) {
DrawCenteredText("Enable 'CollectShader' in config to see shaders");
End();
return;
}
auto width = GetContentRegionAvail().x;
int i = 0;
for (const auto& shader : DebugState.shader_dump_list) {
char name[128];
if (shader.is_patched) {
snprintf(name, sizeof(name), "%s (PATCH ON)", shader.name.c_str());
} else if (!shader.patch_spv.empty()) {
snprintf(name, sizeof(name), "%s (PATCH OFF)", shader.name.c_str());
} else {
snprintf(name, sizeof(name), "%s", shader.name.c_str());
}
if (ButtonEx(name, {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) {
open_shaders.emplace_back(i);
}
i++;
}
End();
}
} // namespace Core::Devtools::Widget

View File

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/debug_state.h"
#include "text_editor.h"
#include <filesystem>
namespace Core::Devtools::Widget {
class ShaderList {
struct Selection {
explicit Selection(int index);
~Selection();
void ReloadShader(DebugStateType::ShaderDump& value);
bool DrawShader(DebugStateType::ShaderDump& value);
int index;
TextEditor isa_editor{};
TextEditor glsl_editor{};
bool open = true;
bool showing_bin = false;
std::filesystem::path patch_path;
std::filesystem::path patch_bin_path;
};
std::vector<Selection> open_shaders{};
public:
bool open = false;
void Draw();
};
} // namespace Core::Devtools::Widget

View File

@ -1059,7 +1059,8 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) {
if (!mIgnoreImGuiChild)
ImGui::BeginChild(aTitle, aSize, aBorder,
ImGuiWindowFlags_HorizontalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove);
ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoNav);
if (mHandleKeyboardInputs) {
HandleKeyboardInputs();
@ -2331,4 +2332,50 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL() {
return langDef;
}
// Source: https://github.com/dfranx/ImGuiColorTextEdit/blob/master/TextEditor.cpp
const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::SPIRV() {
static bool inited = false;
static LanguageDefinition langDef;
if (!inited) {
/*
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[
\\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string,
PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string,
PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string,
PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]",
PaletteIndex::Punctuation));
*/
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
"L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
langDef.mTokenRegexStrings.push_back(
std::make_pair<std::string, PaletteIndex>("[ =\\t]Op[a-zA-Z]*", PaletteIndex::Keyword));
langDef.mTokenRegexStrings.push_back(
std::make_pair<std::string, PaletteIndex>("%[_a-zA-Z0-9]*", PaletteIndex::Identifier));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
"[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
"[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
"0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
"0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
langDef.mCommentStart = "/*";
langDef.mCommentEnd = "*/";
langDef.mSingleLineComment = ";";
langDef.mCaseSensitive = true;
langDef.mAutoIndentation = false;
langDef.mName = "SPIR-V";
inited = true;
}
return langDef;
}
} // namespace Core::Devtools::Widget

View File

@ -161,6 +161,7 @@ public:
: mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) {}
static const LanguageDefinition& GLSL();
static const LanguageDefinition& SPIRV();
};
TextEditor();

View File

@ -5,13 +5,9 @@
#include "common/assert.h"
#include "common/io_file.h"
#include "common/stb.h"
#include "splash.h"
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#define STBI_NO_STDIO
#include "externals/stb_image.h"
bool Splash::Open(const std::filesystem::path& filepath) {
ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed");

View File

@ -4,12 +4,12 @@
#include <algorithm>
#include "common/config.h"
#include "common/string_util.h"
#include "core/devices/logger.h"
#include "core/devices/nop_device.h"
#include "core/file_sys/fs.h"
namespace Core::FileSys {
constexpr int RESERVED_HANDLES = 3; // First 3 handles are stdin,stdout,stderr
void MntPoints::Mount(const std::filesystem::path& host_folder, const std::string& guest_folder,
bool read_only) {
std::scoped_lock lock{m_mutex};
@ -135,7 +135,6 @@ int HandleTable::CreateHandle() {
std::scoped_lock lock{m_mutex};
auto* file = new File{};
file->is_directory = false;
file->is_opened = false;
int existingFilesNum = m_files.size();
@ -143,23 +142,23 @@ int HandleTable::CreateHandle() {
for (int index = 0; index < existingFilesNum; index++) {
if (m_files.at(index) == nullptr) {
m_files[index] = file;
return index + RESERVED_HANDLES;
return index;
}
}
m_files.push_back(file);
return m_files.size() + RESERVED_HANDLES - 1;
return m_files.size() - 1;
}
void HandleTable::DeleteHandle(int d) {
std::scoped_lock lock{m_mutex};
delete m_files.at(d - RESERVED_HANDLES);
m_files[d - RESERVED_HANDLES] = nullptr;
delete m_files.at(d);
m_files[d] = nullptr;
}
File* HandleTable::GetFile(int d) {
std::scoped_lock lock{m_mutex};
return m_files.at(d - RESERVED_HANDLES);
return m_files.at(d);
}
File* HandleTable::GetFile(const std::filesystem::path& host_name) {
@ -171,4 +170,20 @@ File* HandleTable::GetFile(const std::filesystem::path& host_name) {
return nullptr;
}
void HandleTable::CreateStdHandles() {
auto setup = [this](const char* path, auto* device) {
int fd = CreateHandle();
auto* file = GetFile(fd);
file->is_opened = true;
file->type = FileType::Device;
file->m_guest_name = path;
file->device =
std::shared_ptr<Devices::BaseDevice>{reinterpret_cast<Devices::BaseDevice*>(device)};
};
// order matters
setup("/dev/stdin", new Devices::NopDevice(0)); // stdin
setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout
setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr
}
} // namespace Core::FileSys

View File

@ -9,6 +9,7 @@
#include <vector>
#include <tsl/robin_map.h>
#include "common/io_file.h"
#include "core/devices/base_device.h"
namespace Core::FileSys {
@ -55,15 +56,22 @@ struct DirEntry {
bool isFile;
};
enum class FileType {
Regular, // standard file
Directory,
Device,
};
struct File {
std::atomic_bool is_opened{};
std::atomic_bool is_directory{};
std::atomic<FileType> type{FileType::Regular};
std::filesystem::path m_host_name;
std::string m_guest_name;
Common::FS::IOFile f;
std::vector<DirEntry> dirents;
u32 dirents_index;
std::mutex m_mutex;
std::shared_ptr<Devices::BaseDevice> device; // only valid for type == Device
};
class HandleTable {
@ -76,6 +84,8 @@ public:
File* GetFile(int d);
File* GetFile(const std::filesystem::path& host_name);
void CreateStdHandles();
private:
std::vector<File*> m_files;
std::mutex m_mutex;

View File

@ -9,7 +9,7 @@
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
namespace Libraries::Ajm {
@ -19,7 +19,7 @@ constexpr int ORBIS_AJM_CHANNELMASK_QUAD = 0x0033;
constexpr int ORBIS_AJM_CHANNELMASK_5POINT1 = 0x060F;
constexpr int ORBIS_AJM_CHANNELMASK_7POINT1 = 0x063F;
static std::unique_ptr<AjmContext> context{};
static std::unordered_map<u32, std::unique_ptr<AjmContext>> contexts{};
u32 GetChannelMask(u32 num_channels) {
switch (num_channels) {
@ -40,7 +40,13 @@ u32 GetChannelMask(u32 num_channels) {
int PS4_SYSV_ABI sceAjmBatchCancel(const u32 context_id, const u32 batch_id) {
LOG_INFO(Lib_Ajm, "called context_id = {} batch_id = {}", context_id, batch_id);
return context->BatchCancel(batch_id);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->BatchCancel(batch_id);
}
int PS4_SYSV_ABI sceAjmBatchErrorDump() {
@ -90,14 +96,26 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context_id, u8* p_batch, u32 batch_s
u32* out_batch_id) {
LOG_TRACE(Lib_Ajm, "called context = {}, batch_size = {:#x}, priority = {}", context_id,
batch_size, priority);
return context->BatchStartBuffer(p_batch, batch_size, priority, batch_error, out_batch_id);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->BatchStartBuffer(p_batch, batch_size, priority, batch_error, out_batch_id);
}
int PS4_SYSV_ABI sceAjmBatchWait(const u32 context_id, const u32 batch_id, const u32 timeout,
AjmBatchError* const batch_error) {
LOG_TRACE(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context_id, batch_id,
timeout);
return context->BatchWait(batch_id, timeout, batch_error);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->BatchWait(batch_id, timeout, batch_error);
}
int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() {
@ -117,12 +135,12 @@ int PS4_SYSV_ABI sceAjmFinalize() {
int PS4_SYSV_ABI sceAjmInitialize(s64 reserved, u32* p_context_id) {
LOG_INFO(Lib_Ajm, "called reserved = {}", reserved);
ASSERT_MSG(context == nullptr, "Multiple contexts are currently unsupported.");
if (p_context_id == nullptr || reserved != 0) {
return ORBIS_AJM_ERROR_INVALID_PARAMETER;
}
*p_context_id = 1;
context = std::make_unique<AjmContext>();
u32 id = contexts.size() + 1;
*p_context_id = id;
contexts.emplace(id, std::make_unique<AjmContext>());
return ORBIS_OK;
}
@ -135,12 +153,24 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context_id, AjmCodecType codec_type,
AjmInstanceFlags flags, u32* out_instance) {
LOG_INFO(Lib_Ajm, "called context = {}, codec_type = {}, flags = {:#x}", context_id,
magic_enum::enum_name(codec_type), flags.raw);
return context->InstanceCreate(codec_type, flags, out_instance);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->InstanceCreate(codec_type, flags, out_instance);
}
int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context_id, u32 instance_id) {
LOG_INFO(Lib_Ajm, "called context = {}, instance = {}", context_id, instance_id);
return context->InstanceDestroy(instance_id);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->InstanceDestroy(instance_id);
}
int PS4_SYSV_ABI sceAjmInstanceExtend() {
@ -168,7 +198,13 @@ int PS4_SYSV_ABI sceAjmModuleRegister(u32 context_id, AjmCodecType codec_type, s
if (reserved != 0) {
return ORBIS_AJM_ERROR_INVALID_PARAMETER;
}
return context->ModuleRegister(codec_type);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->ModuleRegister(codec_type);
}
int PS4_SYSV_ABI sceAjmModuleUnregister() {

View File

@ -3,6 +3,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/thread.h"
#include "core/libraries/ajm/ajm.h"
#include "core/libraries/ajm/ajm_at9.h"
#include "core/libraries/ajm/ajm_context.h"
@ -53,6 +54,7 @@ s32 AjmContext::ModuleRegister(AjmCodecType type) {
}
void AjmContext::WorkerThread(std::stop_token stop) {
Common::SetCurrentThreadName("shadPS4:AjmWorker");
while (!stop.stop_requested()) {
auto batch = batch_queue.PopWait(stop);
if (batch != nullptr) {

View File

@ -5,7 +5,7 @@
#include "core/libraries/ajm/ajm_instance.h"
#include "core/libraries/ajm/ajm_mp3.h"
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
namespace Libraries::Ajm {

View File

@ -12,6 +12,8 @@ extern "C" {
#include <libswresample/swresample.h>
}
#include "common/support/avdec.h"
namespace Libraries::Ajm {
// Following tables have been reversed from AJM library

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
#include "common/assert.h"
#include "common/logging/log.h"

View File

@ -80,7 +80,7 @@ int PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported(OrbisAudio3dPortId uiPortI
int PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId uiPortId, u32* pQueueLevel,
u32* pQueueAvailable) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> // std::max, std::min
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
#include "core/libraries/avplayer/avplayer_file_streamer.h"
extern "C" {

View File

@ -8,7 +8,7 @@
#include "core/libraries/avplayer/avplayer_file_streamer.h"
#include "core/libraries/avplayer/avplayer_source.h"
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
extern "C" {
#include <libavcodec/avcodec.h>
@ -18,16 +18,7 @@ extern "C" {
#include <libswscale/swscale.h>
}
// The av_err2str macro in libavutil/error.h does not play nice with C++
#ifdef av_err2str
#undef av_err2str
#include <string>
av_always_inline std::string av_err2string(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum);
}
#define av_err2str(err) av_err2string(err).c_str()
#endif // av_err2str
#include "common/support/avdec.h"
namespace Libraries::AvPlayer {

View File

@ -8,7 +8,7 @@
#include "core/libraries/avplayer/avplayer_state.h"
#include "core/tls.h"
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
namespace Libraries::AvPlayer {

View File

@ -544,15 +544,15 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) {
.base_addr = base_addr,
});
}
liverpool->SubmitAsc(vqid, acb_span);
liverpool->SubmitAsc(gnm_vqid, acb_span);
*asc_queue.read_addr += acb_size;
*asc_queue.read_addr %= asc_queue.ring_size_dw * 4;
}
int PS4_SYSV_ABI sceGnmDingDongForWorkload() {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
return ORBIS_OK;
void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id) {
LOG_DEBUG(Lib_GnmDriver, "called, redirecting to sceGnmDingDong");
sceGnmDingDong(gnm_vqid, next_offs_dw);
}
int PS4_SYSV_ABI sceGnmDisableMipStatsReport() {
@ -971,7 +971,7 @@ s32 PS4_SYSV_ABI sceGnmFindResourcesPublic() {
}
void PS4_SYSV_ABI sceGnmFlushGarlic() {
LOG_WARNING(Lib_GnmDriver, "(STUBBED) called");
LOG_TRACE(Lib_GnmDriver, "(STUBBED) called");
}
int PS4_SYSV_ABI sceGnmGetCoredumpAddress() {

View File

@ -34,7 +34,7 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus();
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id);
int PS4_SYSV_ABI sceGnmDestroyWorkloadStream();
void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw);
int PS4_SYSV_ABI sceGnmDingDongForWorkload();
void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id);
int PS4_SYSV_ABI sceGnmDisableMipStatsReport();
s32 PS4_SYSV_ABI sceGnmDispatchDirect(u32* cmdbuf, u32 size, u32 threads_x, u32 threads_y,
u32 threads_z, u32 flags);

View File

@ -3,7 +3,7 @@
#include <utility>
#include <imgui.h>
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
#include "common/assert.h"
#include "common/logging/log.h"

View File

@ -44,10 +44,14 @@ public:
openEvent.param.rect.y = m_param.ime.posy;
} else {
openEvent.param.resource_id_array.userId = 1;
openEvent.param.resource_id_array.resource_id[0] = 1;
openEvent.param.resource_id_array.resourceId[0] = 1;
}
Execute(nullptr, &openEvent, true);
// Are we supposed to call the event handler on init with
// ADD_OSK?
if (!ime_mode && False(m_param.key.option & OrbisImeKeyboardOption::AddOsk)) {
Execute(nullptr, &openEvent, true);
}
if (ime_mode) {
g_ime_state = ImeState(&m_param.ime);
@ -56,6 +60,11 @@ public:
}
s32 Update(OrbisImeEventHandler handler) {
if (!m_ime_mode) {
/* We don't handle any events for ImeKeyboard */
return ORBIS_OK;
}
std::unique_lock lock{g_ime_state.queue_mutex};
while (!g_ime_state.event_queue.empty()) {
@ -85,6 +94,16 @@ public:
}
}
s32 SetText(const char16_t* text, u32 length) {
g_ime_state.SetText(text, length);
return ORBIS_OK;
}
s32 SetCaret(const OrbisImeCaret* caret) {
g_ime_state.SetCaret(caret->index);
return ORBIS_OK;
}
bool IsIme() {
return m_ime_mode;
}
@ -98,6 +117,7 @@ private:
};
static std::unique_ptr<ImeHandler> g_ime_handler;
static std::unique_ptr<ImeHandler> g_keyboard_handler;
int PS4_SYSV_ABI FinalizeImeModule() {
LOG_ERROR(Lib_Ime, "(STUBBED) called");
@ -130,9 +150,6 @@ s32 PS4_SYSV_ABI sceImeClose() {
if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
if (!g_ime_handler->IsIme()) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
g_ime_handler.release();
g_ime_ui = ImeUi();
@ -233,14 +250,11 @@ s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32*
s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) {
LOG_INFO(Lib_Ime, "(STUBBED) called");
if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
if (g_ime_handler->IsIme()) {
if (!g_keyboard_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
g_ime_handler.release();
g_keyboard_handler.release();
return ORBIS_OK;
}
@ -255,18 +269,17 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() {
}
s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) {
LOG_ERROR(Lib_Ime, "(STUBBED) called");
LOG_INFO(Lib_Ime, "called");
if (!param) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
}
if (g_ime_handler) {
if (g_keyboard_handler) {
return ORBIS_IME_ERROR_BUSY;
}
// g_ime_handler = std::make_unique<ImeHandler>(param);
// return ORBIS_OK;
return ORBIS_IME_ERROR_CONNECTION_FAILED; // Fixup
g_keyboard_handler = std::make_unique<ImeHandler>(param);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceImeKeyboardOpenInternal() {
@ -287,16 +300,14 @@ int PS4_SYSV_ABI sceImeKeyboardUpdate() {
s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended) {
LOG_INFO(Lib_Ime, "called");
if (!g_ime_handler) {
g_ime_handler = std::make_unique<ImeHandler>(param);
} else {
if (g_ime_handler->IsIme()) {
return ORBIS_IME_ERROR_BUSY;
}
g_ime_handler->Init((void*)param, true);
if (!param) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
}
if (g_ime_handler) {
return ORBIS_IME_ERROR_BUSY;
}
g_ime_handler = std::make_unique<ImeHandler>(param);
return ORBIS_OK;
}
@ -322,13 +333,29 @@ int PS4_SYSV_ABI sceImeSetCandidateIndex() {
}
int PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) {
LOG_ERROR(Lib_Ime, "(STUBBED) called");
return ORBIS_OK;
LOG_TRACE(Lib_Ime, "called");
if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
if (!caret) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
}
return g_ime_handler->SetCaret(caret);
}
int PS4_SYSV_ABI sceImeSetText() {
LOG_ERROR(Lib_Ime, "(STUBBED) called");
return ORBIS_OK;
s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length) {
LOG_TRACE(Lib_Ime, "called");
if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
if (!text) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
}
return g_ime_handler->SetText(text, length);
}
int PS4_SYSV_ABI sceImeSetTextGeometry() {
@ -337,13 +364,19 @@ int PS4_SYSV_ABI sceImeSetTextGeometry() {
}
s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
LOG_TRACE(Lib_Ime, "called");
if (g_ime_handler) {
g_ime_handler->Update(handler);
}
if (!g_ime_handler) {
if (g_keyboard_handler) {
g_keyboard_handler->Update(handler);
}
if (!g_ime_handler || !g_keyboard_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
return g_ime_handler->Update(handler);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceImeVshClearPreedit() {

View File

@ -26,6 +26,24 @@ enum class OrbisImeKeyboardOption : u32 {
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption)
enum class OrbisImeOption : u32 {
DEFAULT = 0,
MULTILINE = 1,
NO_AUTO_CAPITALIZATION = 2,
PASSWORD = 4,
LANGUAGES_FORCED = 8,
EXT_KEYBOARD = 16,
NO_LEARNING = 32,
FIXED_POSITION = 64,
DISABLE_RESUME = 256,
DISABLE_AUTO_SPACE = 512,
DISABLE_POSITION_ADJUSTMENT = 2048,
EXPANDED_PREEDIT_BUFFER = 4096,
USE_JAPANESE_EISUU_KEY_AS_CAPSLOCK = 8192,
USE_2K_COORDINATES = 16384,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeOption)
struct OrbisImeKeyboardParam {
OrbisImeKeyboardOption option;
s8 reserved1[4];
@ -41,9 +59,9 @@ struct OrbisImeParam {
OrbisImeEnterLabel enter_label;
OrbisImeInputMethod input_method;
OrbisImeTextFilter filter;
u32 option;
u32 max_text_length;
char16_t* input_text_buffer;
OrbisImeOption option;
u32 maxTextLength;
char16_t* inputTextBuffer;
float posx;
float posy;
OrbisImeHorizontalAlignment horizontal_alignment;
@ -93,7 +111,7 @@ int PS4_SYSV_ABI sceImeOpenInternal();
void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param);
int PS4_SYSV_ABI sceImeSetCandidateIndex();
s32 PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret);
int PS4_SYSV_ABI sceImeSetText();
s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length);
int PS4_SYSV_ABI sceImeSetTextGeometry();
s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler);
int PS4_SYSV_ABI sceImeVshClearPreedit();

View File

@ -142,7 +142,7 @@ struct OrbisImeKeycode {
struct OrbisImeKeyboardResourceIdArray {
s32 userId;
u32 resource_id[6];
u32 resourceId[5];
};
enum class OrbisImeCaretMovementDirection : u32 {

View File

@ -2,7 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"

View File

@ -4,7 +4,7 @@
#include <cwchar>
#include <string>
#include <imgui.h>
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
#include "common/assert.h"
#include "common/logging/log.h"

View File

@ -16,7 +16,7 @@ ImeState::ImeState(const OrbisImeParam* param) {
}
work_buffer = param->work;
text_buffer = param->input_text_buffer;
text_buffer = param->inputTextBuffer;
std::size_t text_len = std::char_traits<char16_t>::length(text_buffer);
if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(),
@ -26,15 +26,13 @@ ImeState::ImeState(const OrbisImeParam* param) {
}
ImeState::ImeState(ImeState&& other) noexcept
: input_changed(other.input_changed), work_buffer(other.work_buffer),
text_buffer(other.text_buffer), current_text(std::move(other.current_text)),
event_queue(std::move(other.event_queue)) {
: work_buffer(other.work_buffer), text_buffer(other.text_buffer),
current_text(std::move(other.current_text)), event_queue(std::move(other.event_queue)) {
other.text_buffer = nullptr;
}
ImeState& ImeState::operator=(ImeState&& other) noexcept {
if (this != &other) {
input_changed = other.input_changed;
work_buffer = other.work_buffer;
text_buffer = other.text_buffer;
current_text = std::move(other.current_text);
@ -63,6 +61,10 @@ void ImeState::SendCloseEvent() {
SendEvent(&closeEvent);
}
void ImeState::SetText(const char16_t* text, u32 length) {}
void ImeState::SetCaret(u32 position) {}
bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len,
char* utf8_text, std::size_t utf8_text_len) {
std::fill(utf8_text, utf8_text + utf8_text_len, '\0');
@ -180,9 +182,8 @@ void ImeUi::DrawInputText() {
if (first_render) {
SetKeyboardFocusHere();
}
if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->max_text_length,
if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength,
input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) {
state->input_changed = true;
}
}
@ -190,6 +191,39 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ImeUi* ui = static_cast<ImeUi*>(data->UserData);
ASSERT(ui);
static std::string lastText;
std::string currentText(data->Buf, data->BufTextLen);
if (currentText != lastText) {
OrbisImeEditText eventParam{};
eventParam.str = reinterpret_cast<char16_t*>(ui->ime_param->work);
eventParam.caret_index = data->CursorPos;
eventParam.area_num = 1;
eventParam.text_area[0].mode = 1; // Edit mode
eventParam.text_area[0].index = data->CursorPos;
eventParam.text_area[0].length = data->BufTextLen;
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen,
ui->ime_param->inputTextBuffer,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UpdateText;
event.param.text = eventParam;
lastText = currentText;
ui->state->SendEvent(&event);
}
static int lastCaretPos = -1;
if (lastCaretPos == -1) {
lastCaretPos = data->CursorPos;
@ -209,39 +243,6 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ui->state->SendEvent(&event);
}
static std::string lastText;
std::string currentText(data->Buf, data->BufTextLen);
if (currentText != lastText) {
OrbisImeEditText eventParam{};
eventParam.str = reinterpret_cast<char16_t*>(ui->ime_param->work);
eventParam.caret_index = data->CursorPos;
eventParam.area_num = 1;
eventParam.text_area[0].mode = 1; // Edit mode
eventParam.text_area[0].index = data->CursorPos;
eventParam.text_area[0].length = data->BufTextLen;
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str,
ui->ime_param->max_text_length)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen,
ui->ime_param->input_text_buffer,
ui->ime_param->max_text_length)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UpdateText;
event.param.text = eventParam;
lastText = currentText;
ui->state->SendEvent(&event);
}
return 0;
}

View File

@ -22,10 +22,7 @@ class ImeState {
friend class ImeHandler;
friend class ImeUi;
bool input_changed = false;
void* work_buffer{};
char16_t* text_buffer{};
// A character can hold up to 4 bytes in UTF-8
@ -43,6 +40,9 @@ public:
void SendEnterEvent();
void SendCloseEvent();
void SetText(const char16_t* text, u32 length);
void SetCaret(u32 position);
private:
bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text,
std::size_t native_text_len);

View File

@ -1,7 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <magic_enum.hpp>
#include <magic_enum/magic_enum.hpp>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"

View File

@ -1,16 +1,49 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <map>
#include <ranges>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/singleton.h"
#include "core/devices/logger.h"
#include "core/devices/nop_device.h"
#include "core/file_sys/fs.h"
#include "core/libraries/kernel/file_system.h"
#include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/libs.h"
#include "core/memory.h"
#include "kernel.h"
namespace D = Core::Devices;
using FactoryDevice = std::function<std::shared_ptr<D::BaseDevice>(u32, const char*, int, u16)>;
#define GET_DEVICE_FD(fd) \
[](u32, const char*, int, u16) { \
return Common::Singleton<Core::FileSys::HandleTable>::Instance()->GetFile(fd)->device; \
}
// prefix path, only dev devices
static std::map<std::string, FactoryDevice> available_device = {
// clang-format off
{"/dev/stdin", GET_DEVICE_FD(0)},
{"/dev/stdout", GET_DEVICE_FD(1)},
{"/dev/stderr", GET_DEVICE_FD(2)},
{"/dev/fd/0", GET_DEVICE_FD(0)},
{"/dev/fd/1", GET_DEVICE_FD(1)},
{"/dev/fd/2", GET_DEVICE_FD(2)},
{"/dev/deci_stdin", GET_DEVICE_FD(0)},
{"/dev/deci_stdout", GET_DEVICE_FD(1)},
{"/dev/deci_stderr", GET_DEVICE_FD(2)},
{"/dev/null", GET_DEVICE_FD(0)}, // fd0 (stdin) is a nop device
// clang-format on
};
namespace Libraries::Kernel {
auto GetDirectoryEntries(const std::filesystem::path& path) {
@ -24,8 +57,8 @@ auto GetDirectoryEntries(const std::filesystem::path& path) {
return files;
}
int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) {
LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", path, flags, mode);
int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode);
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
@ -44,22 +77,35 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) {
bool direct = (flags & ORBIS_KERNEL_O_DIRECT) != 0;
bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0;
if (std::string_view{path} == "/dev/console") {
std::string_view path{raw_path};
if (path == "/dev/console") {
return 2000;
}
if (std::string_view{path} == "/dev/deci_tty6") {
if (path == "/dev/deci_tty6") {
return 2001;
}
if (std::string_view{path} == "/dev/stdout") {
return 2002;
}
if (std::string_view{path} == "/dev/urandom") {
if (path == "/dev/urandom") {
return 2003;
}
u32 handle = h->CreateHandle();
auto* file = h->GetFile(handle);
if (path.starts_with("/dev/")) {
for (const auto& [prefix, factory] : available_device) {
if (path.starts_with(prefix)) {
file->is_opened = true;
file->type = Core::FileSys::FileType::Device;
file->m_guest_name = path;
file->device = factory(handle, path.data(), flags, mode);
return handle;
}
}
}
if (directory) {
file->is_directory = true;
file->type = Core::FileSys::FileType::Directory;
file->m_guest_name = path;
file->m_host_name = mnt->GetHostPath(file->m_guest_name);
if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist
@ -135,11 +181,12 @@ int PS4_SYSV_ABI sceKernelClose(int d) {
if (file == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
if (!file->is_directory) {
if (file->type == Core::FileSys::FileType::Regular) {
file->f.Close();
}
file->is_opened = false;
LOG_INFO(Kernel_Fs, "Closing {}", file->m_guest_name);
// FIXME: Lock file mutex before deleting it?
h->DeleteHandle(d);
return ORBIS_OK;
}
@ -154,15 +201,7 @@ int PS4_SYSV_ABI posix_close(int d) {
return result;
}
size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) {
if (d <= 2) { // stdin,stdout,stderr
char* str = strdup((const char*)buf);
if (str[nbytes - 1] == '\n')
str[nbytes - 1] = 0;
LOG_INFO(Tty, "{}", str);
free(str);
return nbytes;
}
s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d);
if (file == nullptr) {
@ -170,6 +209,9 @@ size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) {
}
std::scoped_lock lk{file->m_mutex};
if (file->type == Core::FileSys::FileType::Device) {
return file->device->write(buf, nbytes);
}
return file->f.WriteRaw<u8>(buf, nbytes);
}
@ -204,20 +246,75 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) {
return ORBIS_OK;
}
size_t ReadFile(Common::FS::IOFile& file, void* buf, size_t nbytes) {
const auto* memory = Core::Memory::Instance();
// Invalidate up to the actual number of bytes that could be read.
const auto remaining = file.GetSize() - file.Tell();
memory->InvalidateMemory(reinterpret_cast<VAddr>(buf), std::min<u64>(nbytes, remaining));
return file.ReadRaw<u8>(buf, nbytes);
}
size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d);
size_t total_read = 0;
if (file == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
std::scoped_lock lk{file->m_mutex};
if (file->type == Core::FileSys::FileType::Device) {
int r = file->device->readv(iov, iovcnt);
if (r < 0) {
ErrSceToPosix(r);
return -1;
}
return r;
}
size_t total_read = 0;
for (int i = 0; i < iovcnt; i++) {
total_read += file->f.ReadRaw<u8>(iov[i].iov_base, iov[i].iov_len);
total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len);
}
return total_read;
}
size_t PS4_SYSV_ABI _writev(int fd, const SceKernelIovec* iov, int iovcn) {
if (fd == 1) {
size_t total_written = 0;
for (int i = 0; i < iovcn; i++) {
total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout);
}
return total_written;
}
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(fd);
if (file == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
std::scoped_lock lk{file->m_mutex};
if (file->type == Core::FileSys::FileType::Device) {
return file->device->writev(iov, iovcn);
}
size_t total_written = 0;
for (int i = 0; i < iovcn; i++) {
total_written += file->f.WriteRaw<u8>(iov[i].iov_base, iov[i].iov_len);
}
return total_written;
}
s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d);
if (file == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
std::scoped_lock lk{file->m_mutex};
if (file->type == Core::FileSys::FileType::Device) {
return file->device->lseek(offset, whence);
}
Common::FS::SeekOrigin origin{};
if (whence == 0) {
@ -228,7 +325,6 @@ s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) {
origin = Common::FS::SeekOrigin::End;
}
std::scoped_lock lk{file->m_mutex};
if (!file->f.Seek(offset, origin)) {
LOG_CRITICAL(Kernel_Fs, "sceKernelLseek: failed to seek");
return ORBIS_KERNEL_ERROR_EINVAL;
@ -261,7 +357,10 @@ s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) {
}
std::scoped_lock lk{file->m_mutex};
return file->f.ReadRaw<u8>(buf, nbytes);
if (file->type == Core::FileSys::FileType::Device) {
return file->device->read(buf, nbytes);
}
return ReadFile(file->f, buf, nbytes);
}
int PS4_SYSV_ABI posix_read(int d, void* buf, size_t nbytes) {
@ -409,7 +508,13 @@ int PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) {
int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) {
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto path_name = mnt->GetHostPath(path);
std::string_view guest_path{path};
for (const auto& prefix : available_device | std::views::keys) {
if (guest_path.starts_with(prefix)) {
return ORBIS_OK;
}
}
const auto path_name = mnt->GetHostPath(guest_path);
if (!std::filesystem::exists(path_name)) {
return ORBIS_KERNEL_ERROR_ENOENT;
}
@ -431,6 +536,10 @@ s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 off
}
std::scoped_lock lk{file->m_mutex};
if (file->type == Core::FileSys::FileType::Device) {
return file->device->preadv(iov, iovcnt, offset);
}
const s64 pos = file->f.Tell();
SCOPE_EXIT {
file->f.Seek(pos);
@ -441,7 +550,7 @@ s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 off
}
size_t total_read = 0;
for (int i = 0; i < iovcnt; i++) {
total_read += file->f.ReadRaw<u8>(iov[i].iov_base, iov[i].iov_len);
total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len);
}
return total_read;
}
@ -466,18 +575,25 @@ int PS4_SYSV_ABI sceKernelFStat(int fd, OrbisKernelStat* sb) {
}
std::memset(sb, 0, sizeof(OrbisKernelStat));
if (file->is_directory) {
sb->st_mode = 0000777u | 0040000u;
sb->st_size = 0;
sb->st_blksize = 512;
sb->st_blocks = 0;
// TODO incomplete
} else {
switch (file->type) {
case Core::FileSys::FileType::Device:
return file->device->fstat(sb);
case Core::FileSys::FileType::Regular:
sb->st_mode = 0000777u | 0100000u;
sb->st_size = file->f.GetSize();
sb->st_blksize = 512;
sb->st_blocks = (sb->st_size + 511) / 512;
// TODO incomplete
break;
case Core::FileSys::FileType::Directory:
sb->st_mode = 0000777u | 0040000u;
sb->st_size = 0;
sb->st_blksize = 512;
sb->st_blocks = 0;
// TODO incomplete
break;
default:
UNREACHABLE();
}
return ORBIS_OK;
}
@ -495,6 +611,13 @@ int PS4_SYSV_ABI posix_fstat(int fd, OrbisKernelStat* sb) {
s32 PS4_SYSV_ABI sceKernelFsync(int fd) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(fd);
if (file == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
if (file->type == Core::FileSys::FileType::Device) {
return file->device->fsync();
}
file->f.Flush();
return ORBIS_OK;
}
@ -517,6 +640,10 @@ int PS4_SYSV_ABI sceKernelFtruncate(int fd, s64 length) {
return ORBIS_KERNEL_ERROR_EBADF;
}
if (file->type == Core::FileSys::FileType::Device) {
return file->device->ftruncate(length);
}
if (file->m_host_name.empty()) {
return ORBIS_KERNEL_ERROR_EACCES;
}
@ -538,10 +665,15 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) {
if (file == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
if (file->type == Core::FileSys::FileType::Device) {
return file->device->getdents(buf, nbytes, basep);
}
if (file->dirents_index == file->dirents.size()) {
return ORBIS_OK;
}
if (!file->is_directory || nbytes < 512 || file->dirents_index > file->dirents.size()) {
if (file->type != Core::FileSys::FileType::Directory || nbytes < 512 ||
file->dirents_index > file->dirents.size()) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
const auto& entry = file->dirents.at(file->dirents_index++);
@ -586,6 +718,10 @@ s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) {
}
std::scoped_lock lk{file->m_mutex};
if (file->type == Core::FileSys::FileType::Device) {
return file->device->pwrite(buf, nbytes, offset);
}
const s64 pos = file->f.Tell();
SCOPE_EXIT {
file->f.Seek(pos);
@ -637,6 +773,7 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite);
LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, _readv);
LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev);
LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek);
LIB_FUNCTION("Oy6IpwgtYOk", "libScePosix", 1, "libkernel", 1, 1, posix_lseek);
LIB_FUNCTION("oib76F-12fk", "libkernel", 1, "libkernel", 1, 1, sceKernelLseek);

View File

@ -65,6 +65,9 @@ constexpr int ORBIS_KERNEL_O_DSYNC = 0x1000;
constexpr int ORBIS_KERNEL_O_DIRECT = 0x00010000;
constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000;
s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes);
s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes);
void RegisterFileSystem(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Kernel

View File

@ -9,6 +9,9 @@
#include "common/logging/log.h"
#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "common/va_ctx.h"
#include "core/file_sys/fs.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/equeue.h"
#include "core/libraries/kernel/file_system.h"
#include "core/libraries/kernel/kernel.h"
@ -24,6 +27,7 @@
#ifdef _WIN64
#include <Rpc.h>
#endif
#include <common/singleton.h>
namespace Libraries::Kernel {
@ -42,7 +46,7 @@ void KernelSignalRequest() {
}
static void KernelServiceThread(std::stop_token stoken) {
Common::SetCurrentThreadName("shadPS4:Kernel_ServiceThread");
Common::SetCurrentThreadName("shadPS4:KernelServiceThread");
while (!stoken.stop_requested()) {
HLE_TRACE;
@ -65,19 +69,6 @@ static PS4_SYSV_ABI void stack_chk_fail() {
UNREACHABLE();
}
struct iovec {
void* iov_base; /* Base address. */
size_t iov_len; /* Length. */
};
size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) {
size_t total_written = 0;
for (int i = 0; i < iovcn; i++) {
total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout);
}
return total_written;
}
static thread_local int g_posix_errno = 0;
int* PS4_SYSV_ABI __Error() {
@ -142,24 +133,11 @@ void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) {
}
s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) {
if (d <= 2) { // stdin,stdout,stderr
std::string_view str{buf};
if (str[nbytes - 1] == '\n') {
str = str.substr(0, nbytes - 1);
}
LOG_INFO(Tty, "{}", str);
return nbytes;
}
LOG_ERROR(Kernel, "(STUBBED) called d = {} nbytes = {} ", d, nbytes);
UNREACHABLE();
return ORBIS_OK;
return sceKernelWrite(d, buf, nbytes);
}
s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) {
ASSERT_MSG(d == 0, "d is not 0!");
return static_cast<s64>(
strlen(std::fgets(static_cast<char*>(buf), static_cast<int>(nbytes), stdin)));
return sceKernelRead(d, buf, nbytes);
}
struct OrbisKernelUuid {
@ -189,6 +167,29 @@ int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
return 0;
}
int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(fd);
if (file == nullptr) {
LOG_INFO(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} file == nullptr", fd, cmd);
g_posix_errno = POSIX_EBADF;
return -1;
}
if (file->type != Core::FileSys::FileType::Device) {
LOG_WARNING(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} file->type != Device", fd, cmd);
g_posix_errno = ENOTTY;
return -1;
}
VA_CTX(ctx);
int result = file->device->ioctl(cmd, &ctx);
LOG_TRACE(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} result = {}", fd, cmd, result);
if (result < 0) {
ErrSceToPosix(result);
return -1;
}
return result;
}
const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() {
const char* path = "sys";
return path;
@ -203,7 +204,7 @@ int PS4_SYSV_ABI _sigprocmask() {
}
int PS4_SYSV_ABI posix_getpagesize() {
return 4096;
return 16_KB;
}
void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
@ -219,19 +220,20 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
Libraries::Kernel::RegisterException(sym);
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", 1, 1, kernel_ioctl);
LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord);
LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect);
LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask);
LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate);
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error);
LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev);
LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read);
LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize);
LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize);
LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1,
sceLibcHeapGetTraceInfo);
LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write);
LIB_FUNCTION("FN4gaPmuFV8", "libScePosix", 1, "libkernel", 1, 1, ps4__write);
}
} // namespace Libraries::Kernel

View File

@ -492,8 +492,7 @@ int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) {
return ORBIS_OK;
}
auto* memory = Core::Memory::Instance();
memory->UnmapMemory(std::bit_cast<VAddr>(addr), len);
return ORBIS_OK;
return memory->UnmapMemory(std::bit_cast<VAddr>(addr), len);
}
int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) {

View File

@ -20,7 +20,7 @@ int PS4_SYSV_ABI sceKernelIsNeoMode() {
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {
int version = Common::ElfInfo::Instance().RawFirmwareVer();
*ver = version;
return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
return (version >= 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
}
int PS4_SYSV_ABI sceKernelGetCpumode() {
@ -45,9 +45,13 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg
// Load PRX module and relocate any modules that import it.
auto* linker = Common::Singleton<Core::Linker>::Instance();
u32 handle = linker->LoadModule(path, true);
u32 handle = linker->FindByName(path);
if (handle != -1) {
return handle;
}
handle = linker->LoadModule(path, true);
if (handle == -1) {
return ORBIS_KERNEL_ERROR_EINVAL;
return ORBIS_KERNEL_ERROR_ESRCH;
}
auto* module = linker->GetModule(handle);
linker->RelocateAnyImports(module);
@ -60,7 +64,10 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg
// Retrieve and verify proc param according to libkernel.
u64* param = module->GetProcParam<u64*>();
ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]);
module->Start(args, argp, param);
s32 ret = module->Start(args, argp, param);
if (pRes) {
*pRes = ret;
}
return handle;
}
@ -104,6 +111,9 @@ s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags,
LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags);
auto* linker = Common::Singleton<Core::Linker>::Instance();
auto* module = linker->FindByAddress(addr);
if (!module) {
return ORBIS_KERNEL_ERROR_EFAULT;
}
const auto mod_info = module->GetModuleInfoEx();
// Fill in module info.

View File

@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "mutex.h"
#include "common/assert.h"
namespace Libraries::Kernel {
TimedMutex::TimedMutex() {
#ifdef _WIN64
mtx = CreateMutex(nullptr, false, nullptr);
ASSERT(mtx);
#endif
}
TimedMutex::~TimedMutex() {
#ifdef _WIN64
CloseHandle(mtx);
#endif
}
void TimedMutex::lock() {
#ifdef _WIN64
for (;;) {
u64 res = WaitForSingleObjectEx(mtx, INFINITE, true);
if (res == WAIT_OBJECT_0) {
return;
}
}
#else
mtx.lock();
#endif
}
bool TimedMutex::try_lock() {
#ifdef _WIN64
return WaitForSingleObjectEx(mtx, 0, true) == WAIT_OBJECT_0;
#else
return mtx.try_lock();
#endif
}
void TimedMutex::unlock() {
#ifdef _WIN64
ReleaseMutex(mtx);
#else
mtx.unlock();
#endif
}
} // namespace Libraries::Kernel

View File

@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include "common/types.h"
#ifdef _WIN64
#include <windows.h>
#else
#include <mutex>
#endif
namespace Libraries::Kernel {
class TimedMutex {
public:
TimedMutex();
~TimedMutex();
void lock();
bool try_lock();
void unlock();
template <class Rep, class Period>
bool try_lock_for(const std::chrono::duration<Rep, Period>& rel_time) {
#ifdef _WIN64
constexpr auto zero = std::chrono::duration<Rep, Period>::zero();
const auto now = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point abs_time = now;
if (rel_time > zero) {
constexpr auto max = (std::chrono::steady_clock::time_point::max)();
if (abs_time < max - rel_time) {
abs_time += rel_time;
} else {
abs_time = max;
}
}
return try_lock_until(abs_time);
#else
return mtx.try_lock_for(rel_time);
#endif
}
template <class Clock, class Duration>
bool try_lock_until(const std::chrono::time_point<Clock, Duration>& abs_time) {
#ifdef _WIN64
for (;;) {
const auto now = Clock::now();
if (abs_time <= now) {
return false;
}
const auto rel_ms = std::chrono::ceil<std::chrono::milliseconds>(abs_time - now);
u64 res = WaitForSingleObjectEx(mtx, static_cast<u64>(rel_ms.count()), true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_TIMEOUT) {
return false;
}
}
#else
return mtx.try_lock_until(abs_time);
#endif
}
private:
#ifdef _WIN64
HANDLE mtx;
#else
std::timed_mutex mtx;
#endif
};
} // namespace Libraries::Kernel

View File

@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <chrono>
#include "common/assert.h"
#include "common/types.h"
#ifdef _WIN64
#include <windows.h>
#elif defined(__APPLE__)
#include <dispatch/dispatch.h>
#else
#include <semaphore>
#endif
namespace Libraries::Kernel {
template <s64 max>
class Semaphore {
public:
Semaphore(s32 initialCount)
#if !defined(_WIN64) && !defined(__APPLE__)
: sem{initialCount}
#endif
{
#ifdef _WIN64
sem = CreateSemaphore(nullptr, initialCount, max, nullptr);
ASSERT(sem);
#elif defined(__APPLE__)
sem = dispatch_semaphore_create(initialCount);
ASSERT(sem);
#endif
}
~Semaphore() {
#ifdef _WIN64
CloseHandle(sem);
#elif defined(__APPLE__)
dispatch_release(sem);
#endif
}
void release() {
#ifdef _WIN64
ReleaseSemaphore(sem, 1, nullptr);
#elif defined(__APPLE__)
dispatch_semaphore_signal(sem);
#else
sem.release();
#endif
}
void acquire() {
#ifdef _WIN64
for (;;) {
u64 res = WaitForSingleObjectEx(sem, INFINITE, true);
if (res == WAIT_OBJECT_0) {
return;
}
}
#elif defined(__APPLE__)
for (;;) {
const auto res = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
if (res == 0) {
return;
}
}
#else
sem.acquire();
#endif
}
bool try_acquire() {
#ifdef _WIN64
return WaitForSingleObjectEx(sem, 0, true) == WAIT_OBJECT_0;
#elif defined(__APPLE__)
return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0;
#else
return sem.try_acquire();
#endif
}
template <class Rep, class Period>
bool try_acquire_for(const std::chrono::duration<Rep, Period>& rel_time) {
#ifdef _WIN64
const auto start_time = std::chrono::high_resolution_clock::now();
auto rel_time_ms = std::chrono::ceil<std::chrono::milliseconds>(rel_time);
while (rel_time_ms.count() > 0) {
u64 timeout_ms = static_cast<u64>(rel_time_ms.count());
u64 res = WaitForSingleObjectEx(sem, timeout_ms, true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_IO_COMPLETION) {
auto elapsed_time = std::chrono::high_resolution_clock::now() - start_time;
rel_time_ms -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
} else {
return false;
}
}
return false;
#elif defined(__APPLE__)
const auto rel_time_ns = std::chrono::ceil<std::chrono::nanoseconds>(rel_time).count();
const auto timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time_ns);
return dispatch_semaphore_wait(sem, timeout) == 0;
#else
return sem.try_acquire_for(rel_time);
#endif
}
template <class Clock, class Duration>
bool try_acquire_until(const std::chrono::time_point<Clock, Duration>& abs_time) {
#ifdef _WIN64
const auto start_time = Clock::now();
if (start_time >= abs_time) {
return false;
}
auto rel_time = std::chrono::ceil<std::chrono::milliseconds>(abs_time - start_time);
while (rel_time.count() > 0) {
u64 timeout_ms = static_cast<u64>(rel_time.count());
u64 res = WaitForSingleObjectEx(sem, timeout_ms, true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_IO_COMPLETION) {
auto elapsed_time = Clock::now() - start_time;
rel_time -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
} else {
return false;
}
}
return false;
#elif defined(__APPLE__)
auto abs_s = std::chrono::time_point_cast<std::chrono::seconds>(abs_time);
auto abs_ns = std::chrono::time_point_cast<std::chrono::nanoseconds>(abs_time) -
std::chrono::time_point_cast<std::chrono::nanoseconds>(abs_s);
const timespec abs_timespec = {
.tv_sec = abs_s.time_since_epoch().count(),
.tv_nsec = abs_ns.count(),
};
const auto timeout = dispatch_walltime(&abs_timespec, 0);
return dispatch_semaphore_wait(sem, timeout) == 0;
#else
return sem.try_acquire_until(abs_time);
#endif
}
private:
#ifdef _WIN64
HANDLE sem;
#elif defined(__APPLE__)
dispatch_semaphore_t sem;
#else
std::counting_semaphore<max> sem;
#endif
};
using BinarySemaphore = Semaphore<1>;
using CountingSemaphore = Semaphore<0x7FFFFFFF /*ORBIS_KERNEL_SEM_VALUE_MAX*/>;
} // namespace Libraries::Kernel

View File

@ -177,7 +177,7 @@ int PS4_SYSV_ABI posix_pthread_cond_reltimedwait_np(PthreadCondT* cond, PthreadM
return cvp->Wait(mutex, THR_RELTIME, usec);
}
int PthreadCond::Signal() {
int PthreadCond::Signal(Pthread* thread) {
Pthread* curthread = g_curthread;
SleepqLock(this);
@ -187,11 +187,12 @@ int PthreadCond::Signal() {
return 0;
}
Pthread* td = sq->sq_blocked.front();
Pthread* td = thread ? thread : sq->sq_blocked.front();
PthreadMutex* mp = td->mutex_obj;
has_user_waiters = SleepqRemove(sq, td);
std::binary_semaphore* waddr = nullptr;
BinarySemaphore* waddr = nullptr;
if (mp->m_owner == curthread) {
if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) {
curthread->WakeAll();
@ -211,7 +212,7 @@ int PthreadCond::Signal() {
struct BroadcastArg {
Pthread* curthread;
std::binary_semaphore* waddrs[Pthread::MaxDeferWaiters];
BinarySemaphore* waddrs[Pthread::MaxDeferWaiters];
int count;
};
@ -262,7 +263,13 @@ int PthreadCond::Broadcast() {
int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) {
PthreadCond* cvp{};
CHECK_AND_INIT_COND
return cvp->Signal();
return cvp->Signal(nullptr);
}
int PS4_SYSV_ABI posix_pthread_cond_signalto_np(PthreadCondT* cond, Pthread* thread) {
PthreadCond* cvp{};
CHECK_AND_INIT_COND
return cvp->Signal(thread);
}
int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) {
@ -358,6 +365,8 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) {
ORBIS(posix_pthread_cond_reltimedwait_np));
LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_cond_destroy));
LIB_FUNCTION("o69RpYO-Mu0", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_cond_signalto_np));
}
} // namespace Libraries::Kernel

View File

@ -118,7 +118,6 @@ public:
}
m_bits |= bits;
m_cond_var.notify_all();
}
@ -133,6 +132,33 @@ public:
m_bits &= bits;
}
void Cancel(u64 setPattern, int* numWaitThreads) {
std::unique_lock lock{m_mutex};
while (m_status != Status::Set) {
m_mutex.unlock();
std::this_thread::sleep_for(std::chrono::microseconds(10));
m_mutex.lock();
}
if (numWaitThreads) {
*numWaitThreads = m_waiting_threads;
}
m_status = Status::Canceled;
m_bits = setPattern;
m_cond_var.notify_all();
while (m_waiting_threads > 0) {
m_mutex.unlock();
std::this_thread::sleep_for(std::chrono::microseconds(10));
m_mutex.lock();
}
m_status = Status::Set;
}
private:
enum class Status { Set, Canceled, Deleted };
@ -233,7 +259,8 @@ int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern
int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern,
int* pNumWaitThreads) {
LOG_ERROR(Kernel_Event, "(STUBBED) called");
LOG_DEBUG(Kernel_Event, "called");
ef->Cancel(setPattern, pNumWaitThreads);
return ORBIS_OK;
}

View File

@ -7,6 +7,7 @@
#include "core/libraries/libs.h"
#ifdef _WIN64
#include "common/ntapi.h"
#else
#include <signal.h>
#endif
@ -64,6 +65,34 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) {
handler(POSIX_SIGUSR1, &ctx);
}
}
#else
void ExceptionHandler(void* arg1, void* arg2, void* arg3, PCONTEXT context) {
const char* thrName = (char*)arg1;
LOG_INFO(Lib_Kernel, "Exception raised successfully on thread '{}'", thrName);
const auto handler = Handlers[POSIX_SIGUSR1];
if (handler) {
auto ctx = Ucontext{};
ctx.uc_mcontext.mc_r8 = context->R8;
ctx.uc_mcontext.mc_r9 = context->R9;
ctx.uc_mcontext.mc_r10 = context->R10;
ctx.uc_mcontext.mc_r11 = context->R11;
ctx.uc_mcontext.mc_r12 = context->R12;
ctx.uc_mcontext.mc_r13 = context->R13;
ctx.uc_mcontext.mc_r14 = context->R14;
ctx.uc_mcontext.mc_r15 = context->R15;
ctx.uc_mcontext.mc_rdi = context->Rdi;
ctx.uc_mcontext.mc_rsi = context->Rsi;
ctx.uc_mcontext.mc_rbp = context->Rbp;
ctx.uc_mcontext.mc_rbx = context->Rbx;
ctx.uc_mcontext.mc_rdx = context->Rdx;
ctx.uc_mcontext.mc_rax = context->Rax;
ctx.uc_mcontext.mc_rcx = context->Rcx;
ctx.uc_mcontext.mc_rsp = context->Rsp;
ctx.uc_mcontext.mc_fs = context->SegFs;
ctx.uc_mcontext.mc_gs = context->SegGs;
handler(POSIX_SIGUSR1, &ctx);
}
}
#endif
int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelExceptionHandler handler) {
@ -73,9 +102,7 @@ int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelException
}
ASSERT_MSG(!Handlers[POSIX_SIGUSR1], "Invalid parameters");
Handlers[POSIX_SIGUSR1] = handler;
#ifdef _WIN64
UNREACHABLE_MSG("Missing exception implementation");
#else
#ifndef _WIN64
struct sigaction act = {};
act.sa_flags = SA_SIGINFO | SA_RESTART;
act.sa_sigaction = reinterpret_cast<decltype(act.sa_sigaction)>(SigactionHandler);
@ -91,9 +118,7 @@ int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) {
}
ASSERT_MSG(Handlers[POSIX_SIGUSR1], "Invalid parameters");
Handlers[POSIX_SIGUSR1] = nullptr;
#ifdef _WIN64
UNREACHABLE_MSG("Missing exception implementation");
#else
#ifndef _WIN64
struct sigaction act = {};
act.sa_flags = SA_SIGINFO | SA_RESTART;
act.sa_sigaction = nullptr;
@ -103,13 +128,18 @@ int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) {
}
int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) {
LOG_ERROR(Lib_Kernel, "Raising exception");
LOG_WARNING(Lib_Kernel, "Raising exception on thread '{}'", thread->name);
ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!");
#ifdef _WIN64
UNREACHABLE_MSG("Missing exception implementation");
#else
#ifndef _WIN64
pthread_t pthr = *reinterpret_cast<pthread_t*>(thread->native_thr.GetHandle());
pthread_kill(pthr, SIGUSR2);
#else
USER_APC_OPTION option;
option.UserApcFlags = QueueUserApcFlagsSpecialUserApc;
u64 res = NtQueueApcThreadEx(reinterpret_cast<HANDLE>(thread->native_thr.GetHandle()), option,
ExceptionHandler, (void*)thread->name.c_str(), nullptr, nullptr);
ASSERT(res == 0);
#endif
return 0;
}

View File

@ -206,6 +206,7 @@ static void RunThread(void* arg) {
DebugState.AddCurrentThreadToGuestList();
/* Run the current thread's start routine with argument: */
curthread->native_thr.Initialize();
void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg);
/* Remove thread from tracking */
@ -242,6 +243,13 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
static int TidCounter = 1;
new_thread->tid = ++TidCounter;
if (new_thread->attr.stackaddr_attr == 0) {
/* Enforce minimum stack size of 64 KB */
static constexpr size_t MinimumStack = 64_KB;
auto& stacksize = new_thread->attr.stacksize_attr;
stacksize = std::max(stacksize, MinimumStack);
}
if (thread_state->CreateStack(&new_thread->attr) != 0) {
/* Insufficient memory to create a stack: */
thread_state->Free(curthread, new_thread);
@ -280,8 +288,8 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
(*thread) = new_thread;
/* Create thread */
new_thread->native_thr = Core::Thread();
int ret = new_thread->native_thr.Create(RunThread, new_thread);
new_thread->native_thr = Core::NativeThread();
int ret = new_thread->native_thr.Create(RunThread, new_thread, &new_thread->attr);
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret);
if (ret) {
*thread = nullptr;
@ -379,6 +387,7 @@ int PS4_SYSV_ABI posix_sched_get_priority_min() {
int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) {
LOG_INFO(Kernel_Pthread, "name = {}", name);
Common::SetThreadName(reinterpret_cast<void*>(thread->native_thr.GetHandle()), name);
thread->name = name;
return ORBIS_OK;
}
@ -412,6 +421,33 @@ int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* poli
return 0;
}
int PS4_SYSV_ABI posix_pthread_setschedparam(PthreadT pthread, SchedPolicy policy,
const SchedParam* param) {
if (pthread == nullptr || param == nullptr) {
return POSIX_EINVAL;
}
auto* thread_state = ThrState::Instance();
if (pthread == g_curthread) {
g_curthread->lock.lock();
} else if (int ret = thread_state->FindThread(pthread, /*include dead*/ 0); ret != 0) {
return ret;
}
if (pthread->attr.sched_policy == policy &&
(policy == SchedPolicy::Other || pthread->attr.prio == param->sched_priority)) {
pthread->attr.prio = param->sched_priority;
pthread->lock.unlock();
return 0;
}
// TODO: _thr_setscheduler
pthread->attr.sched_policy = policy;
pthread->attr.prio = param->sched_priority;
pthread->lock.unlock();
return 0;
}
int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) {
SchedParam param;
SchedPolicy policy;
@ -495,6 +531,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate);
LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio);
LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam);
LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setschedparam);
LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield);
// Posix-Kernel
@ -510,6 +547,8 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join));
LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_getschedparam));
LIB_FUNCTION("oIRFTjoILbg", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_setschedparam));
LIB_FUNCTION("How7B8Oet6k", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_getname_np));
LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit);
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);

View File

@ -11,6 +11,8 @@
#include <shared_mutex>
#include "common/enum.h"
#include "core/libraries/kernel/sync/mutex.h"
#include "core/libraries/kernel/sync/semaphore.h"
#include "core/libraries/kernel/time.h"
#include "core/thread.h"
#include "core/tls.h"
@ -44,7 +46,7 @@ enum class PthreadMutexProt : u32 {
};
struct PthreadMutex {
std::timed_mutex m_lock;
TimedMutex m_lock;
PthreadMutexFlags m_flags;
Pthread* m_owner;
int m_count;
@ -121,7 +123,7 @@ struct PthreadCond {
int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, u64 usec = 0);
int Signal();
int Signal(Pthread* thread);
int Broadcast();
};
using PthreadCondT = PthreadCond*;
@ -259,7 +261,7 @@ struct Pthread {
int refcount;
PthreadEntryFunc start_routine;
void* arg;
Core::Thread native_thr;
Core::NativeThread native_thr;
PthreadAttr attr;
bool cancel_enable;
bool cancel_pending;
@ -288,14 +290,14 @@ struct Pthread {
int report_events;
int event_mask;
std::string name;
std::binary_semaphore wake_sema{0};
BinarySemaphore wake_sema{0};
SleepQueue* sleepqueue;
void* wchan;
PthreadMutex* mutex_obj;
bool will_sleep;
bool has_user_waiters;
int nwaiter_defer;
std::binary_semaphore* defer_waiters[MaxDeferWaiters];
BinarySemaphore* defer_waiters[MaxDeferWaiters];
bool InCritical() const noexcept {
return locklevel > 0 || critical_count > 0;

View File

@ -177,13 +177,13 @@ int PS4_SYSV_ABI posix_pthread_rwlock_unlock(PthreadRwlockT* rwlock) {
}
if (prwlock->owner == curthread) {
prwlock->lock.unlock();
prwlock->owner = nullptr;
prwlock->lock.unlock();
} else {
prwlock->lock.unlock_shared();
if (prwlock->owner == nullptr) {
curthread->rdlock_count--;
}
prwlock->lock.unlock_shared();
}
return 0;

View File

@ -6,7 +6,10 @@
#include <mutex>
#include <semaphore>
#include "core/libraries/kernel/sync/semaphore.h"
#include "common/logging/log.h"
#include "common/slot_vector.h"
#include "core/libraries/kernel/kernel.h"
#include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/kernel/posix_error.h"
@ -21,7 +24,7 @@ constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF;
struct PthreadSem {
explicit PthreadSem(s32 value_) : semaphore{value_}, value{value_} {}
std::counting_semaphore<ORBIS_KERNEL_SEM_VALUE_MAX> semaphore;
CountingSemaphore semaphore;
std::atomic<s32> value;
};
@ -75,7 +78,7 @@ public:
it = wait_list.erase(it);
token_count -= waiter->need_count;
waiter->was_signaled = true;
waiter->cv.notify_one();
waiter->sem.release();
}
return true;
@ -88,7 +91,7 @@ public:
}
for (auto* waiter : wait_list) {
waiter->was_cancled = true;
waiter->cv.notify_one();
waiter->sem.release();
}
wait_list.clear();
token_count = set_count < 0 ? init_count : set_count;
@ -99,25 +102,29 @@ public:
std::scoped_lock lk{mutex};
for (auto* waiter : wait_list) {
waiter->was_deleted = true;
waiter->cv.notify_one();
waiter->sem.release();
}
wait_list.clear();
}
public:
struct WaitingThread {
std::condition_variable cv;
BinarySemaphore sem;
u32 priority;
s32 need_count;
std::string thr_name;
bool was_signaled{};
bool was_deleted{};
bool was_cancled{};
explicit WaitingThread(s32 need_count, bool is_fifo) : need_count{need_count} {
explicit WaitingThread(s32 need_count, bool is_fifo)
: sem{0}, priority{0}, need_count{need_count} {
// Retrieve calling thread priority for sorting into waiting threads list.
if (!is_fifo) {
priority = g_curthread->attr.prio;
}
thr_name = g_curthread->name;
}
int GetResult(bool timed_out) {
@ -134,24 +141,26 @@ public:
}
int Wait(std::unique_lock<std::mutex>& lk, u32* timeout) {
lk.unlock();
if (!timeout) {
// Wait indefinitely until we are woken up.
cv.wait(lk);
sem.acquire();
lk.lock();
return GetResult(false);
}
// Wait until timeout runs out, recording how much remaining time there was.
const auto start = std::chrono::high_resolution_clock::now();
const auto signaled = cv.wait_for(lk, std::chrono::microseconds(*timeout),
[this] { return was_signaled; });
sem.try_acquire_for(std::chrono::microseconds(*timeout));
const auto end = std::chrono::high_resolution_clock::now();
const auto time =
std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
if (signaled) {
lk.lock();
if (was_signaled) {
*timeout -= time;
} else {
*timeout = 0;
}
return GetResult(!signaled);
return GetResult(!was_signaled);
}
};
@ -180,7 +189,9 @@ public:
bool is_fifo;
};
using OrbisKernelSema = OrbisSem*;
using OrbisKernelSema = Common::SlotId;
static Common::SlotVector<std::unique_ptr<OrbisSem>> orbis_sems;
s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u32 attr,
s32 initCount, s32 maxCount, const void* pOptParam) {
@ -188,46 +199,48 @@ s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u3
LOG_ERROR(Lib_Kernel, "Semaphore creation parameters are invalid!");
return ORBIS_KERNEL_ERROR_EINVAL;
}
*sem = new OrbisSem(initCount, maxCount, pName, attr == 1);
*sem = orbis_sems.insert(
std::move(std::make_unique<OrbisSem>(initCount, maxCount, pName, attr == 1)));
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceKernelWaitSema(OrbisKernelSema sem, s32 needCount, u32* pTimeout) {
if (!sem) {
if (!orbis_sems.is_allocated(sem)) {
return ORBIS_KERNEL_ERROR_ESRCH;
}
return sem->Wait(true, needCount, pTimeout);
return orbis_sems[sem]->Wait(true, needCount, pTimeout);
}
s32 PS4_SYSV_ABI sceKernelSignalSema(OrbisKernelSema sem, s32 signalCount) {
if (!sem) {
if (!orbis_sems.is_allocated(sem)) {
return ORBIS_KERNEL_ERROR_ESRCH;
}
if (!sem->Signal(signalCount)) {
if (!orbis_sems[sem]->Signal(signalCount)) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceKernelPollSema(OrbisKernelSema sem, s32 needCount) {
if (!sem) {
if (!orbis_sems.is_allocated(sem)) {
return ORBIS_KERNEL_ERROR_ESRCH;
}
return sem->Wait(false, needCount, nullptr);
return orbis_sems[sem]->Wait(false, needCount, nullptr);
}
int PS4_SYSV_ABI sceKernelCancelSema(OrbisKernelSema sem, s32 setCount, s32* pNumWaitThreads) {
if (!sem) {
if (!orbis_sems.is_allocated(sem)) {
return ORBIS_KERNEL_ERROR_ESRCH;
}
return sem->Cancel(setCount, pNumWaitThreads);
return orbis_sems[sem]->Cancel(setCount, pNumWaitThreads);
}
int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) {
if (!sem) {
if (!orbis_sems.is_allocated(sem)) {
return ORBIS_KERNEL_ERROR_ESRCH;
}
sem->Delete();
orbis_sems[sem]->Delete();
orbis_sems.erase(sem);
return ORBIS_OK;
}
@ -242,6 +255,16 @@ int PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, int pshared, u32 value) {
return 0;
}
int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) {
if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL;
return -1;
}
delete *sem;
*sem = nullptr;
return 0;
}
int PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) {
if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL;
@ -292,16 +315,6 @@ int PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) {
return 0;
}
int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) {
if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL;
return -1;
}
delete *sem;
*sem = nullptr;
return 0;
}
int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) {
if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL;
@ -313,6 +326,77 @@ int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) {
return 0;
}
s32 PS4_SYSV_ABI scePthreadSemInit(PthreadSem** sem, int flag, u32 value, const char* name) {
if (flag != 0) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
s32 ret = posix_sem_init(sem, 0, value);
if (ret != 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePthreadSemDestroy(PthreadSem** sem) {
s32 ret = posix_sem_destroy(sem);
if (ret != 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePthreadSemWait(PthreadSem** sem) {
s32 ret = posix_sem_wait(sem);
if (ret != 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePthreadSemTrywait(PthreadSem** sem) {
s32 ret = posix_sem_trywait(sem);
if (ret != 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePthreadSemTimedwait(PthreadSem** sem, u32 usec) {
OrbisKernelTimespec time{};
time.tv_sec = usec / 1000000;
time.tv_nsec = (usec % 1000000) * 1000;
s32 ret = posix_sem_timedwait(sem, &time);
if (ret != 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePthreadSemPost(PthreadSem** sem) {
s32 ret = posix_sem_post(sem);
if (ret != 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePthreadSemGetvalue(PthreadSem** sem, int* sval) {
s32 ret = posix_sem_getvalue(sem, sval);
if (ret != 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
void RegisterSemaphore(Core::Loader::SymbolsResolver* sym) {
// Orbis
LIB_FUNCTION("188x57JYp0g", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateSema);
@ -324,12 +408,20 @@ void RegisterSemaphore(Core::Loader::SymbolsResolver* sym) {
// Posix
LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init);
LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy);
LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait);
LIB_FUNCTION("WBWzsRifCEA", "libScePosix", 1, "libkernel", 1, 1, posix_sem_trywait);
LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait);
LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post);
LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy);
LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue);
LIB_FUNCTION("GEnUkDZoUwY", "libkernel", 1, "libkernel", 1, 1, scePthreadSemInit);
LIB_FUNCTION("Vwc+L05e6oE", "libkernel", 1, "libkernel", 1, 1, scePthreadSemDestroy);
LIB_FUNCTION("C36iRE0F5sE", "libkernel", 1, "libkernel", 1, 1, scePthreadSemWait);
LIB_FUNCTION("H2a+IN9TP0E", "libkernel", 1, "libkernel", 1, 1, scePthreadSemTrywait);
LIB_FUNCTION("fjN6NQHhK8k", "libkernel", 1, "libkernel", 1, 1, scePthreadSemTimedwait);
LIB_FUNCTION("aishVAiFaYM", "libkernel", 1, "libkernel", 1, 1, scePthreadSemPost);
LIB_FUNCTION("DjpBvGlaWbQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSemGetvalue);
}
} // namespace Libraries::Kernel

Some files were not shown because too many files have changed in this diff Show More