Merge branch 'main' into compat-gui

This commit is contained in:
f8ith 2024-12-12 15:43:24 +08:00 committed by GitHub
commit d43de282f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
198 changed files with 4072 additions and 755 deletions

2
.gitmodules vendored
View File

@ -102,6 +102,8 @@
[submodule "externals/LibAtrac9"] [submodule "externals/LibAtrac9"]
path = externals/LibAtrac9 path = externals/LibAtrac9
url = https://github.com/shadps4-emu/ext-LibAtrac9.git url = https://github.com/shadps4-emu/ext-LibAtrac9.git
shallow = true
[submodule "externals/libpng"] [submodule "externals/libpng"]
path = externals/libpng path = externals/libpng
url = https://github.com/pnggroup/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(fmt 10.2.0 CONFIG)
find_package(glslang 15 CONFIG) find_package(glslang 15 CONFIG)
find_package(half 1.12.0 MODULE) 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(PNG 1.6 MODULE)
find_package(RenderDoc 1.6.0 MODULE) find_package(RenderDoc 1.6.0 MODULE)
find_package(SDL3 3.1.2 CONFIG) find_package(SDL3 3.1.2 CONFIG)
find_package(stb MODULE)
find_package(toml11 4.2.0 CONFIG) find_package(toml11 4.2.0 CONFIG)
find_package(tsl-robin-map 1.3.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(VulkanMemoryAllocator 3.1.0 CONFIG)
find_package(xbyak 7.07 CONFIG) find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE) 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 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/event_flag.cpp
src/core/libraries/kernel/threads/exception.cpp src/core/libraries/kernel/threads/exception.cpp
src/core/libraries/kernel/threads/exception.h src/core/libraries/kernel/threads/exception.h
@ -495,6 +499,8 @@ set(COMMON src/common/logging/backend.cpp
src/common/slot_vector.h src/common/slot_vector.h
src/common/spin_lock.cpp src/common/spin_lock.cpp
src/common/spin_lock.h src/common/spin_lock.h
src/common/stb.cpp
src/common/stb.h
src/common/string_util.cpp src/common/string_util.cpp
src/common/string_util.h src/common/string_util.h
src/common/thread.cpp src/common/thread.cpp
@ -502,6 +508,7 @@ set(COMMON src/common/logging/backend.cpp
src/common/types.h src/common/types.h
src/common/uint128.h src/common/uint128.h
src/common/unique_function.h src/common/unique_function.h
src/common/va_ctx.h
src/common/version.h src/common/version.h
src/common/ntapi.h src/common/ntapi.h
src/common/ntapi.cpp src/common/ntapi.cpp
@ -526,6 +533,12 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/crypto/crypto.cpp src/core/crypto/crypto.cpp
src/core/crypto/crypto.h src/core/crypto/crypto.h
src/core/crypto/keys.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/pfs.h
src/core/file_format/pkg.cpp src/core/file_format/pkg.cpp
src/core/file_format/pkg.h src/core/file_format/pkg.h
@ -725,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_resource_pool.h
src/video_core/renderer_vulkan/vk_scheduler.cpp src/video_core/renderer_vulkan/vk_scheduler.cpp
src/video_core/renderer_vulkan/vk_scheduler.h 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.cpp
src/video_core/renderer_vulkan/vk_shader_util.h src/video_core/renderer_vulkan/vk_shader_util.h
src/video_core/renderer_vulkan/vk_swapchain.cpp src/video_core/renderer_vulkan/vk_swapchain.cpp
@ -862,11 +877,19 @@ endif()
create_target_directory_groups(shadps4) 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 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(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") 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) 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) 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) if (USE_SYSTEM_VULKAN_LOADER)

View File

@ -12,12 +12,13 @@ path = [
"dist/net.shadps4.shadPS4_metadata.pot", "dist/net.shadps4.shadPS4_metadata.pot",
"dist/net.shadps4.shadPS4.metainfo.xml", "dist/net.shadps4.shadPS4.metainfo.xml",
"dist/net.shadps4.shadPS4.releases.xml", "dist/net.shadps4.shadPS4.releases.xml",
"documents/changelog.txt", "documents/changelog.md",
"documents/Quickstart/2.png", "documents/Quickstart/2.png",
"documents/Screenshots/*", "documents/Screenshots/*",
"scripts/ps4_names.txt", "scripts/ps4_names.txt",
"src/images/about_icon.png", "src/images/about_icon.png",
"src/images/controller_icon.png", "src/images/controller_icon.png",
"src/images/discord.png",
"src/images/dump_icon.png", "src/images/dump_icon.png",
"src/images/exit_icon.png", "src/images/exit_icon.png",
"src/images/file_icon.png", "src/images/file_icon.png",
@ -28,8 +29,10 @@ path = [
"src/images/flag_us.png", "src/images/flag_us.png",
"src/images/flag_world.png", "src/images/flag_world.png",
"src/images/folder_icon.png", "src/images/folder_icon.png",
"src/images/github.png",
"src/images/grid_icon.png", "src/images/grid_icon.png",
"src/images/iconsize_icon.png", "src/images/iconsize_icon.png",
"src/images/ko-fi.png",
"src/images/list_icon.png", "src/images/list_icon.png",
"src/images/list_mode_icon.png", "src/images/list_mode_icon.png",
"src/images/pause_icon.png", "src/images/pause_icon.png",
@ -43,6 +46,8 @@ path = [
"src/images/net.shadps4.shadPS4.svg", "src/images/net.shadps4.shadPS4.svg",
"src/images/themes_icon.png", "src/images/themes_icon.png",
"src/images/update_icon.png", "src/images/update_icon.png",
"src/images/youtube.png",
"src/images/website.png",
"src/shadps4.qrc", "src/shadps4.qrc",
"src/shadps4.rc", "src/shadps4.rc",
] ]
@ -63,7 +68,7 @@ SPDX-FileCopyrightText = "2019-2024 Baldur Karlsson"
SPDX-License-Identifier = "MIT" SPDX-License-Identifier = "MIT"
[[annotations]] [[annotations]]
path = "externals/stb_image.h" path = "externals/stb/**"
precedence = "aggregate" precedence = "aggregate"
SPDX-FileCopyrightText = "2017 Sean Barrett" SPDX-FileCopyrightText = "2017 Sean Barrett"
SPDX-License-Identifier = "MIT" 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. 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. 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 2019 ARM64` instead. 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. 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: Once you are finished, you will have to configure Qt within Visual Studio:
1. Tools -> Options -> Qt -> Versions 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. 3. Enable the default checkmark on the new version you just created.
### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win) ### (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: 3. If you want to build shadPS4 with the Qt Gui:
1. Click x64-Clang-Release and select "Manage Configurations" 1. Click x64-Clang-Release and select "Manage Configurations"
2. Look for "CMake command arguments" and add to the text field 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) (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 3. Press CTRL+S to save and wait a moment for CMake generation
4. Change the project to build to shadps4.exe 4. Change the project to build to shadps4.exe
5. Build -> Build All 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: 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) (Change Qt path if you've installed it to non-default path)
## Option 2: MSYS2/MinGW ## Option 2: MSYS2/MinGW
@ -79,7 +79,7 @@ Normal x86-based computers, follow:
1. Open "MSYS2 MINGW64" from your new applications 1. Open "MSYS2 MINGW64" from your new applications
2. Run `pacman -Syu`, let it complete; 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` 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` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4` 5. Run `cd shadPS4`
@ -93,7 +93,7 @@ ARM64-based computers, follow:
1. Open "MSYS2 CLANGARM64" from your new applications 1. Open "MSYS2 CLANGARM64" from your new applications
2. Run `pacman -Syu`, let it complete; 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` 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` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd 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 v0.3.0 23/09/2024 - codename broamic
================= =================

View File

@ -35,7 +35,7 @@ else()
if (NOT TARGET cryptopp::cryptopp) if (NOT TARGET cryptopp::cryptopp)
set(CRYPTOPP_INSTALL OFF) set(CRYPTOPP_INSTALL OFF)
set(CRYPTOPP_BUILD_TESTING 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) add_subdirectory(cryptopp-cmake)
file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h") file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h")
# remove externals/cryptopp from include directories because it contains a conflicting zlib.h file # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file
@ -216,9 +216,16 @@ endif()
# Discord RPC # Discord RPC
if (ENABLE_DISCORD_RPC) if (ENABLE_DISCORD_RPC)
set(BUILD_EXAMPLES OFF) set(BUILD_EXAMPLES OFF)
add_subdirectory(discord-rpc/) add_subdirectory(discord-rpc)
target_include_directories(discord-rpc INTERFACE discord-rpc/include) target_include_directories(discord-rpc INTERFACE discord-rpc/include)
endif() endif()
# GCN Headers # GCN Headers
add_subdirectory(gcn) 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/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

@ -743,6 +743,7 @@ void setDefaultValues() {
emulator_language = "en"; emulator_language = "en";
m_language = 1; m_language = 1;
gpuId = -1; gpuId = -1;
separateupdatefolder = false;
} }
} // namespace Config } // namespace Config

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -108,14 +108,444 @@ typedef struct _FILE_DISPOSITION_INFORMATION {
BOOLEAN DeleteFile; BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; } 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, PVOID FileInformation, ULONG Length,
FILE_INFORMATION_CLASS FileInformationClass); 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 NtSetInformationFile_t NtSetInformationFile;
extern NtCreateThread_t NtCreateThread;
extern NtTerminateThread_t NtTerminateThread;
extern NtQueueApcThreadEx_t NtQueueApcThreadEx;
namespace Common::NtApi { namespace Common::NtApi {
void Initialize(); 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; return output;
} }
std::string_view U8stringToString(std::u8string_view u8str) {
return std::string_view{reinterpret_cast<const char*>(u8str.data()), u8str.size()};
}
#ifdef _WIN32 #ifdef _WIN32
static std::wstring CPToUTF16(u32 code_page, std::string_view input) { static std::wstring CPToUTF16(u32 code_page, std::string_view input) {
const auto size = 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::vector<std::string> SplitString(const std::string& str, char delimiter);
std::string_view U8stringToString(std::u8string_view u8str);
#ifdef _WIN32 #ifdef _WIN32
[[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input); [[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input);
[[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str); [[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()); 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 #else // !MSVC_VER, so must be POSIX threads
// MinGW with the POSIX threading model does not support pthread_setname_np // 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); pthread_setname_np(pthread_self(), name);
#endif #endif
} }
void SetThreadName(void* thread, const char* name) {
// TODO
}
#endif #endif
#if defined(_WIN32) #if defined(_WIN32)
void SetCurrentThreadName(const char*) { void SetCurrentThreadName(const char*) {
// Do Nothing on MingW // Do Nothing on MinGW
}
void SetThreadName(void* thread, const char* name) {
// Do Nothing on MinGW
} }
#endif #endif

View File

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

@ -177,9 +177,10 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
} }
} }
void DebugStateImpl::CollectShader(const std::string& name, std::span<const u32> spv, void DebugStateImpl::CollectShader(const std::string& name, vk::ShaderModule module,
std::span<const u32> raw_code) { std::span<const u32> spv, std::span<const u32> raw_code,
shader_dump_list.emplace_back(name, std::vector<u32>{spv.begin(), spv.end()}, std::span<const u32> patch_spv, bool is_patched) {
std::vector<u32>{raw_code.begin(), raw_code.end()}); shader_dump_list.emplace_back(name, module, std::vector<u32>{spv.begin(), spv.end()},
std::ranges::sort(shader_dump_list, {}, &ShaderDump::name); 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 "common/types.h"
#include "video_core/amdgpu/liverpool.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 #ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
@ -76,29 +76,46 @@ struct FrameDump {
struct ShaderDump { struct ShaderDump {
std::string name; std::string name;
vk::ShaderModule module;
std::vector<u32> spv; std::vector<u32> spv;
std::vector<u32> raw_code; 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_spv_disasm{};
std::string cache_raw_disasm{}; std::string cache_isa_disasm{};
std::string cache_patch_disasm{};
ShaderDump(std::string name, std::vector<u32> spv, std::vector<u32> raw_code) ShaderDump(std::string name, vk::ShaderModule module, std::vector<u32> spv,
: name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {} 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(const ShaderDump& other) = delete;
ShaderDump(ShaderDump&& other) noexcept ShaderDump(ShaderDump&& other) noexcept
: name{std::move(other.name)}, spv{std::move(other.spv)}, : name{std::move(other.name)}, module{std::move(other.module)}, spv{std::move(other.spv)},
raw_code{std::move(other.raw_code)}, cache_spv_disasm{std::move(other.cache_spv_disasm)}, isa{std::move(other.isa)}, patch_spv{std::move(other.patch_spv)},
cache_raw_disasm{std::move(other.cache_raw_disasm)} {} 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=(const ShaderDump& other) = delete;
ShaderDump& operator=(ShaderDump&& other) noexcept { ShaderDump& operator=(ShaderDump&& other) noexcept {
if (this == &other) if (this == &other)
return *this; return *this;
name = std::move(other.name); name = std::move(other.name);
module = std::move(other.module);
spv = std::move(other.spv); spv = std::move(other.spv);
raw_code = std::move(other.raw_code); 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_spv_disasm = std::move(other.cache_spv_disasm);
cache_raw_disasm = std::move(other.cache_raw_disasm); cache_isa_disasm = std::move(other.cache_isa_disasm);
cache_patch_disasm = std::move(other.cache_patch_disasm);
return *this; return *this;
} }
}; };
@ -186,8 +203,9 @@ public:
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); const AmdGpu::Liverpool::Regs& regs, bool is_compute = false);
void CollectShader(const std::string& name, std::span<const u32> spv, void CollectShader(const std::string& name, vk::ShaderModule module, std::span<const u32> spv,
std::span<const u32> raw_code); std::span<const u32> raw_code, std::span<const u32> patch_spv,
bool is_patched);
}; };
} // namespace DebugStateType } // 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

@ -254,7 +254,7 @@ void L::DrawAdvanced() {
void L::DrawSimple() { void L::DrawSimple() {
const auto io = GetIO(); 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) { static void LoadSettings(const char* line) {
@ -338,6 +338,7 @@ void L::Draw() {
const auto fn = DebugState.flip_frame_count.load(); const auto fn = DebugState.flip_frame_count.load();
frame_graph.AddFrame(fn, io.DeltaTime); frame_graph.AddFrame(fn, io.DeltaTime);
} }
if (IsKeyPressed(ImGuiKey_F10, false)) { if (IsKeyPressed(ImGuiKey_F10, false)) {
if (io.KeyCtrl) { if (io.KeyCtrl) {
show_advanced_debug = !show_advanced_debug; show_advanced_debug = !show_advanced_debug;

View File

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

View File

@ -8,7 +8,7 @@
#include <type_traits> #include <type_traits>
#include <variant> #include <variant>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/io_file.h" #include "common/io_file.h"
@ -117,7 +117,7 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
inline std::optional<std::string> exec_cli(const char* cli) { inline std::optional<std::string> exec_cli(const char* cli) {
std::array<char, 64> buffer{}; std::array<char, 64> buffer{};
std::string output; std::string output;
const auto f = popen(cli, "r"); const auto f = popen(cli, "rt");
if (!f) { if (!f) {
pclose(f); pclose(f);
return {}; return {};
@ -129,21 +129,27 @@ inline std::optional<std::string> exec_cli(const char* cli) {
return output; return output;
} }
inline std::string RunDisassembler(const std::string& disassembler_cli, template <typename T>
const std::vector<u32>& shader_code) { inline std::string RunDisassembler(const std::string& disassembler_cli, const T& shader_code,
bool* success = nullptr) {
std::string shader_dis; std::string shader_dis;
if (disassembler_cli.empty()) { if (disassembler_cli.empty()) {
shader_dis = "No disassembler set"; shader_dis = "No disassembler set";
if (success) {
*success = false;
}
} else { } else {
auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin"; auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin";
constexpr std::string_view src_arg = "{src}"; constexpr std::string_view src_arg = "{src}";
std::string cli = disassembler_cli; std::string cli = disassembler_cli + " 2>&1";
const auto pos = cli.find(src_arg); const auto pos = cli.find(src_arg);
if (pos == std::string::npos) { if (pos == std::string::npos) {
DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" + shader_dis = "Disassembler CLI does not contain {src} argument";
disassembler_cli); if (success) {
*success = false;
}
} else { } else {
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
@ -151,9 +157,16 @@ inline std::string RunDisassembler(const std::string& disassembler_cli,
file.Close(); file.Close();
auto result = exec_cli(cli.c_str()); auto result = exec_cli(cli.c_str());
shader_dis = result.value_or("Could not disassemble shader"); if (result) {
if (shader_dis.empty()) { shader_dis = result.value();
shader_dis = "Disassembly empty or failed"; if (success) {
*success = true;
}
} else {
if (success) {
*success = false;
}
shader_dis = "Could not disassemble shader";
} }
std::filesystem::remove(bin_path); std::filesystem::remove(bin_path);

View File

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

View File

@ -3,7 +3,7 @@
#include <cinttypes> #include <cinttypes>
#include <imgui.h> #include <imgui.h>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "core/debug_state.h" #include "core/debug_state.h"
#include "core/memory.h" #include "core/memory.h"

View File

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

View File

@ -5,7 +5,7 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <imgui.h> #include <imgui.h>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include <stdio.h> #include <stdio.h>
#include "common.h" #include "common.h"

View File

@ -1,66 +1,221 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include "shader_list.h" #include "shader_list.h"
#include <imgui.h> #include <imgui.h>
#include "common.h" #include "common.h"
#include "common/config.h" #include "common/config.h"
#include "common/path_util.h"
#include "common/string_util.h"
#include "core/debug_state.h" #include "core/debug_state.h"
#include "core/devtools/options.h" #include "core/devtools/options.h"
#include "imgui/imgui_std.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; using namespace ImGui;
namespace Core::Devtools::Widget { namespace Core::Devtools::Widget {
void ShaderList::DrawShader(DebugStateType::ShaderDump& value) { ShaderList::Selection::Selection(int index) : index(index) {
if (!loaded_data) { isa_editor.SetPalette(TextEditor::GetDarkPalette());
loaded_data = true; isa_editor.SetReadOnly(true);
if (value.cache_raw_disasm.empty()) { glsl_editor.SetPalette(TextEditor::GetDarkPalette());
value.cache_raw_disasm = RunDisassembler(Options.disassembler_cli_isa, value.raw_code); glsl_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
} presenter->GetWindow().RequestKeyboard();
isa_editor.SetText(value.cache_raw_disasm); }
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()) { if (value.cache_spv_disasm.empty()) {
value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv); value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv);
} }
spv_editor.SetText(value.cache_spv_disasm); 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>{}};
} }
if (SmallButton("<-")) { value.is_patched = !value.patch_spv.empty();
selected_shader = -1; 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);
} }
SameLine(); }
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()); Text("%s", value.name.c_str());
SameLine(0.0f, 7.0f); SameLine(0.0f, 7.0f);
if (BeginCombo("Shader type", showing_isa ? "ISA" : "SPIRV", ImGuiComboFlags_WidthFitPreview)) { if (Checkbox("Enable patch", &value.is_patched)) {
if (Selectable("SPIRV")) { if (value.is_patched) {
showing_isa = false; if (value.patch_source.empty()) {
value.patch_source = value.cache_spv_disasm;
} }
if (Selectable("ISA")) { isa_editor.SetText(value.cache_patch_disasm);
showing_isa = true; 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(); EndCombo();
} }
if (showing_isa) {
isa_editor.Render("ISA", GetContentRegionAvail());
} else { } else {
spv_editor.Render("SPIRV", GetContentRegionAvail()); if (BeginCombo("Shader type", showing_bin ? "ISA" : "GLSL",
ImGuiComboFlags_WidthFitPreview)) {
if (Selectable("GLSL")) {
showing_bin = false;
}
if (Selectable("ISA")) {
showing_bin = true;
}
EndCombo();
}
} }
}
ShaderList::ShaderList() { if (value.is_patched) {
isa_editor.SetPalette(TextEditor::GetDarkPalette()); bool save = false;
isa_editor.SetReadOnly(true); bool compile = false;
spv_editor.SetPalette(TextEditor::GetDarkPalette()); SameLine(0.0f, 3.0f);
spv_editor.SetReadOnly(true); if (Button("Save")) {
spv_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL()); 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() { 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); SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver);
if (!Begin("Shader list", &open)) { if (!Begin("Shader list", &open)) {
End(); End();
@ -73,18 +228,19 @@ void ShaderList::Draw() {
return; return;
} }
if (selected_shader >= 0) {
DrawShader(DebugState.shader_dump_list[selected_shader]);
End();
return;
}
auto width = GetContentRegionAvail().x; auto width = GetContentRegionAvail().x;
int i = 0; int i = 0;
for (const auto& shader : DebugState.shader_dump_list) { for (const auto& shader : DebugState.shader_dump_list) {
if (ButtonEx(shader.name.c_str(), {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) { char name[128];
selected_shader = i; if (shader.is_patched) {
loaded_data = false; 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++; i++;
} }

View File

@ -6,20 +6,32 @@
#include "core/debug_state.h" #include "core/debug_state.h"
#include "text_editor.h" #include "text_editor.h"
#include <filesystem>
namespace Core::Devtools::Widget { namespace Core::Devtools::Widget {
class ShaderList { class ShaderList {
int selected_shader = -1; struct Selection {
TextEditor isa_editor{}; explicit Selection(int index);
TextEditor spv_editor{}; ~Selection();
bool loaded_data = false;
bool showing_isa = false;
void DrawShader(DebugStateType::ShaderDump& value); 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: public:
ShaderList();
bool open = false; bool open = false;
void Draw(); void Draw();

View File

@ -1059,7 +1059,8 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) {
if (!mIgnoreImGuiChild) if (!mIgnoreImGuiChild)
ImGui::BeginChild(aTitle, aSize, aBorder, ImGui::BeginChild(aTitle, aSize, aBorder,
ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_HorizontalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove); ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoNav);
if (mHandleKeyboardInputs) { if (mHandleKeyboardInputs) {
HandleKeyboardInputs(); HandleKeyboardInputs();
@ -2331,4 +2332,50 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL() {
return langDef; 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 } // namespace Core::Devtools::Widget

View File

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

View File

@ -5,13 +5,9 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/io_file.h" #include "common/io_file.h"
#include "common/stb.h"
#include "splash.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) { bool Splash::Open(const std::filesystem::path& filepath) {
ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed"); ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed");

View File

@ -4,12 +4,12 @@
#include <algorithm> #include <algorithm>
#include "common/config.h" #include "common/config.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/devices/logger.h"
#include "core/devices/nop_device.h"
#include "core/file_sys/fs.h" #include "core/file_sys/fs.h"
namespace Core::FileSys { 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, void MntPoints::Mount(const std::filesystem::path& host_folder, const std::string& guest_folder,
bool read_only) { bool read_only) {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
@ -135,7 +135,6 @@ int HandleTable::CreateHandle() {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
auto* file = new File{}; auto* file = new File{};
file->is_directory = false;
file->is_opened = false; file->is_opened = false;
int existingFilesNum = m_files.size(); int existingFilesNum = m_files.size();
@ -143,23 +142,23 @@ int HandleTable::CreateHandle() {
for (int index = 0; index < existingFilesNum; index++) { for (int index = 0; index < existingFilesNum; index++) {
if (m_files.at(index) == nullptr) { if (m_files.at(index) == nullptr) {
m_files[index] = file; m_files[index] = file;
return index + RESERVED_HANDLES; return index;
} }
} }
m_files.push_back(file); m_files.push_back(file);
return m_files.size() + RESERVED_HANDLES - 1; return m_files.size() - 1;
} }
void HandleTable::DeleteHandle(int d) { void HandleTable::DeleteHandle(int d) {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
delete m_files.at(d - RESERVED_HANDLES); delete m_files.at(d);
m_files[d - RESERVED_HANDLES] = nullptr; m_files[d] = nullptr;
} }
File* HandleTable::GetFile(int d) { File* HandleTable::GetFile(int d) {
std::scoped_lock lock{m_mutex}; 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) { File* HandleTable::GetFile(const std::filesystem::path& host_name) {
@ -171,4 +170,20 @@ File* HandleTable::GetFile(const std::filesystem::path& host_name) {
return nullptr; 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 } // namespace Core::FileSys

View File

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

View File

@ -9,7 +9,7 @@
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
namespace Libraries::Ajm { 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_5POINT1 = 0x060F;
constexpr int ORBIS_AJM_CHANNELMASK_7POINT1 = 0x063F; 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) { u32 GetChannelMask(u32 num_channels) {
switch (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) { 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); 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() { 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) { u32* out_batch_id) {
LOG_TRACE(Lib_Ajm, "called context = {}, batch_size = {:#x}, priority = {}", context_id, LOG_TRACE(Lib_Ajm, "called context = {}, batch_size = {:#x}, priority = {}", context_id,
batch_size, priority); 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, int PS4_SYSV_ABI sceAjmBatchWait(const u32 context_id, const u32 batch_id, const u32 timeout,
AjmBatchError* const batch_error) { AjmBatchError* const batch_error) {
LOG_TRACE(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context_id, batch_id, LOG_TRACE(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context_id, batch_id,
timeout); 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() { 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) { int PS4_SYSV_ABI sceAjmInitialize(s64 reserved, u32* p_context_id) {
LOG_INFO(Lib_Ajm, "called reserved = {}", reserved); LOG_INFO(Lib_Ajm, "called reserved = {}", reserved);
ASSERT_MSG(context == nullptr, "Multiple contexts are currently unsupported.");
if (p_context_id == nullptr || reserved != 0) { if (p_context_id == nullptr || reserved != 0) {
return ORBIS_AJM_ERROR_INVALID_PARAMETER; return ORBIS_AJM_ERROR_INVALID_PARAMETER;
} }
*p_context_id = 1; u32 id = contexts.size() + 1;
context = std::make_unique<AjmContext>(); *p_context_id = id;
contexts.emplace(id, std::make_unique<AjmContext>());
return ORBIS_OK; return ORBIS_OK;
} }
@ -135,12 +153,24 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context_id, AjmCodecType codec_type,
AjmInstanceFlags flags, u32* out_instance) { AjmInstanceFlags flags, u32* out_instance) {
LOG_INFO(Lib_Ajm, "called context = {}, codec_type = {}, flags = {:#x}", context_id, LOG_INFO(Lib_Ajm, "called context = {}, codec_type = {}, flags = {:#x}", context_id,
magic_enum::enum_name(codec_type), flags.raw); 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) { int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context_id, u32 instance_id) {
LOG_INFO(Lib_Ajm, "called context = {}, instance = {}", context_id, 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() { int PS4_SYSV_ABI sceAjmInstanceExtend() {
@ -168,7 +198,13 @@ int PS4_SYSV_ABI sceAjmModuleRegister(u32 context_id, AjmCodecType codec_type, s
if (reserved != 0) { if (reserved != 0) {
return ORBIS_AJM_ERROR_INVALID_PARAMETER; 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() { int PS4_SYSV_ABI sceAjmModuleUnregister() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@
#include "core/libraries/avplayer/avplayer_file_streamer.h" #include "core/libraries/avplayer/avplayer_file_streamer.h"
#include "core/libraries/avplayer/avplayer_source.h" #include "core/libraries/avplayer/avplayer_source.h"
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
extern "C" { extern "C" {
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
@ -18,16 +18,7 @@ extern "C" {
#include <libswscale/swscale.h> #include <libswscale/swscale.h>
} }
// The av_err2str macro in libavutil/error.h does not play nice with C++ #include "common/support/avdec.h"
#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
namespace Libraries::AvPlayer { namespace Libraries::AvPlayer {

View File

@ -8,7 +8,7 @@
#include "core/libraries/avplayer/avplayer_state.h" #include "core/libraries/avplayer/avplayer_state.h"
#include "core/tls.h" #include "core/tls.h"
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
namespace Libraries::AvPlayer { 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, .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 += acb_size;
*asc_queue.read_addr %= asc_queue.ring_size_dw * 4; *asc_queue.read_addr %= asc_queue.ring_size_dw * 4;
} }
int PS4_SYSV_ABI sceGnmDingDongForWorkload() { void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id) {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); LOG_DEBUG(Lib_GnmDriver, "called, redirecting to sceGnmDingDong");
return ORBIS_OK; sceGnmDingDong(gnm_vqid, next_offs_dw);
} }
int PS4_SYSV_ABI sceGnmDisableMipStatsReport() { int PS4_SYSV_ABI sceGnmDisableMipStatsReport() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,49 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <map>
#include <ranges>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/singleton.h" #include "common/singleton.h"
#include "core/devices/logger.h"
#include "core/devices/nop_device.h"
#include "core/file_sys/fs.h" #include "core/file_sys/fs.h"
#include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/file_system.h"
#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/memory.h"
#include "kernel.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 { namespace Libraries::Kernel {
auto GetDirectoryEntries(const std::filesystem::path& path) { auto GetDirectoryEntries(const std::filesystem::path& path) {
@ -24,8 +57,8 @@ auto GetDirectoryEntries(const std::filesystem::path& path) {
return files; return files;
} }
int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) { int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", path, flags, mode); LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode);
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance(); auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::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 direct = (flags & ORBIS_KERNEL_O_DIRECT) != 0;
bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 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; return 2000;
} }
if (std::string_view{path} == "/dev/deci_tty6") { if (path == "/dev/deci_tty6") {
return 2001; return 2001;
} }
if (std::string_view{path} == "/dev/stdout") { if (path == "/dev/urandom") {
return 2002;
}
if (std::string_view{path} == "/dev/urandom") {
return 2003; return 2003;
} }
u32 handle = h->CreateHandle(); u32 handle = h->CreateHandle();
auto* file = h->GetFile(handle); 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) { if (directory) {
file->is_directory = true; file->type = Core::FileSys::FileType::Directory;
file->m_guest_name = path; file->m_guest_name = path;
file->m_host_name = mnt->GetHostPath(file->m_guest_name); file->m_host_name = mnt->GetHostPath(file->m_guest_name);
if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist 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) { if (file == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
} }
if (!file->is_directory) { if (file->type == Core::FileSys::FileType::Regular) {
file->f.Close(); file->f.Close();
} }
file->is_opened = false; file->is_opened = false;
LOG_INFO(Kernel_Fs, "Closing {}", file->m_guest_name); LOG_INFO(Kernel_Fs, "Closing {}", file->m_guest_name);
// FIXME: Lock file mutex before deleting it?
h->DeleteHandle(d); h->DeleteHandle(d);
return ORBIS_OK; return ORBIS_OK;
} }
@ -154,15 +201,7 @@ int PS4_SYSV_ABI posix_close(int d) {
return result; return result;
} }
size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) { s64 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;
}
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance(); auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d); auto* file = h->GetFile(d);
if (file == nullptr) { 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}; 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); return file->f.WriteRaw<u8>(buf, nbytes);
} }
@ -204,20 +246,75 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) {
return ORBIS_OK; 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) { size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance(); auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d); 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}; 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++) { 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; 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) { s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance(); auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d); 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{}; Common::FS::SeekOrigin origin{};
if (whence == 0) { if (whence == 0) {
@ -228,7 +325,6 @@ s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) {
origin = Common::FS::SeekOrigin::End; origin = Common::FS::SeekOrigin::End;
} }
std::scoped_lock lk{file->m_mutex};
if (!file->f.Seek(offset, origin)) { if (!file->f.Seek(offset, origin)) {
LOG_CRITICAL(Kernel_Fs, "sceKernelLseek: failed to seek"); LOG_CRITICAL(Kernel_Fs, "sceKernelLseek: failed to seek");
return ORBIS_KERNEL_ERROR_EINVAL; 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}; 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) { 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) { int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) {
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance(); 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)) { if (!std::filesystem::exists(path_name)) {
return ORBIS_KERNEL_ERROR_ENOENT; 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}; 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(); const s64 pos = file->f.Tell();
SCOPE_EXIT { SCOPE_EXIT {
file->f.Seek(pos); 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; size_t total_read = 0;
for (int i = 0; i < iovcnt; i++) { 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; return total_read;
} }
@ -466,18 +575,25 @@ int PS4_SYSV_ABI sceKernelFStat(int fd, OrbisKernelStat* sb) {
} }
std::memset(sb, 0, sizeof(OrbisKernelStat)); std::memset(sb, 0, sizeof(OrbisKernelStat));
if (file->is_directory) { switch (file->type) {
sb->st_mode = 0000777u | 0040000u; case Core::FileSys::FileType::Device:
sb->st_size = 0; return file->device->fstat(sb);
sb->st_blksize = 512; case Core::FileSys::FileType::Regular:
sb->st_blocks = 0;
// TODO incomplete
} else {
sb->st_mode = 0000777u | 0100000u; sb->st_mode = 0000777u | 0100000u;
sb->st_size = file->f.GetSize(); sb->st_size = file->f.GetSize();
sb->st_blksize = 512; sb->st_blksize = 512;
sb->st_blocks = (sb->st_size + 511) / 512; sb->st_blocks = (sb->st_size + 511) / 512;
// TODO incomplete // 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; return ORBIS_OK;
} }
@ -495,6 +611,13 @@ int PS4_SYSV_ABI posix_fstat(int fd, OrbisKernelStat* sb) {
s32 PS4_SYSV_ABI sceKernelFsync(int fd) { s32 PS4_SYSV_ABI sceKernelFsync(int fd) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance(); auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(fd); 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(); file->f.Flush();
return ORBIS_OK; return ORBIS_OK;
} }
@ -517,6 +640,10 @@ int PS4_SYSV_ABI sceKernelFtruncate(int fd, s64 length) {
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
} }
if (file->type == Core::FileSys::FileType::Device) {
return file->device->ftruncate(length);
}
if (file->m_host_name.empty()) { if (file->m_host_name.empty()) {
return ORBIS_KERNEL_ERROR_EACCES; return ORBIS_KERNEL_ERROR_EACCES;
} }
@ -538,10 +665,15 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) {
if (file == nullptr) { if (file == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF; 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()) { if (file->dirents_index == file->dirents.size()) {
return ORBIS_OK; 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; return ORBIS_KERNEL_ERROR_EINVAL;
} }
const auto& entry = file->dirents.at(file->dirents_index++); 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}; 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(); const s64 pos = file->f.Tell();
SCOPE_EXIT { SCOPE_EXIT {
file->f.Seek(pos); 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("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite);
LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, _readv); 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", "libkernel", 1, "libkernel", 1, 1, posix_lseek);
LIB_FUNCTION("Oy6IpwgtYOk", "libScePosix", 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); 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_DIRECT = 0x00010000;
constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000; 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); void RegisterFileSystem(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Kernel } // namespace Libraries::Kernel

View File

@ -9,6 +9,9 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/polyfill_thread.h" #include "common/polyfill_thread.h"
#include "common/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/equeue.h"
#include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/file_system.h"
#include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/kernel.h"
@ -24,6 +27,7 @@
#ifdef _WIN64 #ifdef _WIN64
#include <Rpc.h> #include <Rpc.h>
#endif #endif
#include <common/singleton.h>
namespace Libraries::Kernel { namespace Libraries::Kernel {
@ -42,7 +46,7 @@ void KernelSignalRequest() {
} }
static void KernelServiceThread(std::stop_token stoken) { static void KernelServiceThread(std::stop_token stoken) {
Common::SetCurrentThreadName("shadPS4:Kernel_ServiceThread"); Common::SetCurrentThreadName("shadPS4:KernelServiceThread");
while (!stoken.stop_requested()) { while (!stoken.stop_requested()) {
HLE_TRACE; HLE_TRACE;
@ -65,19 +69,6 @@ static PS4_SYSV_ABI void stack_chk_fail() {
UNREACHABLE(); 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; static thread_local int g_posix_errno = 0;
int* PS4_SYSV_ABI __Error() { 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) { s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) {
if (d <= 2) { // stdin,stdout,stderr return sceKernelWrite(d, buf, nbytes);
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;
} }
s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) { s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) {
ASSERT_MSG(d == 0, "d is not 0!"); return sceKernelRead(d, buf, nbytes);
return static_cast<s64>(
strlen(std::fgets(static_cast<char*>(buf), static_cast<int>(nbytes), stdin)));
} }
struct OrbisKernelUuid { struct OrbisKernelUuid {
@ -189,6 +167,29 @@ int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
return 0; 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* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() {
const char* path = "sys"; const char* path = "sys";
return path; return path;
@ -219,19 +220,20 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
Libraries::Kernel::RegisterException(sym); Libraries::Kernel::RegisterException(sym);
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); 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("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord);
LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect); LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect);
LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask); LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask);
LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate); LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate);
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail); LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error); 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("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", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize);
LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 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, LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1,
sceLibcHeapGetTraceInfo); sceLibcHeapGetTraceInfo);
LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write); LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write);
LIB_FUNCTION("FN4gaPmuFV8", "libScePosix", 1, "libkernel", 1, 1, ps4__write);
} }
} // namespace Libraries::Kernel } // namespace Libraries::Kernel

View File

@ -492,8 +492,7 @@ int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) {
return ORBIS_OK; return ORBIS_OK;
} }
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
memory->UnmapMemory(std::bit_cast<VAddr>(addr), len); return memory->UnmapMemory(std::bit_cast<VAddr>(addr), len);
return ORBIS_OK;
} }
int PS4_SYSV_ABI posix_munmap(void* addr, size_t 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 PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {
int version = Common::ElfInfo::Instance().RawFirmwareVer(); int version = Common::ElfInfo::Instance().RawFirmwareVer();
*ver = version; *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() { int PS4_SYSV_ABI sceKernelGetCpumode() {
@ -45,10 +45,11 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg
// Load PRX module and relocate any modules that import it. // Load PRX module and relocate any modules that import it.
auto* linker = Common::Singleton<Core::Linker>::Instance(); auto* linker = Common::Singleton<Core::Linker>::Instance();
u32 handle = linker->LoadModule(path, true); u32 handle = linker->FindByName(path);
if (handle == -1) { if (handle != -1) {
return ORBIS_KERNEL_ERROR_EINVAL; return handle;
} }
handle = linker->LoadModule(path, true);
auto* module = linker->GetModule(handle); auto* module = linker->GetModule(handle);
linker->RelocateAnyImports(module); linker->RelocateAnyImports(module);
@ -60,7 +61,10 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg
// Retrieve and verify proc param according to libkernel. // Retrieve and verify proc param according to libkernel.
u64* param = module->GetProcParam<u64*>(); u64* param = module->GetProcParam<u64*>();
ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]); 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; return handle;
} }
@ -104,6 +108,9 @@ s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags,
LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags); LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags);
auto* linker = Common::Singleton<Core::Linker>::Instance(); auto* linker = Common::Singleton<Core::Linker>::Instance();
auto* module = linker->FindByAddress(addr); auto* module = linker->FindByAddress(addr);
if (!module) {
return ORBIS_KERNEL_ERROR_EFAULT;
}
const auto mod_info = module->GetModuleInfoEx(); const auto mod_info = module->GetModuleInfoEx();
// Fill in module info. // 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); return cvp->Wait(mutex, THR_RELTIME, usec);
} }
int PthreadCond::Signal() { int PthreadCond::Signal(Pthread* thread) {
Pthread* curthread = g_curthread; Pthread* curthread = g_curthread;
SleepqLock(this); SleepqLock(this);
@ -187,11 +187,12 @@ int PthreadCond::Signal() {
return 0; return 0;
} }
Pthread* td = sq->sq_blocked.front(); Pthread* td = thread ? thread : sq->sq_blocked.front();
PthreadMutex* mp = td->mutex_obj; PthreadMutex* mp = td->mutex_obj;
has_user_waiters = SleepqRemove(sq, td); has_user_waiters = SleepqRemove(sq, td);
std::binary_semaphore* waddr = nullptr; BinarySemaphore* waddr = nullptr;
if (mp->m_owner == curthread) { if (mp->m_owner == curthread) {
if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) { if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) {
curthread->WakeAll(); curthread->WakeAll();
@ -211,7 +212,7 @@ int PthreadCond::Signal() {
struct BroadcastArg { struct BroadcastArg {
Pthread* curthread; Pthread* curthread;
std::binary_semaphore* waddrs[Pthread::MaxDeferWaiters]; BinarySemaphore* waddrs[Pthread::MaxDeferWaiters];
int count; int count;
}; };
@ -262,7 +263,13 @@ int PthreadCond::Broadcast() {
int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) { int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) {
PthreadCond* cvp{}; PthreadCond* cvp{};
CHECK_AND_INIT_COND 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) { 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)); ORBIS(posix_pthread_cond_reltimedwait_np));
LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_cond_destroy)); ORBIS(posix_pthread_cond_destroy));
LIB_FUNCTION("o69RpYO-Mu0", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_cond_signalto_np));
} }
} // namespace Libraries::Kernel } // namespace Libraries::Kernel

View File

@ -118,7 +118,6 @@ public:
} }
m_bits |= bits; m_bits |= bits;
m_cond_var.notify_all(); m_cond_var.notify_all();
} }
@ -133,6 +132,33 @@ public:
m_bits &= bits; 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: private:
enum class Status { Set, Canceled, Deleted }; 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 PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern,
int* pNumWaitThreads) { int* pNumWaitThreads) {
LOG_ERROR(Kernel_Event, "(STUBBED) called"); LOG_DEBUG(Kernel_Event, "called");
ef->Cancel(setPattern, pNumWaitThreads);
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -7,6 +7,7 @@
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#ifdef _WIN64 #ifdef _WIN64
#include "common/ntapi.h"
#else #else
#include <signal.h> #include <signal.h>
#endif #endif
@ -64,6 +65,34 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) {
handler(POSIX_SIGUSR1, &ctx); 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 #endif
int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelExceptionHandler handler) { 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"); ASSERT_MSG(!Handlers[POSIX_SIGUSR1], "Invalid parameters");
Handlers[POSIX_SIGUSR1] = handler; Handlers[POSIX_SIGUSR1] = handler;
#ifdef _WIN64 #ifndef _WIN64
UNREACHABLE_MSG("Missing exception implementation");
#else
struct sigaction act = {}; struct sigaction act = {};
act.sa_flags = SA_SIGINFO | SA_RESTART; act.sa_flags = SA_SIGINFO | SA_RESTART;
act.sa_sigaction = reinterpret_cast<decltype(act.sa_sigaction)>(SigactionHandler); 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"); ASSERT_MSG(Handlers[POSIX_SIGUSR1], "Invalid parameters");
Handlers[POSIX_SIGUSR1] = nullptr; Handlers[POSIX_SIGUSR1] = nullptr;
#ifdef _WIN64 #ifndef _WIN64
UNREACHABLE_MSG("Missing exception implementation");
#else
struct sigaction act = {}; struct sigaction act = {};
act.sa_flags = SA_SIGINFO | SA_RESTART; act.sa_flags = SA_SIGINFO | SA_RESTART;
act.sa_sigaction = nullptr; act.sa_sigaction = nullptr;
@ -103,13 +128,18 @@ int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) {
} }
int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int 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!"); ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!");
#ifdef _WIN64 #ifndef _WIN64
UNREACHABLE_MSG("Missing exception implementation");
#else
pthread_t pthr = *reinterpret_cast<pthread_t*>(thread->native_thr.GetHandle()); pthread_t pthr = *reinterpret_cast<pthread_t*>(thread->native_thr.GetHandle());
pthread_kill(pthr, SIGUSR2); 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 #endif
return 0; return 0;
} }

View File

@ -206,6 +206,7 @@ static void RunThread(void* arg) {
DebugState.AddCurrentThreadToGuestList(); DebugState.AddCurrentThreadToGuestList();
/* Run the current thread's start routine with argument: */ /* Run the current thread's start routine with argument: */
curthread->native_thr.Initialize();
void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg); void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg);
/* Remove thread from tracking */ /* 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; static int TidCounter = 1;
new_thread->tid = ++TidCounter; 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) { if (thread_state->CreateStack(&new_thread->attr) != 0) {
/* Insufficient memory to create a stack: */ /* Insufficient memory to create a stack: */
thread_state->Free(curthread, new_thread); thread_state->Free(curthread, new_thread);
@ -280,7 +288,7 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
(*thread) = new_thread; (*thread) = new_thread;
/* Create thread */ /* Create thread */
new_thread->native_thr = Core::Thread(); new_thread->native_thr = Core::NativeThread();
int ret = new_thread->native_thr.Create(RunThread, new_thread, &new_thread->attr); int ret = new_thread->native_thr.Create(RunThread, new_thread, &new_thread->attr);
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret); ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret);
if (ret) { if (ret) {
@ -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) { int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) {
LOG_INFO(Kernel_Pthread, "name = {}", name); LOG_INFO(Kernel_Pthread, "name = {}", name);
Common::SetThreadName(reinterpret_cast<void*>(thread->native_thr.GetHandle()), name);
thread->name = name; thread->name = name;
return ORBIS_OK; return ORBIS_OK;
} }
@ -412,6 +421,33 @@ int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* poli
return 0; 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) { int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) {
SchedParam param; SchedParam param;
SchedPolicy policy; 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("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate);
LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio); 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("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); LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield);
// Posix-Kernel // 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("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join));
LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1, LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_getschedparam)); 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("How7B8Oet6k", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_getname_np));
LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit); LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit);
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);

View File

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

View File

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

View File

@ -52,7 +52,22 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() {
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) { int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
#ifdef _WIN64 #ifdef _WIN64
std::this_thread::sleep_for(std::chrono::microseconds(microseconds)); const auto start_time = std::chrono::high_resolution_clock::now();
auto total_wait_time = std::chrono::microseconds(microseconds);
while (total_wait_time.count() > 0) {
auto wait_time = std::chrono::ceil<std::chrono::milliseconds>(total_wait_time).count();
u64 res = SleepEx(static_cast<u64>(wait_time), true);
if (res == WAIT_IO_COMPLETION) {
auto elapsedTime = std::chrono::high_resolution_clock::now() - start_time;
auto elapsedMicroseconds =
std::chrono::duration_cast<std::chrono::microseconds>(elapsedTime).count();
total_wait_time = std::chrono::microseconds(microseconds - elapsedMicroseconds);
} else {
break;
}
}
return 0; return 0;
#else #else
timespec start; timespec start;

View File

@ -1,11 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <magic_enum/magic_enum.hpp>
#include "common/elf_info.h" #include "common/elf_info.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/libraries/system/commondialog.h" #include "core/libraries/system/commondialog.h"
#include "magic_enum.hpp"
#include "savedatadialog.h" #include "savedatadialog.h"
#include "savedatadialog_ui.h" #include "savedatadialog_ui.h"

View File

@ -3,7 +3,7 @@
#include <fmt/chrono.h> #include <fmt/chrono.h>
#include <imgui.h> #include <imgui.h>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "common/elf_info.h" #include "common/elf_info.h"
#include "common/singleton.h" #include "common/singleton.h"

View File

@ -5,7 +5,7 @@
#include <mutex> #include <mutex>
#include <semaphore> #include <semaphore>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "save_backup.h" #include "save_backup.h"
#include "save_instance.h" #include "save_instance.h"
@ -79,7 +79,7 @@ static void backup(const std::filesystem::path& dir_name) {
} }
static void BackupThreadBody() { static void BackupThreadBody() {
Common::SetCurrentThreadName("shadPS4:SaveData_BackupThread"); Common::SetCurrentThreadName("shadPS4:SaveData:BackupThread");
while (g_backup_status != WorkerStatus::Stopping) { while (g_backup_status != WorkerStatus::Stopping) {
g_backup_status = WorkerStatus::Waiting; g_backup_status = WorkerStatus::Waiting;

View File

@ -3,7 +3,7 @@
#include <iostream> #include <iostream>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "common/assert.h" #include "common/assert.h"
#include "common/config.h" #include "common/config.h"

View File

@ -66,7 +66,7 @@ static void SaveFileSafe(void* buf, size_t count, const std::filesystem::path& p
} }
[[noreturn]] void SaveThreadLoop() { [[noreturn]] void SaveThreadLoop() {
Common::SetCurrentThreadName("shadPS4:SaveData_SaveDataMemoryThread"); Common::SetCurrentThreadName("shadPS4:SaveData:SaveDataMemoryThread");
std::mutex mtx; std::mutex mtx;
while (true) { while (true) {
{ {

View File

@ -5,7 +5,7 @@
#include <vector> #include <vector>
#include <core/libraries/system/msgdialog_ui.h> #include <core/libraries/system/msgdialog_ui.h>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "common/assert.h" #include "common/assert.h"
#include "common/cstring.h" #include "common/cstring.h"

View File

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

View File

@ -3,7 +3,7 @@
#define MAGIC_ENUM_RANGE_MIN 0 #define MAGIC_ENUM_RANGE_MIN 0
#define MAGIC_ENUM_RANGE_MAX 300 #define MAGIC_ENUM_RANGE_MAX 300
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"

View File

@ -7,16 +7,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/videodec/videodec_error.h" #include "core/libraries/videodec/videodec_error.h"
// The av_err2str macro in libavutil/error.h does not play nice with C++ #include "common/support/avdec.h"
#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
namespace Libraries::Vdec2 { namespace Libraries::Vdec2 {

View File

@ -8,16 +8,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/videodec/videodec_error.h" #include "core/libraries/videodec/videodec_error.h"
// The av_err2str macro in libavutil/error.h does not play nice with C++ #include "common/support/avdec.h"
#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
namespace Libraries::Videodec { namespace Libraries::Videodec {

View File

@ -85,6 +85,15 @@ public:
return m_modules.at(index).get(); return m_modules.at(index).get();
} }
u32 FindByName(const std::filesystem::path& name) const {
for (u32 i = 0; i < m_modules.size(); i++) {
if (name == m_modules[i]->file) {
return i;
}
}
return -1;
}
u32 MaxTlsIndex() const { u32 MaxTlsIndex() const {
return max_tls_index; return max_tls_index;
} }

View File

@ -96,12 +96,12 @@ PAddr MemoryManager::PoolExpand(PAddr search_start, PAddr search_end, size_t siz
PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment, PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment,
int memory_type) { int memory_type) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
alignment = alignment > 0 ? alignment : 16_KB;
auto dmem_area = FindDmemArea(search_start); auto dmem_area = FindDmemArea(search_start);
const auto is_suitable = [&] { const auto is_suitable = [&] {
const auto aligned_base = alignment > 0 ? Common::AlignUp(dmem_area->second.base, alignment) const auto aligned_base = Common::AlignUp(dmem_area->second.base, alignment);
: dmem_area->second.base;
const auto alignment_size = aligned_base - dmem_area->second.base; const auto alignment_size = aligned_base - dmem_area->second.base;
const auto remaining_size = const auto remaining_size =
dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0; dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0;
@ -114,7 +114,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
// Align free position // Align free position
PAddr free_addr = dmem_area->second.base; PAddr free_addr = dmem_area->second.base;
free_addr = alignment > 0 ? Common::AlignUp(free_addr, alignment) : free_addr; free_addr = Common::AlignUp(free_addr, alignment);
// Add the allocated region to the list and commit its pages. // Add the allocated region to the list and commit its pages.
auto& area = CarveDmemArea(free_addr, size)->second; auto& area = CarveDmemArea(free_addr, size)->second;
@ -328,7 +328,7 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
} }
// Map the file. // Map the file.
impl.MapFile(mapped_addr, size, offset, std::bit_cast<u32>(prot), fd); impl.MapFile(mapped_addr, size_aligned, offset, std::bit_cast<u32>(prot), fd);
// Add virtual memory area // Add virtual memory area
auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second; auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second;
@ -375,12 +375,12 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
TRACK_FREE(virtual_addr, "VMEM"); TRACK_FREE(virtual_addr, "VMEM");
} }
void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
UnmapMemoryImpl(virtual_addr, size); return UnmapMemoryImpl(virtual_addr, size);
} }
void MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) { s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) {
const auto it = FindVMA(virtual_addr); const auto it = FindVMA(virtual_addr);
const auto& vma_base = it->second; const auto& vma_base = it->second;
ASSERT_MSG(vma_base.Contains(virtual_addr, size), ASSERT_MSG(vma_base.Contains(virtual_addr, size),
@ -415,6 +415,8 @@ void MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) {
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec, impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec,
has_backing, readonly_file); has_backing, readonly_file);
TRACK_FREE(virtual_addr, "VMEM"); TRACK_FREE(virtual_addr, "VMEM");
return ORBIS_OK;
} }
int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* prot) { int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* prot) {
@ -512,9 +514,8 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags,
info->is_flexible.Assign(vma.type == VMAType::Flexible); info->is_flexible.Assign(vma.type == VMAType::Flexible);
info->is_direct.Assign(vma.type == VMAType::Direct); info->is_direct.Assign(vma.type == VMAType::Direct);
info->is_stack.Assign(vma.type == VMAType::Stack); info->is_stack.Assign(vma.type == VMAType::Stack);
info->is_pooled.Assign(vma.type == VMAType::Pooled); info->is_pooled.Assign(vma.type == VMAType::PoolReserved);
info->is_committed.Assign(vma.type != VMAType::Free && vma.type != VMAType::Reserved && info->is_committed.Assign(vma.type == VMAType::Pooled);
vma.type != VMAType::PoolReserved);
vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size())); vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size()));
if (vma.type == VMAType::Direct) { if (vma.type == VMAType::Direct) {
const auto dmem_it = FindDmemArea(vma.phys_base); const auto dmem_it = FindDmemArea(vma.phys_base);
@ -585,6 +586,13 @@ void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::strin
"Range provided is not fully contained in vma"); "Range provided is not fully contained in vma");
it->second.name = name; it->second.name = name;
} }
void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const {
if (rasterizer) {
rasterizer->InvalidateMemory(addr, size);
}
}
VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) { VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) {
// If the requested address is below the mapped range, start search from the lowest address // If the requested address is below the mapped range, start search from the lowest address
auto min_search_address = impl.SystemManagedVirtualBase(); auto min_search_address = impl.SystemManagedVirtualBase();
@ -691,7 +699,7 @@ MemoryManager::DMemHandle MemoryManager::Split(DMemHandle dmem_handle, size_t of
new_area.size -= offset_in_area; new_area.size -= offset_in_area;
return dmem_map.emplace_hint(std::next(dmem_handle), new_area.base, new_area); return dmem_map.emplace_hint(std::next(dmem_handle), new_area.base, new_area);
}; }
int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut, int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut,
void** directMemoryStartOut, void** directMemoryEndOut) { void** directMemoryStartOut, void** directMemoryEndOut) {

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