mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-05 00:42:48 +00:00
Merge branch 'main' into compat-gui
This commit is contained in:
commit
d43de282f7
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -102,6 +102,8 @@
|
||||
[submodule "externals/LibAtrac9"]
|
||||
path = externals/LibAtrac9
|
||||
url = https://github.com/shadps4-emu/ext-LibAtrac9.git
|
||||
shallow = true
|
||||
[submodule "externals/libpng"]
|
||||
path = externals/libpng
|
||||
url = https://github.com/pnggroup/libpng
|
||||
shallow = true
|
@ -113,13 +113,14 @@ find_package(FFmpeg 5.1.2 MODULE)
|
||||
find_package(fmt 10.2.0 CONFIG)
|
||||
find_package(glslang 15 CONFIG)
|
||||
find_package(half 1.12.0 MODULE)
|
||||
find_package(magic_enum 0.9.6 CONFIG)
|
||||
find_package(magic_enum 0.9.7 CONFIG)
|
||||
find_package(PNG 1.6 MODULE)
|
||||
find_package(RenderDoc 1.6.0 MODULE)
|
||||
find_package(SDL3 3.1.2 CONFIG)
|
||||
find_package(stb MODULE)
|
||||
find_package(toml11 4.2.0 CONFIG)
|
||||
find_package(tsl-robin-map 1.3.0 CONFIG)
|
||||
find_package(VulkanHeaders 1.3.289 CONFIG)
|
||||
find_package(VulkanHeaders 1.4.303 CONFIG)
|
||||
find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
|
||||
find_package(xbyak 7.07 CONFIG)
|
||||
find_package(xxHash 0.8.2 MODULE)
|
||||
@ -209,7 +210,10 @@ set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp
|
||||
src/core/libraries/gnmdriver/gnm_error.h
|
||||
)
|
||||
|
||||
set(KERNEL_LIB src/core/libraries/kernel/threads/condvar.cpp
|
||||
set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp
|
||||
src/core/libraries/kernel/sync/mutex.h
|
||||
src/core/libraries/kernel/sync/semaphore.h
|
||||
src/core/libraries/kernel/threads/condvar.cpp
|
||||
src/core/libraries/kernel/threads/event_flag.cpp
|
||||
src/core/libraries/kernel/threads/exception.cpp
|
||||
src/core/libraries/kernel/threads/exception.h
|
||||
@ -495,6 +499,8 @@ set(COMMON src/common/logging/backend.cpp
|
||||
src/common/slot_vector.h
|
||||
src/common/spin_lock.cpp
|
||||
src/common/spin_lock.h
|
||||
src/common/stb.cpp
|
||||
src/common/stb.h
|
||||
src/common/string_util.cpp
|
||||
src/common/string_util.h
|
||||
src/common/thread.cpp
|
||||
@ -502,6 +508,7 @@ set(COMMON src/common/logging/backend.cpp
|
||||
src/common/types.h
|
||||
src/common/uint128.h
|
||||
src/common/unique_function.h
|
||||
src/common/va_ctx.h
|
||||
src/common/version.h
|
||||
src/common/ntapi.h
|
||||
src/common/ntapi.cpp
|
||||
@ -526,6 +533,12 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||
src/core/crypto/crypto.cpp
|
||||
src/core/crypto/crypto.h
|
||||
src/core/crypto/keys.h
|
||||
src/core/devices/base_device.cpp
|
||||
src/core/devices/base_device.h
|
||||
src/core/devices/ioccom.h
|
||||
src/core/devices/logger.cpp
|
||||
src/core/devices/logger.h
|
||||
src/core/devices/nop_device.h
|
||||
src/core/file_format/pfs.h
|
||||
src/core/file_format/pkg.cpp
|
||||
src/core/file_format/pkg.h
|
||||
@ -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_scheduler.cpp
|
||||
src/video_core/renderer_vulkan/vk_scheduler.h
|
||||
src/video_core/renderer_vulkan/vk_shader_hle.cpp
|
||||
src/video_core/renderer_vulkan/vk_shader_hle.h
|
||||
src/video_core/renderer_vulkan/vk_shader_util.cpp
|
||||
src/video_core/renderer_vulkan/vk_shader_util.h
|
||||
src/video_core/renderer_vulkan/vk_swapchain.cpp
|
||||
@ -862,11 +877,19 @@ endif()
|
||||
create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers)
|
||||
|
||||
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
||||
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
|
||||
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC)
|
||||
endif()
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
option(USE_SYSTEM_VULKAN_LOADER "Enables using the system Vulkan loader instead of directly linking with MoltenVK. Useful for loading validation layers." OFF)
|
||||
if (USE_SYSTEM_VULKAN_LOADER)
|
||||
|
@ -12,12 +12,13 @@ path = [
|
||||
"dist/net.shadps4.shadPS4_metadata.pot",
|
||||
"dist/net.shadps4.shadPS4.metainfo.xml",
|
||||
"dist/net.shadps4.shadPS4.releases.xml",
|
||||
"documents/changelog.txt",
|
||||
"documents/changelog.md",
|
||||
"documents/Quickstart/2.png",
|
||||
"documents/Screenshots/*",
|
||||
"scripts/ps4_names.txt",
|
||||
"src/images/about_icon.png",
|
||||
"src/images/controller_icon.png",
|
||||
"src/images/discord.png",
|
||||
"src/images/dump_icon.png",
|
||||
"src/images/exit_icon.png",
|
||||
"src/images/file_icon.png",
|
||||
@ -28,8 +29,10 @@ path = [
|
||||
"src/images/flag_us.png",
|
||||
"src/images/flag_world.png",
|
||||
"src/images/folder_icon.png",
|
||||
"src/images/github.png",
|
||||
"src/images/grid_icon.png",
|
||||
"src/images/iconsize_icon.png",
|
||||
"src/images/ko-fi.png",
|
||||
"src/images/list_icon.png",
|
||||
"src/images/list_mode_icon.png",
|
||||
"src/images/pause_icon.png",
|
||||
@ -43,6 +46,8 @@ path = [
|
||||
"src/images/net.shadps4.shadPS4.svg",
|
||||
"src/images/themes_icon.png",
|
||||
"src/images/update_icon.png",
|
||||
"src/images/youtube.png",
|
||||
"src/images/website.png",
|
||||
"src/shadps4.qrc",
|
||||
"src/shadps4.rc",
|
||||
]
|
||||
@ -63,7 +68,7 @@ SPDX-FileCopyrightText = "2019-2024 Baldur Karlsson"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "externals/stb_image.h"
|
||||
path = "externals/stb/**"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2017 Sean Barrett"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
19
cmake/Findstb.cmake
Normal file
19
cmake/Findstb.cmake
Normal 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)
|
@ -25,8 +25,8 @@ Once you are within the installer:
|
||||
|
||||
Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead.
|
||||
|
||||
1. Under the current, non beta version of Qt (at the time of writing 6.7.2), select the option `MSVC 2019 64-bit` or similar.
|
||||
If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2019 ARM64` instead.
|
||||
1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar.
|
||||
If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead.
|
||||
|
||||
Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space.
|
||||
|
||||
@ -35,7 +35,7 @@ Beware, this requires you to create a Qt account. If you do not want to do this,
|
||||
Once you are finished, you will have to configure Qt within Visual Studio:
|
||||
|
||||
1. Tools -> Options -> Qt -> Versions
|
||||
2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.2\msvc2019_64`
|
||||
2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.3\msvc2022_64`
|
||||
3. Enable the default checkmark on the new version you just created.
|
||||
|
||||
### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win)
|
||||
@ -55,16 +55,16 @@ Go through the Git for Windows installation as normal
|
||||
3. If you want to build shadPS4 with the Qt Gui:
|
||||
1. Click x64-Clang-Release and select "Manage Configurations"
|
||||
2. Look for "CMake command arguments" and add to the text field
|
||||
`-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.2\msvc2019_64`
|
||||
`-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.3\msvc2022_64`
|
||||
(Change Qt path if you've installed it to non-default path)
|
||||
3. Press CTRL+S to save and wait a moment for CMake generation
|
||||
4. Change the project to build to shadps4.exe
|
||||
5. Build -> Build All
|
||||
|
||||
Your shadps4.exe will be in `c:\path\to\source\Build\x64-Clang-Release\`
|
||||
Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\`
|
||||
|
||||
To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal:
|
||||
`C:\Qt\6.7.2\msvc2019_64\bin\windeployqt.exe "c:\path\to\shadps4.exe"`
|
||||
`C:\Qt\6.7.3\msvc2022_64\bin\windeployqt.exe "C:\path\to\shadps4.exe"`
|
||||
(Change Qt path if you've installed it to non-default path)
|
||||
|
||||
## Option 2: MSYS2/MinGW
|
||||
@ -79,7 +79,7 @@ Normal x86-based computers, follow:
|
||||
|
||||
1. Open "MSYS2 MINGW64" from your new applications
|
||||
2. Run `pacman -Syu`, let it complete;
|
||||
3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg`
|
||||
3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-rapidjson mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg`
|
||||
1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools mingw-w64-x86_64-qt6-multimedia`
|
||||
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
|
||||
5. Run `cd shadPS4`
|
||||
@ -93,7 +93,7 @@ ARM64-based computers, follow:
|
||||
|
||||
1. Open "MSYS2 CLANGARM64" from your new applications
|
||||
2. Run `pacman -Syu`, let it complete;
|
||||
3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg`
|
||||
3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-rapidjson mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg`
|
||||
1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia`
|
||||
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
|
||||
5. Run `cd shadPS4`
|
||||
|
@ -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
|
||||
=================
|
||||
|
11
externals/CMakeLists.txt
vendored
11
externals/CMakeLists.txt
vendored
@ -35,7 +35,7 @@ else()
|
||||
if (NOT TARGET cryptopp::cryptopp)
|
||||
set(CRYPTOPP_INSTALL OFF)
|
||||
set(CRYPTOPP_BUILD_TESTING OFF)
|
||||
set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/)
|
||||
set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp)
|
||||
add_subdirectory(cryptopp-cmake)
|
||||
file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h")
|
||||
# remove externals/cryptopp from include directories because it contains a conflicting zlib.h file
|
||||
@ -216,9 +216,16 @@ endif()
|
||||
# Discord RPC
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
set(BUILD_EXAMPLES OFF)
|
||||
add_subdirectory(discord-rpc/)
|
||||
add_subdirectory(discord-rpc)
|
||||
target_include_directories(discord-rpc INTERFACE discord-rpc/include)
|
||||
endif()
|
||||
|
||||
# GCN Headers
|
||||
add_subdirectory(gcn)
|
||||
|
||||
# stb
|
||||
if (NOT TARGET stb::headers)
|
||||
add_library(stb INTERFACE)
|
||||
target_include_directories(stb INTERFACE stb)
|
||||
add_library(stb::headers ALIAS stb)
|
||||
endif()
|
||||
|
2
externals/LibAtrac9
vendored
2
externals/LibAtrac9
vendored
@ -1 +1 @@
|
||||
Subproject commit 3acdcdc78f129c2e6145331ff650fa76dd88d62c
|
||||
Subproject commit 9640129dc6f2afbca6ceeca3019856e8653a5fb2
|
2
externals/date
vendored
2
externals/date
vendored
@ -1 +1 @@
|
||||
Subproject commit dd8affc6de5755e07638bf0a14382d29549d6ee9
|
||||
Subproject commit 28b7b232521ace2c8ef3f2ad4126daec3569c14f
|
2
externals/ext-boost
vendored
2
externals/ext-boost
vendored
@ -1 +1 @@
|
||||
Subproject commit f2474e1b584fb7a3ed6f85ba875e6eacd742ec8a
|
||||
Subproject commit ca6f230e67be7cc45fc919057f07b2aee64dadc1
|
2
externals/glslang
vendored
2
externals/glslang
vendored
@ -1 +1 @@
|
||||
Subproject commit e61d7bb3006f451968714e2f653412081871e1ee
|
||||
Subproject commit a0995c49ebcaca2c6d3b03efbabf74f3843decdb
|
2
externals/magic_enum
vendored
2
externals/magic_enum
vendored
@ -1 +1 @@
|
||||
Subproject commit 126539e13cccdc2e75ce770e94f3c26403099fa5
|
||||
Subproject commit 1a1824df7ac798177a521eed952720681b0bf482
|
2
externals/pugixml
vendored
2
externals/pugixml
vendored
@ -1 +1 @@
|
||||
Subproject commit 3b17184379fcaaeb7f1fbe08018b7fedf2640b3b
|
||||
Subproject commit 4bc14418d12d289dd9978fdce9490a45deeb653e
|
2
externals/sdl3
vendored
2
externals/sdl3
vendored
@ -1 +1 @@
|
||||
Subproject commit 54e622c2e6af456bfef382fae44c17682d5ac88a
|
||||
Subproject commit 3a1d76d298db023f6cf37fb08ee766f20a4e12ab
|
2
externals/toml11
vendored
2
externals/toml11
vendored
@ -1 +1 @@
|
||||
Subproject commit f925e7f287c0008813c2294798cf9ca167fd9ffd
|
||||
Subproject commit 7f6c574ff5aa1053534e7e19c0a4f22bf4c6aaca
|
2
externals/vma
vendored
2
externals/vma
vendored
@ -1 +1 @@
|
||||
Subproject commit 1c35ba99ce775f8342d87a83a3f0f696f99c2a39
|
||||
Subproject commit 5a53a198945ba8260fbc58fadb788745ce6aa263
|
2
externals/vulkan-headers
vendored
2
externals/vulkan-headers
vendored
@ -1 +1 @@
|
||||
Subproject commit d91597a82f881d473887b560a03a7edf2720b72c
|
||||
Subproject commit 6a74a7d65cafa19e38ec116651436cce6efd5b2e
|
2
externals/xbyak
vendored
2
externals/xbyak
vendored
@ -1 +1 @@
|
||||
Subproject commit d067f0d3f55696ae8bc9a25ad7012ee80f221d54
|
||||
Subproject commit 4e44f4614ddbf038f2a6296f5b906d5c72691e0f
|
2
externals/xxhash
vendored
2
externals/xxhash
vendored
@ -1 +1 @@
|
||||
Subproject commit d4ad85e4afaad5c780f54db1dc967fff5a869ffd
|
||||
Subproject commit 2bf8313b934633b2a5b7e8fd239645b85e10c852
|
2
externals/zydis
vendored
2
externals/zydis
vendored
@ -1 +1 @@
|
||||
Subproject commit 9d298eb8067ff62a237203d1e1470785033e185c
|
||||
Subproject commit bffbb610cfea643b98e87658b9058382f7522807
|
@ -743,6 +743,7 @@ void setDefaultValues() {
|
||||
emulator_language = "en";
|
||||
m_language = 1;
|
||||
gpuId = -1;
|
||||
separateupdatefolder = false;
|
||||
}
|
||||
|
||||
} // namespace Config
|
||||
|
@ -377,6 +377,7 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (False(file_access_mode & (FileAccessMode::Write | FileAccessMode::Append))) {
|
||||
u64 size = GetSize();
|
||||
if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) {
|
||||
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
|
||||
@ -388,6 +389,7 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
|
||||
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "common/concepts.h"
|
||||
#include "common/types.h"
|
||||
#include "enum.h"
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
@ -42,6 +43,7 @@ enum class FileAccessMode {
|
||||
*/
|
||||
ReadAppend = Read | Append,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode);
|
||||
|
||||
enum class FileType {
|
||||
BinaryFile,
|
||||
|
@ -69,6 +69,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Common, Memory) \
|
||||
CLS(Core) \
|
||||
SUB(Core, Linker) \
|
||||
SUB(Core, Devices) \
|
||||
CLS(Config) \
|
||||
CLS(Debug) \
|
||||
CLS(Kernel) \
|
||||
|
@ -35,6 +35,7 @@ enum class Class : u8 {
|
||||
Common_Memory, ///< Memory mapping and management functions
|
||||
Core, ///< LLE emulation core
|
||||
Core_Linker, ///< The module linker
|
||||
Core_Devices, ///< Devices emulation
|
||||
Config, ///< Emulator configuration (including commandline)
|
||||
Debug, ///< Debugging tools
|
||||
Kernel, ///< The HLE implementation of the PS4 kernel.
|
||||
|
@ -5,8 +5,11 @@
|
||||
|
||||
#include "ntapi.h"
|
||||
|
||||
NtDelayExecution_t NtDelayExecution = nullptr;
|
||||
NtClose_t NtClose = nullptr;
|
||||
NtSetInformationFile_t NtSetInformationFile = nullptr;
|
||||
NtCreateThread_t NtCreateThread = nullptr;
|
||||
NtTerminateThread_t NtTerminateThread = nullptr;
|
||||
NtQueueApcThreadEx_t NtQueueApcThreadEx = nullptr;
|
||||
|
||||
namespace Common::NtApi {
|
||||
|
||||
@ -14,9 +17,12 @@ void Initialize() {
|
||||
HMODULE nt_handle = GetModuleHandleA("ntdll.dll");
|
||||
|
||||
// http://stackoverflow.com/a/31411628/4725495
|
||||
NtDelayExecution = (NtDelayExecution_t)GetProcAddress(nt_handle, "NtDelayExecution");
|
||||
NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose");
|
||||
NtSetInformationFile =
|
||||
(NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile");
|
||||
NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread");
|
||||
NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread");
|
||||
NtQueueApcThreadEx = (NtQueueApcThreadEx_t)GetProcAddress(nt_handle, "NtQueueApcThreadEx");
|
||||
}
|
||||
|
||||
} // namespace Common::NtApi
|
||||
|
@ -108,14 +108,444 @@ typedef struct _FILE_DISPOSITION_INFORMATION {
|
||||
BOOLEAN DeleteFile;
|
||||
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
|
||||
|
||||
typedef u32(__stdcall* NtDelayExecution_t)(BOOL Alertable, PLARGE_INTEGER DelayInterval);
|
||||
typedef struct _UNICODE_STRING {
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWCH Buffer;
|
||||
} UNICODE_STRING, *PUNICODE_STRING;
|
||||
|
||||
typedef u32(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
|
||||
typedef const UNICODE_STRING* PCUNICODE_STRING;
|
||||
|
||||
typedef struct _OBJECT_ATTRIBUTES {
|
||||
ULONG Length;
|
||||
HANDLE RootDirectory;
|
||||
PCUNICODE_STRING ObjectName;
|
||||
ULONG Attributes;
|
||||
PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR;
|
||||
PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE
|
||||
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
|
||||
|
||||
typedef const OBJECT_ATTRIBUTES* PCOBJECT_ATTRIBUTES;
|
||||
|
||||
typedef struct _CLIENT_ID {
|
||||
HANDLE UniqueProcess;
|
||||
HANDLE UniqueThread;
|
||||
} CLIENT_ID, *PCLIENT_ID;
|
||||
|
||||
typedef struct _INITIAL_TEB {
|
||||
struct {
|
||||
PVOID OldStackBase;
|
||||
PVOID OldStackLimit;
|
||||
} OldInitialTeb;
|
||||
PVOID StackBase;
|
||||
PVOID StackLimit;
|
||||
PVOID StackAllocationBase;
|
||||
} INITIAL_TEB, *PINITIAL_TEB;
|
||||
|
||||
typedef struct _PEB_LDR_DATA {
|
||||
ULONG Length;
|
||||
BOOLEAN Initialized;
|
||||
PVOID SsHandle;
|
||||
LIST_ENTRY InLoadOrderModuleList;
|
||||
LIST_ENTRY InMemoryOrderModuleList;
|
||||
LIST_ENTRY InInitializationOrderModuleList;
|
||||
PVOID EntryInProgress;
|
||||
BOOLEAN ShutdownInProgress;
|
||||
HANDLE ShutdownThreadId;
|
||||
} PEB_LDR_DATA, *PPEB_LDR_DATA;
|
||||
|
||||
typedef struct _CURDIR {
|
||||
UNICODE_STRING DosPath;
|
||||
PVOID Handle;
|
||||
} CURDIR, *PCURDIR;
|
||||
|
||||
typedef struct RTL_DRIVE_LETTER_CURDIR {
|
||||
USHORT Flags;
|
||||
USHORT Length;
|
||||
ULONG TimeStamp;
|
||||
UNICODE_STRING DosPath;
|
||||
} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
|
||||
|
||||
typedef struct _RTL_USER_PROCESS_PARAMETERS {
|
||||
ULONG AllocationSize;
|
||||
ULONG Size;
|
||||
ULONG Flags;
|
||||
ULONG DebugFlags;
|
||||
HANDLE ConsoleHandle;
|
||||
ULONG ConsoleFlags;
|
||||
HANDLE hStdInput;
|
||||
HANDLE hStdOutput;
|
||||
HANDLE hStdError;
|
||||
CURDIR CurrentDirectory;
|
||||
UNICODE_STRING DllPath;
|
||||
UNICODE_STRING ImagePathName;
|
||||
UNICODE_STRING CommandLine;
|
||||
PWSTR Environment;
|
||||
ULONG dwX;
|
||||
ULONG dwY;
|
||||
ULONG dwXSize;
|
||||
ULONG dwYSize;
|
||||
ULONG dwXCountChars;
|
||||
ULONG dwYCountChars;
|
||||
ULONG dwFillAttribute;
|
||||
ULONG dwFlags;
|
||||
ULONG wShowWindow;
|
||||
UNICODE_STRING WindowTitle;
|
||||
UNICODE_STRING Desktop;
|
||||
UNICODE_STRING ShellInfo;
|
||||
UNICODE_STRING RuntimeInfo;
|
||||
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
|
||||
ULONG_PTR EnvironmentSize;
|
||||
ULONG_PTR EnvironmentVersion;
|
||||
PVOID PackageDependencyData;
|
||||
ULONG ProcessGroupId;
|
||||
ULONG LoaderThreads;
|
||||
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
|
||||
|
||||
typedef struct tagRTL_BITMAP {
|
||||
ULONG SizeOfBitMap;
|
||||
PULONG Buffer;
|
||||
} RTL_BITMAP, *PRTL_BITMAP;
|
||||
|
||||
typedef struct {
|
||||
UINT next;
|
||||
UINT id;
|
||||
ULONGLONG addr;
|
||||
ULONGLONG size;
|
||||
UINT args[4];
|
||||
} CROSS_PROCESS_WORK_ENTRY;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
UINT first;
|
||||
UINT counter;
|
||||
};
|
||||
volatile LONGLONG hdr;
|
||||
} CROSS_PROCESS_WORK_HDR;
|
||||
|
||||
typedef struct {
|
||||
CROSS_PROCESS_WORK_HDR free_list;
|
||||
CROSS_PROCESS_WORK_HDR work_list;
|
||||
ULONGLONG unknown[4];
|
||||
CROSS_PROCESS_WORK_ENTRY entries[1];
|
||||
} CROSS_PROCESS_WORK_LIST;
|
||||
|
||||
typedef struct _CHPEV2_PROCESS_INFO {
|
||||
ULONG Wow64ExecuteFlags; /* 000 */
|
||||
USHORT NativeMachineType; /* 004 */
|
||||
USHORT EmulatedMachineType; /* 006 */
|
||||
HANDLE SectionHandle; /* 008 */
|
||||
CROSS_PROCESS_WORK_LIST* CrossProcessWorkList; /* 010 */
|
||||
void* unknown; /* 018 */
|
||||
} CHPEV2_PROCESS_INFO, *PCHPEV2_PROCESS_INFO;
|
||||
|
||||
typedef u64(__stdcall* KERNEL_CALLBACK_PROC)(void*, ULONG);
|
||||
|
||||
typedef struct _PEB { /* win32/win64 */
|
||||
BOOLEAN InheritedAddressSpace; /* 000/000 */
|
||||
BOOLEAN ReadImageFileExecOptions; /* 001/001 */
|
||||
BOOLEAN BeingDebugged; /* 002/002 */
|
||||
UCHAR ImageUsedLargePages : 1; /* 003/003 */
|
||||
UCHAR IsProtectedProcess : 1;
|
||||
UCHAR IsImageDynamicallyRelocated : 1;
|
||||
UCHAR SkipPatchingUser32Forwarders : 1;
|
||||
UCHAR IsPackagedProcess : 1;
|
||||
UCHAR IsAppContainer : 1;
|
||||
UCHAR IsProtectedProcessLight : 1;
|
||||
UCHAR IsLongPathAwareProcess : 1;
|
||||
HANDLE Mutant; /* 004/008 */
|
||||
HMODULE ImageBaseAddress; /* 008/010 */
|
||||
PPEB_LDR_DATA LdrData; /* 00c/018 */
|
||||
RTL_USER_PROCESS_PARAMETERS* ProcessParameters; /* 010/020 */
|
||||
PVOID SubSystemData; /* 014/028 */
|
||||
HANDLE ProcessHeap; /* 018/030 */
|
||||
PRTL_CRITICAL_SECTION FastPebLock; /* 01c/038 */
|
||||
PVOID AtlThunkSListPtr; /* 020/040 */
|
||||
PVOID IFEOKey; /* 024/048 */
|
||||
ULONG ProcessInJob : 1; /* 028/050 */
|
||||
ULONG ProcessInitializing : 1;
|
||||
ULONG ProcessUsingVEH : 1;
|
||||
ULONG ProcessUsingVCH : 1;
|
||||
ULONG ProcessUsingFTH : 1;
|
||||
ULONG ProcessPreviouslyThrottled : 1;
|
||||
ULONG ProcessCurrentlyThrottled : 1;
|
||||
ULONG ProcessImagesHotPatched : 1;
|
||||
ULONG ReservedBits0 : 24;
|
||||
KERNEL_CALLBACK_PROC* KernelCallbackTable; /* 02c/058 */
|
||||
ULONG Reserved; /* 030/060 */
|
||||
ULONG AtlThunkSListPtr32; /* 034/064 */
|
||||
PVOID ApiSetMap; /* 038/068 */
|
||||
ULONG TlsExpansionCounter; /* 03c/070 */
|
||||
PRTL_BITMAP TlsBitmap; /* 040/078 */
|
||||
ULONG TlsBitmapBits[2]; /* 044/080 */
|
||||
PVOID ReadOnlySharedMemoryBase; /* 04c/088 */
|
||||
PVOID SharedData; /* 050/090 */
|
||||
PVOID* ReadOnlyStaticServerData; /* 054/098 */
|
||||
PVOID AnsiCodePageData; /* 058/0a0 */
|
||||
PVOID OemCodePageData; /* 05c/0a8 */
|
||||
PVOID UnicodeCaseTableData; /* 060/0b0 */
|
||||
ULONG NumberOfProcessors; /* 064/0b8 */
|
||||
ULONG NtGlobalFlag; /* 068/0bc */
|
||||
LARGE_INTEGER CriticalSectionTimeout; /* 070/0c0 */
|
||||
SIZE_T HeapSegmentReserve; /* 078/0c8 */
|
||||
SIZE_T HeapSegmentCommit; /* 07c/0d0 */
|
||||
SIZE_T HeapDeCommitTotalFreeThreshold; /* 080/0d8 */
|
||||
SIZE_T HeapDeCommitFreeBlockThreshold; /* 084/0e0 */
|
||||
ULONG NumberOfHeaps; /* 088/0e8 */
|
||||
ULONG MaximumNumberOfHeaps; /* 08c/0ec */
|
||||
PVOID* ProcessHeaps; /* 090/0f0 */
|
||||
PVOID GdiSharedHandleTable; /* 094/0f8 */
|
||||
PVOID ProcessStarterHelper; /* 098/100 */
|
||||
PVOID GdiDCAttributeList; /* 09c/108 */
|
||||
PVOID LoaderLock; /* 0a0/110 */
|
||||
ULONG OSMajorVersion; /* 0a4/118 */
|
||||
ULONG OSMinorVersion; /* 0a8/11c */
|
||||
ULONG OSBuildNumber; /* 0ac/120 */
|
||||
ULONG OSPlatformId; /* 0b0/124 */
|
||||
ULONG ImageSubSystem; /* 0b4/128 */
|
||||
ULONG ImageSubSystemMajorVersion; /* 0b8/12c */
|
||||
ULONG ImageSubSystemMinorVersion; /* 0bc/130 */
|
||||
KAFFINITY ActiveProcessAffinityMask; /* 0c0/138 */
|
||||
#ifdef _WIN64
|
||||
ULONG GdiHandleBuffer[60]; /* /140 */
|
||||
#else
|
||||
ULONG GdiHandleBuffer[34]; /* 0c4/ */
|
||||
#endif
|
||||
PVOID PostProcessInitRoutine; /* 14c/230 */
|
||||
PRTL_BITMAP TlsExpansionBitmap; /* 150/238 */
|
||||
ULONG TlsExpansionBitmapBits[32]; /* 154/240 */
|
||||
ULONG SessionId; /* 1d4/2c0 */
|
||||
ULARGE_INTEGER AppCompatFlags; /* 1d8/2c8 */
|
||||
ULARGE_INTEGER AppCompatFlagsUser; /* 1e0/2d0 */
|
||||
PVOID ShimData; /* 1e8/2d8 */
|
||||
PVOID AppCompatInfo; /* 1ec/2e0 */
|
||||
UNICODE_STRING CSDVersion; /* 1f0/2e8 */
|
||||
PVOID ActivationContextData; /* 1f8/2f8 */
|
||||
PVOID ProcessAssemblyStorageMap; /* 1fc/300 */
|
||||
PVOID SystemDefaultActivationData; /* 200/308 */
|
||||
PVOID SystemAssemblyStorageMap; /* 204/310 */
|
||||
SIZE_T MinimumStackCommit; /* 208/318 */
|
||||
PVOID* FlsCallback; /* 20c/320 */
|
||||
LIST_ENTRY FlsListHead; /* 210/328 */
|
||||
union {
|
||||
PRTL_BITMAP FlsBitmap; /* 218/338 */
|
||||
#ifdef _WIN64
|
||||
CHPEV2_PROCESS_INFO* ChpeV2ProcessInfo; /* /338 */
|
||||
#endif
|
||||
};
|
||||
ULONG FlsBitmapBits[4]; /* 21c/340 */
|
||||
ULONG FlsHighIndex; /* 22c/350 */
|
||||
PVOID WerRegistrationData; /* 230/358 */
|
||||
PVOID WerShipAssertPtr; /* 234/360 */
|
||||
PVOID EcCodeBitMap; /* 238/368 */
|
||||
PVOID pImageHeaderHash; /* 23c/370 */
|
||||
ULONG HeapTracingEnabled : 1; /* 240/378 */
|
||||
ULONG CritSecTracingEnabled : 1;
|
||||
ULONG LibLoaderTracingEnabled : 1;
|
||||
ULONG SpareTracingBits : 29;
|
||||
ULONGLONG CsrServerReadOnlySharedMemoryBase; /* 248/380 */
|
||||
ULONG TppWorkerpListLock; /* 250/388 */
|
||||
LIST_ENTRY TppWorkerpList; /* 254/390 */
|
||||
PVOID WaitOnAddressHashTable[0x80]; /* 25c/3a0 */
|
||||
PVOID TelemetryCoverageHeader; /* 45c/7a0 */
|
||||
ULONG CloudFileFlags; /* 460/7a8 */
|
||||
ULONG CloudFileDiagFlags; /* 464/7ac */
|
||||
CHAR PlaceholderCompatibilityMode; /* 468/7b0 */
|
||||
CHAR PlaceholderCompatibilityModeReserved[7]; /* 469/7b1 */
|
||||
PVOID LeapSecondData; /* 470/7b8 */
|
||||
ULONG LeapSecondFlags; /* 474/7c0 */
|
||||
ULONG NtGlobalFlag2; /* 478/7c4 */
|
||||
} PEB, *PPEB;
|
||||
|
||||
typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME {
|
||||
struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous;
|
||||
struct _ACTIVATION_CONTEXT* ActivationContext;
|
||||
ULONG Flags;
|
||||
} RTL_ACTIVATION_CONTEXT_STACK_FRAME, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME;
|
||||
|
||||
typedef struct _ACTIVATION_CONTEXT_STACK {
|
||||
RTL_ACTIVATION_CONTEXT_STACK_FRAME* ActiveFrame;
|
||||
LIST_ENTRY FrameListCache;
|
||||
ULONG Flags;
|
||||
ULONG NextCookieSequenceNumber;
|
||||
ULONG_PTR StackId;
|
||||
} ACTIVATION_CONTEXT_STACK, *PACTIVATION_CONTEXT_STACK;
|
||||
|
||||
typedef struct _GDI_TEB_BATCH {
|
||||
ULONG Offset;
|
||||
HANDLE HDC;
|
||||
ULONG Buffer[0x136];
|
||||
} GDI_TEB_BATCH;
|
||||
|
||||
typedef struct _TEB_ACTIVE_FRAME_CONTEXT {
|
||||
ULONG Flags;
|
||||
const char* FrameName;
|
||||
} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT;
|
||||
|
||||
typedef struct _TEB_ACTIVE_FRAME {
|
||||
ULONG Flags;
|
||||
struct _TEB_ACTIVE_FRAME* Previous;
|
||||
TEB_ACTIVE_FRAME_CONTEXT* Context;
|
||||
} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME;
|
||||
|
||||
typedef struct _TEB { /* win32/win64 */
|
||||
NT_TIB Tib; /* 000/0000 */
|
||||
PVOID EnvironmentPointer; /* 01c/0038 */
|
||||
CLIENT_ID ClientId; /* 020/0040 */
|
||||
PVOID ActiveRpcHandle; /* 028/0050 */
|
||||
PVOID ThreadLocalStoragePointer; /* 02c/0058 */
|
||||
PPEB Peb; /* 030/0060 */
|
||||
ULONG LastErrorValue; /* 034/0068 */
|
||||
ULONG CountOfOwnedCriticalSections; /* 038/006c */
|
||||
PVOID CsrClientThread; /* 03c/0070 */
|
||||
PVOID Win32ThreadInfo; /* 040/0078 */
|
||||
ULONG User32Reserved[26]; /* 044/0080 */
|
||||
ULONG UserReserved[5]; /* 0ac/00e8 */
|
||||
PVOID WOW32Reserved; /* 0c0/0100 */
|
||||
ULONG CurrentLocale; /* 0c4/0108 */
|
||||
ULONG FpSoftwareStatusRegister; /* 0c8/010c */
|
||||
PVOID ReservedForDebuggerInstrumentation[16]; /* 0cc/0110 */
|
||||
#ifdef _WIN64
|
||||
PVOID SystemReserved1[30]; /* /0190 */
|
||||
#else
|
||||
PVOID SystemReserved1[26]; /* 10c/ */
|
||||
#endif
|
||||
char PlaceholderCompatibilityMode; /* 174/0280 */
|
||||
BOOLEAN PlaceholderHydrationAlwaysExplicit; /* 175/0281 */
|
||||
char PlaceholderReserved[10]; /* 176/0282 */
|
||||
DWORD ProxiedProcessId; /* 180/028c */
|
||||
ACTIVATION_CONTEXT_STACK ActivationContextStack; /* 184/0290 */
|
||||
UCHAR WorkingOnBehalfOfTicket[8]; /* 19c/02b8 */
|
||||
LONG ExceptionCode; /* 1a4/02c0 */
|
||||
ACTIVATION_CONTEXT_STACK* ActivationContextStackPointer; /* 1a8/02c8 */
|
||||
ULONG_PTR InstrumentationCallbackSp; /* 1ac/02d0 */
|
||||
ULONG_PTR InstrumentationCallbackPreviousPc; /* 1b0/02d8 */
|
||||
ULONG_PTR InstrumentationCallbackPreviousSp; /* 1b4/02e0 */
|
||||
#ifdef _WIN64
|
||||
ULONG TxFsContext; /* /02e8 */
|
||||
BOOLEAN InstrumentationCallbackDisabled; /* /02ec */
|
||||
BOOLEAN UnalignedLoadStoreExceptions; /* /02ed */
|
||||
#else
|
||||
BOOLEAN InstrumentationCallbackDisabled; /* 1b8/ */
|
||||
BYTE SpareBytes1[23]; /* 1b9/ */
|
||||
ULONG TxFsContext; /* 1d0/ */
|
||||
#endif
|
||||
GDI_TEB_BATCH GdiTebBatch; /* 1d4/02f0 */
|
||||
CLIENT_ID RealClientId; /* 6b4/07d8 */
|
||||
HANDLE GdiCachedProcessHandle; /* 6bc/07e8 */
|
||||
ULONG GdiClientPID; /* 6c0/07f0 */
|
||||
ULONG GdiClientTID; /* 6c4/07f4 */
|
||||
PVOID GdiThreadLocaleInfo; /* 6c8/07f8 */
|
||||
ULONG_PTR Win32ClientInfo[62]; /* 6cc/0800 */
|
||||
PVOID glDispatchTable[233]; /* 7c4/09f0 */
|
||||
PVOID glReserved1[29]; /* b68/1138 */
|
||||
PVOID glReserved2; /* bdc/1220 */
|
||||
PVOID glSectionInfo; /* be0/1228 */
|
||||
PVOID glSection; /* be4/1230 */
|
||||
PVOID glTable; /* be8/1238 */
|
||||
PVOID glCurrentRC; /* bec/1240 */
|
||||
PVOID glContext; /* bf0/1248 */
|
||||
ULONG LastStatusValue; /* bf4/1250 */
|
||||
UNICODE_STRING StaticUnicodeString; /* bf8/1258 */
|
||||
WCHAR StaticUnicodeBuffer[261]; /* c00/1268 */
|
||||
PVOID DeallocationStack; /* e0c/1478 */
|
||||
PVOID TlsSlots[64]; /* e10/1480 */
|
||||
LIST_ENTRY TlsLinks; /* f10/1680 */
|
||||
PVOID Vdm; /* f18/1690 */
|
||||
PVOID ReservedForNtRpc; /* f1c/1698 */
|
||||
PVOID DbgSsReserved[2]; /* f20/16a0 */
|
||||
ULONG HardErrorMode; /* f28/16b0 */
|
||||
#ifdef _WIN64
|
||||
PVOID Instrumentation[11]; /* /16b8 */
|
||||
#else
|
||||
PVOID Instrumentation[9]; /* f2c/ */
|
||||
#endif
|
||||
GUID ActivityId; /* f50/1710 */
|
||||
PVOID SubProcessTag; /* f60/1720 */
|
||||
PVOID PerflibData; /* f64/1728 */
|
||||
PVOID EtwTraceData; /* f68/1730 */
|
||||
PVOID WinSockData; /* f6c/1738 */
|
||||
ULONG GdiBatchCount; /* f70/1740 */
|
||||
ULONG IdealProcessorValue; /* f74/1744 */
|
||||
ULONG GuaranteedStackBytes; /* f78/1748 */
|
||||
PVOID ReservedForPerf; /* f7c/1750 */
|
||||
PVOID ReservedForOle; /* f80/1758 */
|
||||
ULONG WaitingOnLoaderLock; /* f84/1760 */
|
||||
PVOID SavedPriorityState; /* f88/1768 */
|
||||
ULONG_PTR ReservedForCodeCoverage; /* f8c/1770 */
|
||||
PVOID ThreadPoolData; /* f90/1778 */
|
||||
PVOID* TlsExpansionSlots; /* f94/1780 */
|
||||
#ifdef _WIN64
|
||||
union {
|
||||
PVOID DeallocationBStore; /* /1788 */
|
||||
PVOID* ChpeV2CpuAreaInfo; /* /1788 */
|
||||
} DUMMYUNIONNAME;
|
||||
PVOID BStoreLimit; /* /1790 */
|
||||
#endif
|
||||
ULONG MuiGeneration; /* f98/1798 */
|
||||
ULONG IsImpersonating; /* f9c/179c */
|
||||
PVOID NlsCache; /* fa0/17a0 */
|
||||
PVOID ShimData; /* fa4/17a8 */
|
||||
ULONG HeapVirtualAffinity; /* fa8/17b0 */
|
||||
PVOID CurrentTransactionHandle; /* fac/17b8 */
|
||||
TEB_ACTIVE_FRAME* ActiveFrame; /* fb0/17c0 */
|
||||
PVOID* FlsSlots; /* fb4/17c8 */
|
||||
PVOID PreferredLanguages; /* fb8/17d0 */
|
||||
PVOID UserPrefLanguages; /* fbc/17d8 */
|
||||
PVOID MergedPrefLanguages; /* fc0/17e0 */
|
||||
ULONG MuiImpersonation; /* fc4/17e8 */
|
||||
USHORT CrossTebFlags; /* fc8/17ec */
|
||||
USHORT SameTebFlags; /* fca/17ee */
|
||||
PVOID TxnScopeEnterCallback; /* fcc/17f0 */
|
||||
PVOID TxnScopeExitCallback; /* fd0/17f8 */
|
||||
PVOID TxnScopeContext; /* fd4/1800 */
|
||||
ULONG LockCount; /* fd8/1808 */
|
||||
LONG WowTebOffset; /* fdc/180c */
|
||||
PVOID ResourceRetValue; /* fe0/1810 */
|
||||
PVOID ReservedForWdf; /* fe4/1818 */
|
||||
ULONGLONG ReservedForCrt; /* fe8/1820 */
|
||||
GUID EffectiveContainerId; /* ff0/1828 */
|
||||
} TEB, *PTEB;
|
||||
static_assert(offsetof(TEB, DeallocationStack) ==
|
||||
0x1478); /* The only member we care about at the moment */
|
||||
|
||||
typedef enum _QUEUE_USER_APC_FLAGS {
|
||||
QueueUserApcFlagsNone,
|
||||
QueueUserApcFlagsSpecialUserApc,
|
||||
QueueUserApcFlagsMaxValue
|
||||
} QUEUE_USER_APC_FLAGS;
|
||||
|
||||
typedef union _USER_APC_OPTION {
|
||||
ULONG_PTR UserApcFlags;
|
||||
HANDLE MemoryReserveHandle;
|
||||
} USER_APC_OPTION, *PUSER_APC_OPTION;
|
||||
|
||||
using PPS_APC_ROUTINE = void (*)(PVOID ApcArgument1, PVOID ApcArgument2, PVOID ApcArgument3,
|
||||
PCONTEXT Context);
|
||||
|
||||
typedef u64(__stdcall* NtClose_t)(HANDLE Handle);
|
||||
|
||||
typedef u64(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID FileInformation, ULONG Length,
|
||||
FILE_INFORMATION_CLASS FileInformationClass);
|
||||
|
||||
extern NtDelayExecution_t NtDelayExecution;
|
||||
typedef u64(__stdcall* NtCreateThread_t)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess,
|
||||
PCOBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle,
|
||||
PCLIENT_ID ClientId, PCONTEXT ThreadContext,
|
||||
PINITIAL_TEB InitialTeb, BOOLEAN CreateSuspended);
|
||||
|
||||
typedef u64(__stdcall* NtTerminateThread_t)(HANDLE ThreadHandle, u64 ExitStatus);
|
||||
|
||||
typedef u64(__stdcall* NtQueueApcThreadEx_t)(HANDLE ThreadHandle,
|
||||
USER_APC_OPTION UserApcReserveHandle,
|
||||
PPS_APC_ROUTINE ApcRoutine, PVOID ApcArgument1,
|
||||
PVOID ApcArgument2, PVOID ApcArgument3);
|
||||
|
||||
extern NtClose_t NtClose;
|
||||
extern NtSetInformationFile_t NtSetInformationFile;
|
||||
extern NtCreateThread_t NtCreateThread;
|
||||
extern NtTerminateThread_t NtTerminateThread;
|
||||
extern NtQueueApcThreadEx_t NtQueueApcThreadEx;
|
||||
|
||||
namespace Common::NtApi {
|
||||
void Initialize();
|
||||
|
7
src/common/stb.cpp
Normal file
7
src/common/stb.cpp
Normal 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
6
src/common/stb.h
Normal 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>
|
@ -37,6 +37,10 @@ std::vector<std::string> SplitString(const std::string& str, char delimiter) {
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string_view U8stringToString(std::u8string_view u8str) {
|
||||
return std::string_view{reinterpret_cast<const char*>(u8str.data()), u8str.size()};
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::wstring CPToUTF16(u32 code_page, std::string_view input) {
|
||||
const auto size =
|
||||
|
@ -16,6 +16,8 @@ void ToLowerInPlace(std::string& str);
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& str, char delimiter);
|
||||
|
||||
std::string_view U8stringToString(std::u8string_view u8str);
|
||||
|
||||
#ifdef _WIN32
|
||||
[[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input);
|
||||
[[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str);
|
||||
|
17
src/common/support/avdec.h
Normal file
17
src/common/support/avdec.h
Normal 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
|
@ -147,6 +147,10 @@ void SetCurrentThreadName(const char* name) {
|
||||
SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data());
|
||||
}
|
||||
|
||||
void SetThreadName(void* thread, const char* name) {
|
||||
SetThreadDescription(thread, UTF8ToUTF16W(name).data());
|
||||
}
|
||||
|
||||
#else // !MSVC_VER, so must be POSIX threads
|
||||
|
||||
// MinGW with the POSIX threading model does not support pthread_setname_np
|
||||
@ -170,11 +174,19 @@ void SetCurrentThreadName(const char* name) {
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetThreadName(void* thread, const char* name) {
|
||||
// TODO
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
void SetCurrentThreadName(const char*) {
|
||||
// Do Nothing on MingW
|
||||
// Do Nothing on MinGW
|
||||
}
|
||||
|
||||
void SetThreadName(void* thread, const char* name) {
|
||||
// Do Nothing on MinGW
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -23,6 +23,8 @@ void SetCurrentThreadPriority(ThreadPriority new_priority);
|
||||
|
||||
void SetCurrentThreadName(const char* name);
|
||||
|
||||
void SetThreadName(void* thread, const char* name);
|
||||
|
||||
class AccurateTimer {
|
||||
std::chrono::nanoseconds target_interval{};
|
||||
std::chrono::nanoseconds total_wait{};
|
||||
|
111
src/common/va_ctx.h
Normal file
111
src/common/va_ctx.h
Normal 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
|
@ -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,
|
||||
std::span<const u32> raw_code) {
|
||||
shader_dump_list.emplace_back(name, std::vector<u32>{spv.begin(), spv.end()},
|
||||
std::vector<u32>{raw_code.begin(), raw_code.end()});
|
||||
std::ranges::sort(shader_dump_list, {}, &ShaderDump::name);
|
||||
void DebugStateImpl::CollectShader(const std::string& name, vk::ShaderModule module,
|
||||
std::span<const u32> spv, std::span<const u32> raw_code,
|
||||
std::span<const u32> patch_spv, bool is_patched) {
|
||||
shader_dump_list.emplace_back(name, module, std::vector<u32>{spv.begin(), spv.end()},
|
||||
std::vector<u32>{raw_code.begin(), raw_code.end()},
|
||||
std::vector<u32>{patch_spv.begin(), patch_spv.end()}, is_patched);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#include "common/types.h"
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
@ -76,29 +76,46 @@ struct FrameDump {
|
||||
|
||||
struct ShaderDump {
|
||||
std::string name;
|
||||
vk::ShaderModule module;
|
||||
|
||||
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_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)
|
||||
: name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {}
|
||||
ShaderDump(std::string name, vk::ShaderModule module, std::vector<u32> spv,
|
||||
std::vector<u32> isa, std::vector<u32> patch_spv, bool is_patched)
|
||||
: name(std::move(name)), module(module), spv(std::move(spv)), isa(std::move(isa)),
|
||||
patch_spv(std::move(patch_spv)), is_patched(is_patched) {}
|
||||
|
||||
ShaderDump(const ShaderDump& other) = delete;
|
||||
ShaderDump(ShaderDump&& other) noexcept
|
||||
: name{std::move(other.name)}, spv{std::move(other.spv)},
|
||||
raw_code{std::move(other.raw_code)}, cache_spv_disasm{std::move(other.cache_spv_disasm)},
|
||||
cache_raw_disasm{std::move(other.cache_raw_disasm)} {}
|
||||
: name{std::move(other.name)}, module{std::move(other.module)}, spv{std::move(other.spv)},
|
||||
isa{std::move(other.isa)}, patch_spv{std::move(other.patch_spv)},
|
||||
patch_source{std::move(other.patch_source)},
|
||||
cache_spv_disasm{std::move(other.cache_spv_disasm)},
|
||||
cache_isa_disasm{std::move(other.cache_isa_disasm)},
|
||||
cache_patch_disasm{std::move(other.cache_patch_disasm)} {}
|
||||
ShaderDump& operator=(const ShaderDump& other) = delete;
|
||||
ShaderDump& operator=(ShaderDump&& other) noexcept {
|
||||
if (this == &other)
|
||||
return *this;
|
||||
name = std::move(other.name);
|
||||
module = std::move(other.module);
|
||||
spv = std::move(other.spv);
|
||||
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_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;
|
||||
}
|
||||
};
|
||||
@ -186,8 +203,9 @@ public:
|
||||
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
||||
const AmdGpu::Liverpool::Regs& regs, bool is_compute = false);
|
||||
|
||||
void CollectShader(const std::string& name, std::span<const u32> spv,
|
||||
std::span<const u32> raw_code);
|
||||
void CollectShader(const std::string& name, vk::ShaderModule module, std::span<const u32> spv,
|
||||
std::span<const u32> raw_code, std::span<const u32> patch_spv,
|
||||
bool is_patched);
|
||||
};
|
||||
} // namespace DebugStateType
|
||||
|
||||
|
12
src/core/devices/base_device.cpp
Normal file
12
src/core/devices/base_device.cpp
Normal 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
|
72
src/core/devices/base_device.h
Normal file
72
src/core/devices/base_device.h
Normal 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
67
src/core/devices/ioccom.h
Normal 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))
|
||||
*/
|
65
src/core/devices/logger.cpp
Normal file
65
src/core/devices/logger.cpp
Normal 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
37
src/core/devices/logger.h
Normal 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
|
55
src/core/devices/nop_device.h
Normal file
55
src/core/devices/nop_device.h
Normal 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
|
@ -254,7 +254,7 @@ void L::DrawAdvanced() {
|
||||
|
||||
void L::DrawSimple() {
|
||||
const auto io = GetIO();
|
||||
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
Text("%.1f FPS (%.2f ms)", io.Framerate, 1000.0f / io.Framerate);
|
||||
}
|
||||
|
||||
static void LoadSettings(const char* line) {
|
||||
@ -338,6 +338,7 @@ void L::Draw() {
|
||||
const auto fn = DebugState.flip_frame_count.load();
|
||||
frame_graph.AddFrame(fn, io.DeltaTime);
|
||||
}
|
||||
|
||||
if (IsKeyPressed(ImGuiKey_F10, false)) {
|
||||
if (io.KeyCtrl) {
|
||||
show_advanced_debug = !show_advanced_debug;
|
||||
|
@ -10,8 +10,8 @@ struct ImGuiTextBuffer;
|
||||
namespace Core::Devtools {
|
||||
|
||||
struct TOptions {
|
||||
std::string disassembler_cli_isa{"clrxdisasm --raw \"{src}\""};
|
||||
std::string disassembler_cli_spv{"spirv-cross -V \"{src}\""};
|
||||
std::string disassembler_cli_isa{"clrxdisasm --raw {src}"};
|
||||
std::string disassembler_cli_spv{"spirv-cross -V {src}"};
|
||||
bool frame_dump_render_on_collapse{false};
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/io_file.h"
|
||||
@ -117,7 +117,7 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
|
||||
inline std::optional<std::string> exec_cli(const char* cli) {
|
||||
std::array<char, 64> buffer{};
|
||||
std::string output;
|
||||
const auto f = popen(cli, "r");
|
||||
const auto f = popen(cli, "rt");
|
||||
if (!f) {
|
||||
pclose(f);
|
||||
return {};
|
||||
@ -129,21 +129,27 @@ inline std::optional<std::string> exec_cli(const char* cli) {
|
||||
return output;
|
||||
}
|
||||
|
||||
inline std::string RunDisassembler(const std::string& disassembler_cli,
|
||||
const std::vector<u32>& shader_code) {
|
||||
template <typename T>
|
||||
inline std::string RunDisassembler(const std::string& disassembler_cli, const T& shader_code,
|
||||
bool* success = nullptr) {
|
||||
std::string shader_dis;
|
||||
|
||||
if (disassembler_cli.empty()) {
|
||||
shader_dis = "No disassembler set";
|
||||
if (success) {
|
||||
*success = false;
|
||||
}
|
||||
} else {
|
||||
auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin";
|
||||
|
||||
constexpr std::string_view src_arg = "{src}";
|
||||
std::string cli = disassembler_cli;
|
||||
std::string cli = disassembler_cli + " 2>&1";
|
||||
const auto pos = cli.find(src_arg);
|
||||
if (pos == std::string::npos) {
|
||||
DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" +
|
||||
disassembler_cli);
|
||||
shader_dis = "Disassembler CLI does not contain {src} argument";
|
||||
if (success) {
|
||||
*success = false;
|
||||
}
|
||||
} else {
|
||||
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
|
||||
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
|
||||
@ -151,9 +157,16 @@ inline std::string RunDisassembler(const std::string& disassembler_cli,
|
||||
file.Close();
|
||||
|
||||
auto result = exec_cli(cli.c_str());
|
||||
shader_dis = result.value_or("Could not disassemble shader");
|
||||
if (shader_dis.empty()) {
|
||||
shader_dis = "Disassembly empty or failed";
|
||||
if (result) {
|
||||
shader_dis = result.value();
|
||||
if (success) {
|
||||
*success = true;
|
||||
}
|
||||
} else {
|
||||
if (success) {
|
||||
*success = false;
|
||||
}
|
||||
shader_dis = "Could not disassemble shader";
|
||||
}
|
||||
|
||||
std::filesystem::remove(bin_path);
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <cstdio>
|
||||
#include <fmt/chrono.h>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/io_file.h"
|
||||
#include "core/devtools/options.h"
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "core/debug_state.h"
|
||||
#include "core/memory.h"
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "cmd_list.h"
|
||||
#include "common.h"
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common.h"
|
||||
|
@ -1,66 +1,221 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "shader_list.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "common/config.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/devtools/options.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "sdl_window.h"
|
||||
#include "video_core/renderer_vulkan/vk_presenter.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
|
||||
extern std::unique_ptr<Vulkan::Presenter> presenter;
|
||||
|
||||
using namespace ImGui;
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
void ShaderList::DrawShader(DebugStateType::ShaderDump& value) {
|
||||
if (!loaded_data) {
|
||||
loaded_data = true;
|
||||
if (value.cache_raw_disasm.empty()) {
|
||||
value.cache_raw_disasm = RunDisassembler(Options.disassembler_cli_isa, value.raw_code);
|
||||
}
|
||||
isa_editor.SetText(value.cache_raw_disasm);
|
||||
ShaderList::Selection::Selection(int index) : index(index) {
|
||||
isa_editor.SetPalette(TextEditor::GetDarkPalette());
|
||||
isa_editor.SetReadOnly(true);
|
||||
glsl_editor.SetPalette(TextEditor::GetDarkPalette());
|
||||
glsl_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
|
||||
presenter->GetWindow().RequestKeyboard();
|
||||
}
|
||||
|
||||
ShaderList::Selection::~Selection() {
|
||||
presenter->GetWindow().ReleaseKeyboard();
|
||||
}
|
||||
|
||||
void ShaderList::Selection::ReloadShader(DebugStateType::ShaderDump& value) {
|
||||
auto& spv = value.is_patched ? value.patch_spv : value.spv;
|
||||
if (spv.empty()) {
|
||||
return;
|
||||
}
|
||||
auto& cache = presenter->GetRasterizer().GetPipelineCache();
|
||||
if (const auto m = cache.ReplaceShader(value.module, spv); m) {
|
||||
value.module = *m;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
|
||||
if (!value.loaded_data) {
|
||||
value.loaded_data = true;
|
||||
if (value.cache_isa_disasm.empty()) {
|
||||
value.cache_isa_disasm = RunDisassembler(Options.disassembler_cli_isa, value.isa);
|
||||
}
|
||||
if (value.cache_spv_disasm.empty()) {
|
||||
value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv);
|
||||
}
|
||||
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("<-")) {
|
||||
selected_shader = -1;
|
||||
value.is_patched = !value.patch_spv.empty();
|
||||
if (!value.is_patched) { // No patch
|
||||
isa_editor.SetText(value.cache_isa_disasm);
|
||||
glsl_editor.SetText(value.cache_spv_disasm);
|
||||
} else {
|
||||
isa_editor.SetText(value.cache_patch_disasm);
|
||||
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
|
||||
glsl_editor.SetText(value.patch_source);
|
||||
glsl_editor.SetReadOnly(false);
|
||||
}
|
||||
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());
|
||||
SameLine(0.0f, 7.0f);
|
||||
if (BeginCombo("Shader type", showing_isa ? "ISA" : "SPIRV", ImGuiComboFlags_WidthFitPreview)) {
|
||||
if (Selectable("SPIRV")) {
|
||||
showing_isa = false;
|
||||
if (Checkbox("Enable patch", &value.is_patched)) {
|
||||
if (value.is_patched) {
|
||||
if (value.patch_source.empty()) {
|
||||
value.patch_source = value.cache_spv_disasm;
|
||||
}
|
||||
if (Selectable("ISA")) {
|
||||
showing_isa = true;
|
||||
isa_editor.SetText(value.cache_patch_disasm);
|
||||
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
|
||||
glsl_editor.SetText(value.patch_source);
|
||||
glsl_editor.SetReadOnly(false);
|
||||
if (!value.patch_spv.empty()) {
|
||||
ReloadShader(value);
|
||||
}
|
||||
} else {
|
||||
isa_editor.SetText(value.cache_isa_disasm);
|
||||
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition());
|
||||
glsl_editor.SetText(value.cache_spv_disasm);
|
||||
glsl_editor.SetReadOnly(true);
|
||||
ReloadShader(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (value.is_patched) {
|
||||
if (BeginCombo("Shader type", showing_bin ? "SPIRV" : "GLSL",
|
||||
ImGuiComboFlags_WidthFitPreview)) {
|
||||
if (Selectable("GLSL")) {
|
||||
showing_bin = false;
|
||||
}
|
||||
if (Selectable("SPIRV")) {
|
||||
showing_bin = true;
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
|
||||
if (showing_isa) {
|
||||
isa_editor.Render("ISA", GetContentRegionAvail());
|
||||
} 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() {
|
||||
isa_editor.SetPalette(TextEditor::GetDarkPalette());
|
||||
isa_editor.SetReadOnly(true);
|
||||
spv_editor.SetPalette(TextEditor::GetDarkPalette());
|
||||
spv_editor.SetReadOnly(true);
|
||||
spv_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
|
||||
if (value.is_patched) {
|
||||
bool save = false;
|
||||
bool compile = false;
|
||||
SameLine(0.0f, 3.0f);
|
||||
if (Button("Save")) {
|
||||
save = true;
|
||||
}
|
||||
SameLine();
|
||||
if (Button("Save & Compile")) {
|
||||
save = true;
|
||||
compile = true;
|
||||
}
|
||||
if (save) {
|
||||
value.patch_source = glsl_editor.GetText();
|
||||
std::ofstream file{patch_path, std::ios::binary | std::ios::trunc};
|
||||
file << value.patch_source;
|
||||
std::string msg = "Patch saved to ";
|
||||
msg += Common::U8stringToString(patch_path.u8string());
|
||||
DebugState.ShowDebugMessage(msg);
|
||||
}
|
||||
if (compile) {
|
||||
static std::map<std::string, std::string> stage_arg = {
|
||||
{"vs", "vert"},
|
||||
{"gs", "geom"},
|
||||
{"fs", "frag"},
|
||||
{"cs", "comp"},
|
||||
};
|
||||
auto stage = stage_arg.find(value.name.substr(0, 2));
|
||||
if (stage == stage_arg.end()) {
|
||||
DebugState.ShowDebugMessage(std::string{"Invalid shader stage: "} +
|
||||
value.name.substr(0, 2));
|
||||
} else {
|
||||
std::string cmd =
|
||||
fmt::format("glslc --target-env=vulkan1.3 --target-spv=spv1.6 "
|
||||
"-fshader-stage={} {{src}} -o \"{}\"",
|
||||
stage->second, Common::U8stringToString(patch_bin_path.u8string()));
|
||||
bool success = false;
|
||||
auto res = RunDisassembler(cmd, value.patch_source, &success);
|
||||
if (!res.empty() || !success) {
|
||||
DebugState.ShowDebugMessage("Compilation failed:\n" + res);
|
||||
} else {
|
||||
Common::FS::IOFile file{patch_bin_path, Common::FS::FileAccessMode::Read};
|
||||
value.patch_spv.resize(file.GetSize() / sizeof(u32));
|
||||
file.Read(value.patch_spv);
|
||||
value.cache_patch_disasm =
|
||||
RunDisassembler("spirv-dis {src}", value.patch_spv, &success);
|
||||
if (!success) {
|
||||
DebugState.ShowDebugMessage("Decompilation failed (Compile was ok):\n" +
|
||||
res);
|
||||
} else {
|
||||
isa_editor.SetText(value.cache_patch_disasm);
|
||||
ReloadShader(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showing_bin) {
|
||||
isa_editor.Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail());
|
||||
} else {
|
||||
glsl_editor.Render("GLSL", GetContentRegionAvail());
|
||||
}
|
||||
|
||||
End();
|
||||
return open;
|
||||
}
|
||||
|
||||
void ShaderList::Draw() {
|
||||
for (auto it = open_shaders.begin(); it != open_shaders.end();) {
|
||||
auto& selection = *it;
|
||||
auto& shader = DebugState.shader_dump_list[selection.index];
|
||||
if (!selection.DrawShader(shader)) {
|
||||
it = open_shaders.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver);
|
||||
if (!Begin("Shader list", &open)) {
|
||||
End();
|
||||
@ -73,18 +228,19 @@ void ShaderList::Draw() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected_shader >= 0) {
|
||||
DrawShader(DebugState.shader_dump_list[selected_shader]);
|
||||
End();
|
||||
return;
|
||||
}
|
||||
|
||||
auto width = GetContentRegionAvail().x;
|
||||
int i = 0;
|
||||
for (const auto& shader : DebugState.shader_dump_list) {
|
||||
if (ButtonEx(shader.name.c_str(), {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) {
|
||||
selected_shader = i;
|
||||
loaded_data = false;
|
||||
char name[128];
|
||||
if (shader.is_patched) {
|
||||
snprintf(name, sizeof(name), "%s (PATCH ON)", shader.name.c_str());
|
||||
} else if (!shader.patch_spv.empty()) {
|
||||
snprintf(name, sizeof(name), "%s (PATCH OFF)", shader.name.c_str());
|
||||
} else {
|
||||
snprintf(name, sizeof(name), "%s", shader.name.c_str());
|
||||
}
|
||||
if (ButtonEx(name, {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) {
|
||||
open_shaders.emplace_back(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
@ -6,20 +6,32 @@
|
||||
#include "core/debug_state.h"
|
||||
#include "text_editor.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class ShaderList {
|
||||
int selected_shader = -1;
|
||||
TextEditor isa_editor{};
|
||||
TextEditor spv_editor{};
|
||||
bool loaded_data = false;
|
||||
bool showing_isa = false;
|
||||
struct Selection {
|
||||
explicit Selection(int index);
|
||||
~Selection();
|
||||
|
||||
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:
|
||||
ShaderList();
|
||||
|
||||
bool open = false;
|
||||
|
||||
void Draw();
|
||||
|
@ -1059,7 +1059,8 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) {
|
||||
if (!mIgnoreImGuiChild)
|
||||
ImGui::BeginChild(aTitle, aSize, aBorder,
|
||||
ImGuiWindowFlags_HorizontalScrollbar |
|
||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove);
|
||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoNav);
|
||||
|
||||
if (mHandleKeyboardInputs) {
|
||||
HandleKeyboardInputs();
|
||||
@ -2331,4 +2332,50 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL() {
|
||||
return langDef;
|
||||
}
|
||||
|
||||
// Source: https://github.com/dfranx/ImGuiColorTextEdit/blob/master/TextEditor.cpp
|
||||
const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::SPIRV() {
|
||||
static bool inited = false;
|
||||
static LanguageDefinition langDef;
|
||||
if (!inited) {
|
||||
/*
|
||||
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>("[ \\t]*#[
|
||||
\\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor));
|
||||
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string,
|
||||
PaletteIndex>("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral));
|
||||
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string,
|
||||
PaletteIndex>("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier));
|
||||
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string,
|
||||
PaletteIndex>("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]",
|
||||
PaletteIndex::Punctuation));
|
||||
*/
|
||||
|
||||
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
|
||||
"L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String));
|
||||
langDef.mTokenRegexStrings.push_back(
|
||||
std::make_pair<std::string, PaletteIndex>("[ =\\t]Op[a-zA-Z]*", PaletteIndex::Keyword));
|
||||
langDef.mTokenRegexStrings.push_back(
|
||||
std::make_pair<std::string, PaletteIndex>("%[_a-zA-Z0-9]*", PaletteIndex::Identifier));
|
||||
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
|
||||
"[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number));
|
||||
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
|
||||
"[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
|
||||
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
|
||||
"0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number));
|
||||
langDef.mTokenRegexStrings.push_back(std::make_pair<std::string, PaletteIndex>(
|
||||
"0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number));
|
||||
|
||||
langDef.mCommentStart = "/*";
|
||||
langDef.mCommentEnd = "*/";
|
||||
langDef.mSingleLineComment = ";";
|
||||
|
||||
langDef.mCaseSensitive = true;
|
||||
langDef.mAutoIndentation = false;
|
||||
|
||||
langDef.mName = "SPIR-V";
|
||||
|
||||
inited = true;
|
||||
}
|
||||
return langDef;
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
||||
|
@ -161,6 +161,7 @@ public:
|
||||
: mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) {}
|
||||
|
||||
static const LanguageDefinition& GLSL();
|
||||
static const LanguageDefinition& SPIRV();
|
||||
};
|
||||
|
||||
TextEditor();
|
||||
|
@ -5,13 +5,9 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/stb.h"
|
||||
#include "splash.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_NO_STDIO
|
||||
#include "externals/stb_image.h"
|
||||
|
||||
bool Splash::Open(const std::filesystem::path& filepath) {
|
||||
ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed");
|
||||
|
||||
|
@ -4,12 +4,12 @@
|
||||
#include <algorithm>
|
||||
#include "common/config.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/devices/logger.h"
|
||||
#include "core/devices/nop_device.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
|
||||
namespace Core::FileSys {
|
||||
|
||||
constexpr int RESERVED_HANDLES = 3; // First 3 handles are stdin,stdout,stderr
|
||||
|
||||
void MntPoints::Mount(const std::filesystem::path& host_folder, const std::string& guest_folder,
|
||||
bool read_only) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
@ -135,7 +135,6 @@ int HandleTable::CreateHandle() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
auto* file = new File{};
|
||||
file->is_directory = false;
|
||||
file->is_opened = false;
|
||||
|
||||
int existingFilesNum = m_files.size();
|
||||
@ -143,23 +142,23 @@ int HandleTable::CreateHandle() {
|
||||
for (int index = 0; index < existingFilesNum; index++) {
|
||||
if (m_files.at(index) == nullptr) {
|
||||
m_files[index] = file;
|
||||
return index + RESERVED_HANDLES;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
m_files.push_back(file);
|
||||
return m_files.size() + RESERVED_HANDLES - 1;
|
||||
return m_files.size() - 1;
|
||||
}
|
||||
|
||||
void HandleTable::DeleteHandle(int d) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
delete m_files.at(d - RESERVED_HANDLES);
|
||||
m_files[d - RESERVED_HANDLES] = nullptr;
|
||||
delete m_files.at(d);
|
||||
m_files[d] = nullptr;
|
||||
}
|
||||
|
||||
File* HandleTable::GetFile(int d) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
return m_files.at(d - RESERVED_HANDLES);
|
||||
return m_files.at(d);
|
||||
}
|
||||
|
||||
File* HandleTable::GetFile(const std::filesystem::path& host_name) {
|
||||
@ -171,4 +170,20 @@ File* HandleTable::GetFile(const std::filesystem::path& host_name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HandleTable::CreateStdHandles() {
|
||||
auto setup = [this](const char* path, auto* device) {
|
||||
int fd = CreateHandle();
|
||||
auto* file = GetFile(fd);
|
||||
file->is_opened = true;
|
||||
file->type = FileType::Device;
|
||||
file->m_guest_name = path;
|
||||
file->device =
|
||||
std::shared_ptr<Devices::BaseDevice>{reinterpret_cast<Devices::BaseDevice*>(device)};
|
||||
};
|
||||
// order matters
|
||||
setup("/dev/stdin", new Devices::NopDevice(0)); // stdin
|
||||
setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout
|
||||
setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr
|
||||
}
|
||||
|
||||
} // namespace Core::FileSys
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
#include <tsl/robin_map.h>
|
||||
#include "common/io_file.h"
|
||||
#include "core/devices/base_device.h"
|
||||
|
||||
namespace Core::FileSys {
|
||||
|
||||
@ -55,15 +56,22 @@ struct DirEntry {
|
||||
bool isFile;
|
||||
};
|
||||
|
||||
enum class FileType {
|
||||
Regular, // standard file
|
||||
Directory,
|
||||
Device,
|
||||
};
|
||||
|
||||
struct File {
|
||||
std::atomic_bool is_opened{};
|
||||
std::atomic_bool is_directory{};
|
||||
std::atomic<FileType> type{FileType::Regular};
|
||||
std::filesystem::path m_host_name;
|
||||
std::string m_guest_name;
|
||||
Common::FS::IOFile f;
|
||||
std::vector<DirEntry> dirents;
|
||||
u32 dirents_index;
|
||||
std::mutex m_mutex;
|
||||
std::shared_ptr<Devices::BaseDevice> device; // only valid for type == Device
|
||||
};
|
||||
|
||||
class HandleTable {
|
||||
@ -76,6 +84,8 @@ public:
|
||||
File* GetFile(int d);
|
||||
File* GetFile(const std::filesystem::path& host_name);
|
||||
|
||||
void CreateStdHandles();
|
||||
|
||||
private:
|
||||
std::vector<File*> m_files;
|
||||
std::mutex m_mutex;
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
namespace Libraries::Ajm {
|
||||
|
||||
@ -19,7 +19,7 @@ constexpr int ORBIS_AJM_CHANNELMASK_QUAD = 0x0033;
|
||||
constexpr int ORBIS_AJM_CHANNELMASK_5POINT1 = 0x060F;
|
||||
constexpr int ORBIS_AJM_CHANNELMASK_7POINT1 = 0x063F;
|
||||
|
||||
static std::unique_ptr<AjmContext> context{};
|
||||
static std::unordered_map<u32, std::unique_ptr<AjmContext>> contexts{};
|
||||
|
||||
u32 GetChannelMask(u32 num_channels) {
|
||||
switch (num_channels) {
|
||||
@ -40,7 +40,13 @@ u32 GetChannelMask(u32 num_channels) {
|
||||
|
||||
int PS4_SYSV_ABI sceAjmBatchCancel(const u32 context_id, const u32 batch_id) {
|
||||
LOG_INFO(Lib_Ajm, "called context_id = {} batch_id = {}", context_id, batch_id);
|
||||
return context->BatchCancel(batch_id);
|
||||
|
||||
auto it = contexts.find(context_id);
|
||||
if (it == contexts.end()) {
|
||||
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
return it->second->BatchCancel(batch_id);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmBatchErrorDump() {
|
||||
@ -90,14 +96,26 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context_id, u8* p_batch, u32 batch_s
|
||||
u32* out_batch_id) {
|
||||
LOG_TRACE(Lib_Ajm, "called context = {}, batch_size = {:#x}, priority = {}", context_id,
|
||||
batch_size, priority);
|
||||
return context->BatchStartBuffer(p_batch, batch_size, priority, batch_error, out_batch_id);
|
||||
|
||||
auto it = contexts.find(context_id);
|
||||
if (it == contexts.end()) {
|
||||
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
return it->second->BatchStartBuffer(p_batch, batch_size, priority, batch_error, out_batch_id);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmBatchWait(const u32 context_id, const u32 batch_id, const u32 timeout,
|
||||
AjmBatchError* const batch_error) {
|
||||
LOG_TRACE(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context_id, batch_id,
|
||||
timeout);
|
||||
return context->BatchWait(batch_id, timeout, batch_error);
|
||||
|
||||
auto it = contexts.find(context_id);
|
||||
if (it == contexts.end()) {
|
||||
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
return it->second->BatchWait(batch_id, timeout, batch_error);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() {
|
||||
@ -117,12 +135,12 @@ int PS4_SYSV_ABI sceAjmFinalize() {
|
||||
|
||||
int PS4_SYSV_ABI sceAjmInitialize(s64 reserved, u32* p_context_id) {
|
||||
LOG_INFO(Lib_Ajm, "called reserved = {}", reserved);
|
||||
ASSERT_MSG(context == nullptr, "Multiple contexts are currently unsupported.");
|
||||
if (p_context_id == nullptr || reserved != 0) {
|
||||
return ORBIS_AJM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
*p_context_id = 1;
|
||||
context = std::make_unique<AjmContext>();
|
||||
u32 id = contexts.size() + 1;
|
||||
*p_context_id = id;
|
||||
contexts.emplace(id, std::make_unique<AjmContext>());
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -135,12 +153,24 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context_id, AjmCodecType codec_type,
|
||||
AjmInstanceFlags flags, u32* out_instance) {
|
||||
LOG_INFO(Lib_Ajm, "called context = {}, codec_type = {}, flags = {:#x}", context_id,
|
||||
magic_enum::enum_name(codec_type), flags.raw);
|
||||
return context->InstanceCreate(codec_type, flags, out_instance);
|
||||
|
||||
auto it = contexts.find(context_id);
|
||||
if (it == contexts.end()) {
|
||||
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
return it->second->InstanceCreate(codec_type, flags, out_instance);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context_id, u32 instance_id) {
|
||||
LOG_INFO(Lib_Ajm, "called context = {}, instance = {}", context_id, instance_id);
|
||||
return context->InstanceDestroy(instance_id);
|
||||
|
||||
auto it = contexts.find(context_id);
|
||||
if (it == contexts.end()) {
|
||||
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
return it->second->InstanceDestroy(instance_id);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmInstanceExtend() {
|
||||
@ -168,7 +198,13 @@ int PS4_SYSV_ABI sceAjmModuleRegister(u32 context_id, AjmCodecType codec_type, s
|
||||
if (reserved != 0) {
|
||||
return ORBIS_AJM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
return context->ModuleRegister(codec_type);
|
||||
|
||||
auto it = contexts.find(context_id);
|
||||
if (it == contexts.end()) {
|
||||
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
|
||||
return it->second->ModuleRegister(codec_type);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmModuleUnregister() {
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/libraries/ajm/ajm.h"
|
||||
#include "core/libraries/ajm/ajm_at9.h"
|
||||
#include "core/libraries/ajm/ajm_context.h"
|
||||
@ -53,6 +54,7 @@ s32 AjmContext::ModuleRegister(AjmCodecType type) {
|
||||
}
|
||||
|
||||
void AjmContext::WorkerThread(std::stop_token stop) {
|
||||
Common::SetCurrentThreadName("shadPS4:AjmWorker");
|
||||
while (!stop.stop_requested()) {
|
||||
auto batch = batch_queue.PopWait(stop);
|
||||
if (batch != nullptr) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "core/libraries/ajm/ajm_instance.h"
|
||||
#include "core/libraries/ajm/ajm_mp3.h"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
namespace Libraries::Ajm {
|
||||
|
||||
|
@ -12,6 +12,8 @@ extern "C" {
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
#include "common/support/avdec.h"
|
||||
|
||||
namespace Libraries::Ajm {
|
||||
|
||||
// Following tables have been reversed from AJM library
|
||||
|
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm> // std::max, std::min
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include "core/libraries/avplayer/avplayer_file_streamer.h"
|
||||
|
||||
extern "C" {
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "core/libraries/avplayer/avplayer_file_streamer.h"
|
||||
#include "core/libraries/avplayer/avplayer_source.h"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
@ -18,16 +18,7 @@ extern "C" {
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
// The av_err2str macro in libavutil/error.h does not play nice with C++
|
||||
#ifdef av_err2str
|
||||
#undef av_err2str
|
||||
#include <string>
|
||||
av_always_inline std::string av_err2string(int errnum) {
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum);
|
||||
}
|
||||
#define av_err2str(err) av_err2string(err).c_str()
|
||||
#endif // av_err2str
|
||||
#include "common/support/avdec.h"
|
||||
|
||||
namespace Libraries::AvPlayer {
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "core/libraries/avplayer/avplayer_state.h"
|
||||
#include "core/tls.h"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
namespace Libraries::AvPlayer {
|
||||
|
||||
|
@ -544,15 +544,15 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) {
|
||||
.base_addr = base_addr,
|
||||
});
|
||||
}
|
||||
liverpool->SubmitAsc(vqid, acb_span);
|
||||
liverpool->SubmitAsc(gnm_vqid, acb_span);
|
||||
|
||||
*asc_queue.read_addr += acb_size;
|
||||
*asc_queue.read_addr %= asc_queue.ring_size_dw * 4;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmDingDongForWorkload() {
|
||||
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id) {
|
||||
LOG_DEBUG(Lib_GnmDriver, "called, redirecting to sceGnmDingDong");
|
||||
sceGnmDingDong(gnm_vqid, next_offs_dw);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmDisableMipStatsReport() {
|
||||
|
@ -34,7 +34,7 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus();
|
||||
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id);
|
||||
int PS4_SYSV_ABI sceGnmDestroyWorkloadStream();
|
||||
void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw);
|
||||
int PS4_SYSV_ABI sceGnmDingDongForWorkload();
|
||||
void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id);
|
||||
int PS4_SYSV_ABI sceGnmDisableMipStatsReport();
|
||||
s32 PS4_SYSV_ABI sceGnmDispatchDirect(u32* cmdbuf, u32 size, u32 threads_x, u32 threads_y,
|
||||
u32 threads_z, u32 flags);
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <utility>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <cwchar>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
@ -1,7 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
@ -1,16 +1,49 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
#include <ranges>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/devices/logger.h"
|
||||
#include "core/devices/nop_device.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/kernel/file_system.h"
|
||||
#include "core/libraries/kernel/orbis_error.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/memory.h"
|
||||
#include "kernel.h"
|
||||
|
||||
namespace D = Core::Devices;
|
||||
using FactoryDevice = std::function<std::shared_ptr<D::BaseDevice>(u32, const char*, int, u16)>;
|
||||
|
||||
#define GET_DEVICE_FD(fd) \
|
||||
[](u32, const char*, int, u16) { \
|
||||
return Common::Singleton<Core::FileSys::HandleTable>::Instance()->GetFile(fd)->device; \
|
||||
}
|
||||
|
||||
// prefix path, only dev devices
|
||||
static std::map<std::string, FactoryDevice> available_device = {
|
||||
// clang-format off
|
||||
{"/dev/stdin", GET_DEVICE_FD(0)},
|
||||
{"/dev/stdout", GET_DEVICE_FD(1)},
|
||||
{"/dev/stderr", GET_DEVICE_FD(2)},
|
||||
|
||||
{"/dev/fd/0", GET_DEVICE_FD(0)},
|
||||
{"/dev/fd/1", GET_DEVICE_FD(1)},
|
||||
{"/dev/fd/2", GET_DEVICE_FD(2)},
|
||||
|
||||
{"/dev/deci_stdin", GET_DEVICE_FD(0)},
|
||||
{"/dev/deci_stdout", GET_DEVICE_FD(1)},
|
||||
{"/dev/deci_stderr", GET_DEVICE_FD(2)},
|
||||
|
||||
{"/dev/null", GET_DEVICE_FD(0)}, // fd0 (stdin) is a nop device
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
auto GetDirectoryEntries(const std::filesystem::path& path) {
|
||||
@ -24,8 +57,8 @@ auto GetDirectoryEntries(const std::filesystem::path& path) {
|
||||
return files;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) {
|
||||
LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", path, flags, mode);
|
||||
int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
|
||||
LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode);
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
|
||||
@ -44,22 +77,35 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) {
|
||||
bool direct = (flags & ORBIS_KERNEL_O_DIRECT) != 0;
|
||||
bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0;
|
||||
|
||||
if (std::string_view{path} == "/dev/console") {
|
||||
std::string_view path{raw_path};
|
||||
|
||||
if (path == "/dev/console") {
|
||||
return 2000;
|
||||
}
|
||||
if (std::string_view{path} == "/dev/deci_tty6") {
|
||||
if (path == "/dev/deci_tty6") {
|
||||
return 2001;
|
||||
}
|
||||
if (std::string_view{path} == "/dev/stdout") {
|
||||
return 2002;
|
||||
}
|
||||
if (std::string_view{path} == "/dev/urandom") {
|
||||
if (path == "/dev/urandom") {
|
||||
return 2003;
|
||||
}
|
||||
|
||||
u32 handle = h->CreateHandle();
|
||||
auto* file = h->GetFile(handle);
|
||||
|
||||
if (path.starts_with("/dev/")) {
|
||||
for (const auto& [prefix, factory] : available_device) {
|
||||
if (path.starts_with(prefix)) {
|
||||
file->is_opened = true;
|
||||
file->type = Core::FileSys::FileType::Device;
|
||||
file->m_guest_name = path;
|
||||
file->device = factory(handle, path.data(), flags, mode);
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (directory) {
|
||||
file->is_directory = true;
|
||||
file->type = Core::FileSys::FileType::Directory;
|
||||
file->m_guest_name = path;
|
||||
file->m_host_name = mnt->GetHostPath(file->m_guest_name);
|
||||
if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist
|
||||
@ -135,11 +181,12 @@ int PS4_SYSV_ABI sceKernelClose(int d) {
|
||||
if (file == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
if (!file->is_directory) {
|
||||
if (file->type == Core::FileSys::FileType::Regular) {
|
||||
file->f.Close();
|
||||
}
|
||||
file->is_opened = false;
|
||||
LOG_INFO(Kernel_Fs, "Closing {}", file->m_guest_name);
|
||||
// FIXME: Lock file mutex before deleting it?
|
||||
h->DeleteHandle(d);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -154,15 +201,7 @@ int PS4_SYSV_ABI posix_close(int d) {
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) {
|
||||
if (d <= 2) { // stdin,stdout,stderr
|
||||
char* str = strdup((const char*)buf);
|
||||
if (str[nbytes - 1] == '\n')
|
||||
str[nbytes - 1] = 0;
|
||||
LOG_INFO(Tty, "{}", str);
|
||||
free(str);
|
||||
return nbytes;
|
||||
}
|
||||
s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) {
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* file = h->GetFile(d);
|
||||
if (file == nullptr) {
|
||||
@ -170,6 +209,9 @@ size_t PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) {
|
||||
}
|
||||
|
||||
std::scoped_lock lk{file->m_mutex};
|
||||
if (file->type == Core::FileSys::FileType::Device) {
|
||||
return file->device->write(buf, nbytes);
|
||||
}
|
||||
return file->f.WriteRaw<u8>(buf, nbytes);
|
||||
}
|
||||
|
||||
@ -204,20 +246,75 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
size_t ReadFile(Common::FS::IOFile& file, void* buf, size_t nbytes) {
|
||||
const auto* memory = Core::Memory::Instance();
|
||||
// Invalidate up to the actual number of bytes that could be read.
|
||||
const auto remaining = file.GetSize() - file.Tell();
|
||||
memory->InvalidateMemory(reinterpret_cast<VAddr>(buf), std::min<u64>(nbytes, remaining));
|
||||
|
||||
return file.ReadRaw<u8>(buf, nbytes);
|
||||
}
|
||||
|
||||
size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) {
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* file = h->GetFile(d);
|
||||
size_t total_read = 0;
|
||||
if (file == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{file->m_mutex};
|
||||
if (file->type == Core::FileSys::FileType::Device) {
|
||||
int r = file->device->readv(iov, iovcnt);
|
||||
if (r < 0) {
|
||||
ErrSceToPosix(r);
|
||||
return -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
size_t total_read = 0;
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
total_read += file->f.ReadRaw<u8>(iov[i].iov_base, iov[i].iov_len);
|
||||
total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len);
|
||||
}
|
||||
return total_read;
|
||||
}
|
||||
|
||||
size_t PS4_SYSV_ABI _writev(int fd, const SceKernelIovec* iov, int iovcn) {
|
||||
if (fd == 1) {
|
||||
size_t total_written = 0;
|
||||
for (int i = 0; i < iovcn; i++) {
|
||||
total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout);
|
||||
}
|
||||
return total_written;
|
||||
}
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* file = h->GetFile(fd);
|
||||
if (file == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{file->m_mutex};
|
||||
|
||||
if (file->type == Core::FileSys::FileType::Device) {
|
||||
return file->device->writev(iov, iovcn);
|
||||
}
|
||||
size_t total_written = 0;
|
||||
for (int i = 0; i < iovcn; i++) {
|
||||
total_written += file->f.WriteRaw<u8>(iov[i].iov_base, iov[i].iov_len);
|
||||
}
|
||||
return total_written;
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) {
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* file = h->GetFile(d);
|
||||
if (file == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{file->m_mutex};
|
||||
if (file->type == Core::FileSys::FileType::Device) {
|
||||
return file->device->lseek(offset, whence);
|
||||
}
|
||||
|
||||
Common::FS::SeekOrigin origin{};
|
||||
if (whence == 0) {
|
||||
@ -228,7 +325,6 @@ s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) {
|
||||
origin = Common::FS::SeekOrigin::End;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{file->m_mutex};
|
||||
if (!file->f.Seek(offset, origin)) {
|
||||
LOG_CRITICAL(Kernel_Fs, "sceKernelLseek: failed to seek");
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
@ -261,7 +357,10 @@ s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) {
|
||||
}
|
||||
|
||||
std::scoped_lock lk{file->m_mutex};
|
||||
return file->f.ReadRaw<u8>(buf, nbytes);
|
||||
if (file->type == Core::FileSys::FileType::Device) {
|
||||
return file->device->read(buf, nbytes);
|
||||
}
|
||||
return ReadFile(file->f, buf, nbytes);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_read(int d, void* buf, size_t nbytes) {
|
||||
@ -409,7 +508,13 @@ int PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) {
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) {
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
const auto path_name = mnt->GetHostPath(path);
|
||||
std::string_view guest_path{path};
|
||||
for (const auto& prefix : available_device | std::views::keys) {
|
||||
if (guest_path.starts_with(prefix)) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
}
|
||||
const auto path_name = mnt->GetHostPath(guest_path);
|
||||
if (!std::filesystem::exists(path_name)) {
|
||||
return ORBIS_KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
@ -431,6 +536,10 @@ s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 off
|
||||
}
|
||||
|
||||
std::scoped_lock lk{file->m_mutex};
|
||||
if (file->type == Core::FileSys::FileType::Device) {
|
||||
return file->device->preadv(iov, iovcnt, offset);
|
||||
}
|
||||
|
||||
const s64 pos = file->f.Tell();
|
||||
SCOPE_EXIT {
|
||||
file->f.Seek(pos);
|
||||
@ -441,7 +550,7 @@ s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 off
|
||||
}
|
||||
size_t total_read = 0;
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
total_read += file->f.ReadRaw<u8>(iov[i].iov_base, iov[i].iov_len);
|
||||
total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len);
|
||||
}
|
||||
return total_read;
|
||||
}
|
||||
@ -466,18 +575,25 @@ int PS4_SYSV_ABI sceKernelFStat(int fd, OrbisKernelStat* sb) {
|
||||
}
|
||||
std::memset(sb, 0, sizeof(OrbisKernelStat));
|
||||
|
||||
if (file->is_directory) {
|
||||
sb->st_mode = 0000777u | 0040000u;
|
||||
sb->st_size = 0;
|
||||
sb->st_blksize = 512;
|
||||
sb->st_blocks = 0;
|
||||
// TODO incomplete
|
||||
} else {
|
||||
switch (file->type) {
|
||||
case Core::FileSys::FileType::Device:
|
||||
return file->device->fstat(sb);
|
||||
case Core::FileSys::FileType::Regular:
|
||||
sb->st_mode = 0000777u | 0100000u;
|
||||
sb->st_size = file->f.GetSize();
|
||||
sb->st_blksize = 512;
|
||||
sb->st_blocks = (sb->st_size + 511) / 512;
|
||||
// TODO incomplete
|
||||
break;
|
||||
case Core::FileSys::FileType::Directory:
|
||||
sb->st_mode = 0000777u | 0040000u;
|
||||
sb->st_size = 0;
|
||||
sb->st_blksize = 512;
|
||||
sb->st_blocks = 0;
|
||||
// TODO incomplete
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -495,6 +611,13 @@ int PS4_SYSV_ABI posix_fstat(int fd, OrbisKernelStat* sb) {
|
||||
s32 PS4_SYSV_ABI sceKernelFsync(int fd) {
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* file = h->GetFile(fd);
|
||||
if (file == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (file->type == Core::FileSys::FileType::Device) {
|
||||
return file->device->fsync();
|
||||
}
|
||||
file->f.Flush();
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -517,6 +640,10 @@ int PS4_SYSV_ABI sceKernelFtruncate(int fd, s64 length) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (file->type == Core::FileSys::FileType::Device) {
|
||||
return file->device->ftruncate(length);
|
||||
}
|
||||
|
||||
if (file->m_host_name.empty()) {
|
||||
return ORBIS_KERNEL_ERROR_EACCES;
|
||||
}
|
||||
@ -538,10 +665,15 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) {
|
||||
if (file == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
if (file->type == Core::FileSys::FileType::Device) {
|
||||
return file->device->getdents(buf, nbytes, basep);
|
||||
}
|
||||
|
||||
if (file->dirents_index == file->dirents.size()) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
if (!file->is_directory || nbytes < 512 || file->dirents_index > file->dirents.size()) {
|
||||
if (file->type != Core::FileSys::FileType::Directory || nbytes < 512 ||
|
||||
file->dirents_index > file->dirents.size()) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
const auto& entry = file->dirents.at(file->dirents_index++);
|
||||
@ -586,6 +718,10 @@ s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) {
|
||||
}
|
||||
|
||||
std::scoped_lock lk{file->m_mutex};
|
||||
|
||||
if (file->type == Core::FileSys::FileType::Device) {
|
||||
return file->device->pwrite(buf, nbytes, offset);
|
||||
}
|
||||
const s64 pos = file->f.Tell();
|
||||
SCOPE_EXIT {
|
||||
file->f.Seek(pos);
|
||||
@ -637,6 +773,7 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite);
|
||||
|
||||
LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, _readv);
|
||||
LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev);
|
||||
LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek);
|
||||
LIB_FUNCTION("Oy6IpwgtYOk", "libScePosix", 1, "libkernel", 1, 1, posix_lseek);
|
||||
LIB_FUNCTION("oib76F-12fk", "libkernel", 1, "libkernel", 1, 1, sceKernelLseek);
|
||||
|
@ -65,6 +65,9 @@ constexpr int ORBIS_KERNEL_O_DSYNC = 0x1000;
|
||||
constexpr int ORBIS_KERNEL_O_DIRECT = 0x00010000;
|
||||
constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000;
|
||||
|
||||
s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes);
|
||||
s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes);
|
||||
|
||||
void RegisterFileSystem(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
@ -9,6 +9,9 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/va_ctx.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/equeue.h"
|
||||
#include "core/libraries/kernel/file_system.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
@ -24,6 +27,7 @@
|
||||
#ifdef _WIN64
|
||||
#include <Rpc.h>
|
||||
#endif
|
||||
#include <common/singleton.h>
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
@ -42,7 +46,7 @@ void KernelSignalRequest() {
|
||||
}
|
||||
|
||||
static void KernelServiceThread(std::stop_token stoken) {
|
||||
Common::SetCurrentThreadName("shadPS4:Kernel_ServiceThread");
|
||||
Common::SetCurrentThreadName("shadPS4:KernelServiceThread");
|
||||
|
||||
while (!stoken.stop_requested()) {
|
||||
HLE_TRACE;
|
||||
@ -65,19 +69,6 @@ static PS4_SYSV_ABI void stack_chk_fail() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
struct iovec {
|
||||
void* iov_base; /* Base address. */
|
||||
size_t iov_len; /* Length. */
|
||||
};
|
||||
|
||||
size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) {
|
||||
size_t total_written = 0;
|
||||
for (int i = 0; i < iovcn; i++) {
|
||||
total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout);
|
||||
}
|
||||
return total_written;
|
||||
}
|
||||
|
||||
static thread_local int g_posix_errno = 0;
|
||||
|
||||
int* PS4_SYSV_ABI __Error() {
|
||||
@ -142,24 +133,11 @@ void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) {
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) {
|
||||
if (d <= 2) { // stdin,stdout,stderr
|
||||
std::string_view str{buf};
|
||||
if (str[nbytes - 1] == '\n') {
|
||||
str = str.substr(0, nbytes - 1);
|
||||
}
|
||||
LOG_INFO(Tty, "{}", str);
|
||||
return nbytes;
|
||||
}
|
||||
LOG_ERROR(Kernel, "(STUBBED) called d = {} nbytes = {} ", d, nbytes);
|
||||
UNREACHABLE();
|
||||
return ORBIS_OK;
|
||||
return sceKernelWrite(d, buf, nbytes);
|
||||
}
|
||||
|
||||
s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) {
|
||||
ASSERT_MSG(d == 0, "d is not 0!");
|
||||
|
||||
return static_cast<s64>(
|
||||
strlen(std::fgets(static_cast<char*>(buf), static_cast<int>(nbytes), stdin)));
|
||||
return sceKernelRead(d, buf, nbytes);
|
||||
}
|
||||
|
||||
struct OrbisKernelUuid {
|
||||
@ -189,6 +167,29 @@ int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) {
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* file = h->GetFile(fd);
|
||||
if (file == nullptr) {
|
||||
LOG_INFO(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} file == nullptr", fd, cmd);
|
||||
g_posix_errno = POSIX_EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (file->type != Core::FileSys::FileType::Device) {
|
||||
LOG_WARNING(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} file->type != Device", fd, cmd);
|
||||
g_posix_errno = ENOTTY;
|
||||
return -1;
|
||||
}
|
||||
VA_CTX(ctx);
|
||||
int result = file->device->ioctl(cmd, &ctx);
|
||||
LOG_TRACE(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} result = {}", fd, cmd, result);
|
||||
if (result < 0) {
|
||||
ErrSceToPosix(result);
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() {
|
||||
const char* path = "sys";
|
||||
return path;
|
||||
@ -219,19 +220,20 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
|
||||
Libraries::Kernel::RegisterException(sym);
|
||||
|
||||
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
|
||||
LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", 1, 1, kernel_ioctl);
|
||||
LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord);
|
||||
LIB_FUNCTION("XVL8So3QJUk", "libkernel", 1, "libkernel", 1, 1, posix_connect);
|
||||
LIB_FUNCTION("6xVpy0Fdq+I", "libkernel", 1, "libkernel", 1, 1, _sigprocmask);
|
||||
LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate);
|
||||
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
|
||||
LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error);
|
||||
LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev);
|
||||
LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read);
|
||||
LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize);
|
||||
LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize);
|
||||
LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1,
|
||||
sceLibcHeapGetTraceInfo);
|
||||
LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write);
|
||||
LIB_FUNCTION("FN4gaPmuFV8", "libScePosix", 1, "libkernel", 1, 1, ps4__write);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
@ -492,8 +492,7 @@ int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
auto* memory = Core::Memory::Instance();
|
||||
memory->UnmapMemory(std::bit_cast<VAddr>(addr), len);
|
||||
return ORBIS_OK;
|
||||
return memory->UnmapMemory(std::bit_cast<VAddr>(addr), len);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) {
|
||||
|
@ -20,7 +20,7 @@ int PS4_SYSV_ABI sceKernelIsNeoMode() {
|
||||
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {
|
||||
int version = Common::ElfInfo::Instance().RawFirmwareVer();
|
||||
*ver = version;
|
||||
return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
|
||||
return (version >= 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGetCpumode() {
|
||||
@ -45,10 +45,11 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg
|
||||
|
||||
// Load PRX module and relocate any modules that import it.
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
u32 handle = linker->LoadModule(path, true);
|
||||
if (handle == -1) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
u32 handle = linker->FindByName(path);
|
||||
if (handle != -1) {
|
||||
return handle;
|
||||
}
|
||||
handle = linker->LoadModule(path, true);
|
||||
auto* module = linker->GetModule(handle);
|
||||
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.
|
||||
u64* param = module->GetProcParam<u64*>();
|
||||
ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]);
|
||||
module->Start(args, argp, param);
|
||||
s32 ret = module->Start(args, argp, param);
|
||||
if (pRes) {
|
||||
*pRes = ret;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
@ -104,6 +108,9 @@ s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags,
|
||||
LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags);
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* module = linker->FindByAddress(addr);
|
||||
if (!module) {
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
const auto mod_info = module->GetModuleInfoEx();
|
||||
|
||||
// Fill in module info.
|
||||
|
52
src/core/libraries/kernel/sync/mutex.cpp
Normal file
52
src/core/libraries/kernel/sync/mutex.cpp
Normal 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
|
80
src/core/libraries/kernel/sync/mutex.h
Normal file
80
src/core/libraries/kernel/sync/mutex.h
Normal 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
|
167
src/core/libraries/kernel/sync/semaphore.h
Normal file
167
src/core/libraries/kernel/sync/semaphore.h
Normal 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
|
@ -177,7 +177,7 @@ int PS4_SYSV_ABI posix_pthread_cond_reltimedwait_np(PthreadCondT* cond, PthreadM
|
||||
return cvp->Wait(mutex, THR_RELTIME, usec);
|
||||
}
|
||||
|
||||
int PthreadCond::Signal() {
|
||||
int PthreadCond::Signal(Pthread* thread) {
|
||||
Pthread* curthread = g_curthread;
|
||||
|
||||
SleepqLock(this);
|
||||
@ -187,11 +187,12 @@ int PthreadCond::Signal() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Pthread* td = sq->sq_blocked.front();
|
||||
Pthread* td = thread ? thread : sq->sq_blocked.front();
|
||||
|
||||
PthreadMutex* mp = td->mutex_obj;
|
||||
has_user_waiters = SleepqRemove(sq, td);
|
||||
|
||||
std::binary_semaphore* waddr = nullptr;
|
||||
BinarySemaphore* waddr = nullptr;
|
||||
if (mp->m_owner == curthread) {
|
||||
if (curthread->nwaiter_defer >= Pthread::MaxDeferWaiters) {
|
||||
curthread->WakeAll();
|
||||
@ -211,7 +212,7 @@ int PthreadCond::Signal() {
|
||||
|
||||
struct BroadcastArg {
|
||||
Pthread* curthread;
|
||||
std::binary_semaphore* waddrs[Pthread::MaxDeferWaiters];
|
||||
BinarySemaphore* waddrs[Pthread::MaxDeferWaiters];
|
||||
int count;
|
||||
};
|
||||
|
||||
@ -262,7 +263,13 @@ int PthreadCond::Broadcast() {
|
||||
int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) {
|
||||
PthreadCond* cvp{};
|
||||
CHECK_AND_INIT_COND
|
||||
return cvp->Signal();
|
||||
return cvp->Signal(nullptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_signalto_np(PthreadCondT* cond, Pthread* thread) {
|
||||
PthreadCond* cvp{};
|
||||
CHECK_AND_INIT_COND
|
||||
return cvp->Signal(thread);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) {
|
||||
@ -358,6 +365,8 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) {
|
||||
ORBIS(posix_pthread_cond_reltimedwait_np));
|
||||
LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_cond_destroy));
|
||||
LIB_FUNCTION("o69RpYO-Mu0", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_cond_signalto_np));
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
@ -118,7 +118,6 @@ public:
|
||||
}
|
||||
|
||||
m_bits |= bits;
|
||||
|
||||
m_cond_var.notify_all();
|
||||
}
|
||||
|
||||
@ -133,6 +132,33 @@ public:
|
||||
m_bits &= bits;
|
||||
}
|
||||
|
||||
void Cancel(u64 setPattern, int* numWaitThreads) {
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
while (m_status != Status::Set) {
|
||||
m_mutex.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
if (numWaitThreads) {
|
||||
*numWaitThreads = m_waiting_threads;
|
||||
}
|
||||
|
||||
m_status = Status::Canceled;
|
||||
m_bits = setPattern;
|
||||
|
||||
m_cond_var.notify_all();
|
||||
|
||||
while (m_waiting_threads > 0) {
|
||||
m_mutex.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
m_status = Status::Set;
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Status { Set, Canceled, Deleted };
|
||||
|
||||
@ -233,7 +259,8 @@ int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern,
|
||||
int* pNumWaitThreads) {
|
||||
LOG_ERROR(Kernel_Event, "(STUBBED) called");
|
||||
LOG_DEBUG(Kernel_Event, "called");
|
||||
ef->Cancel(setPattern, pNumWaitThreads);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
#ifdef _WIN64
|
||||
#include "common/ntapi.h"
|
||||
#else
|
||||
#include <signal.h>
|
||||
#endif
|
||||
@ -64,6 +65,34 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) {
|
||||
handler(POSIX_SIGUSR1, &ctx);
|
||||
}
|
||||
}
|
||||
#else
|
||||
void ExceptionHandler(void* arg1, void* arg2, void* arg3, PCONTEXT context) {
|
||||
const char* thrName = (char*)arg1;
|
||||
LOG_INFO(Lib_Kernel, "Exception raised successfully on thread '{}'", thrName);
|
||||
const auto handler = Handlers[POSIX_SIGUSR1];
|
||||
if (handler) {
|
||||
auto ctx = Ucontext{};
|
||||
ctx.uc_mcontext.mc_r8 = context->R8;
|
||||
ctx.uc_mcontext.mc_r9 = context->R9;
|
||||
ctx.uc_mcontext.mc_r10 = context->R10;
|
||||
ctx.uc_mcontext.mc_r11 = context->R11;
|
||||
ctx.uc_mcontext.mc_r12 = context->R12;
|
||||
ctx.uc_mcontext.mc_r13 = context->R13;
|
||||
ctx.uc_mcontext.mc_r14 = context->R14;
|
||||
ctx.uc_mcontext.mc_r15 = context->R15;
|
||||
ctx.uc_mcontext.mc_rdi = context->Rdi;
|
||||
ctx.uc_mcontext.mc_rsi = context->Rsi;
|
||||
ctx.uc_mcontext.mc_rbp = context->Rbp;
|
||||
ctx.uc_mcontext.mc_rbx = context->Rbx;
|
||||
ctx.uc_mcontext.mc_rdx = context->Rdx;
|
||||
ctx.uc_mcontext.mc_rax = context->Rax;
|
||||
ctx.uc_mcontext.mc_rcx = context->Rcx;
|
||||
ctx.uc_mcontext.mc_rsp = context->Rsp;
|
||||
ctx.uc_mcontext.mc_fs = context->SegFs;
|
||||
ctx.uc_mcontext.mc_gs = context->SegGs;
|
||||
handler(POSIX_SIGUSR1, &ctx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelExceptionHandler handler) {
|
||||
@ -73,9 +102,7 @@ int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelException
|
||||
}
|
||||
ASSERT_MSG(!Handlers[POSIX_SIGUSR1], "Invalid parameters");
|
||||
Handlers[POSIX_SIGUSR1] = handler;
|
||||
#ifdef _WIN64
|
||||
UNREACHABLE_MSG("Missing exception implementation");
|
||||
#else
|
||||
#ifndef _WIN64
|
||||
struct sigaction act = {};
|
||||
act.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
act.sa_sigaction = reinterpret_cast<decltype(act.sa_sigaction)>(SigactionHandler);
|
||||
@ -91,9 +118,7 @@ int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) {
|
||||
}
|
||||
ASSERT_MSG(Handlers[POSIX_SIGUSR1], "Invalid parameters");
|
||||
Handlers[POSIX_SIGUSR1] = nullptr;
|
||||
#ifdef _WIN64
|
||||
UNREACHABLE_MSG("Missing exception implementation");
|
||||
#else
|
||||
#ifndef _WIN64
|
||||
struct sigaction act = {};
|
||||
act.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
act.sa_sigaction = nullptr;
|
||||
@ -103,13 +128,18 @@ int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) {
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) {
|
||||
LOG_ERROR(Lib_Kernel, "Raising exception");
|
||||
LOG_WARNING(Lib_Kernel, "Raising exception on thread '{}'", thread->name);
|
||||
ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!");
|
||||
#ifdef _WIN64
|
||||
UNREACHABLE_MSG("Missing exception implementation");
|
||||
#else
|
||||
#ifndef _WIN64
|
||||
pthread_t pthr = *reinterpret_cast<pthread_t*>(thread->native_thr.GetHandle());
|
||||
pthread_kill(pthr, SIGUSR2);
|
||||
#else
|
||||
USER_APC_OPTION option;
|
||||
option.UserApcFlags = QueueUserApcFlagsSpecialUserApc;
|
||||
|
||||
u64 res = NtQueueApcThreadEx(reinterpret_cast<HANDLE>(thread->native_thr.GetHandle()), option,
|
||||
ExceptionHandler, (void*)thread->name.c_str(), nullptr, nullptr);
|
||||
ASSERT(res == 0);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -206,6 +206,7 @@ static void RunThread(void* arg) {
|
||||
DebugState.AddCurrentThreadToGuestList();
|
||||
|
||||
/* Run the current thread's start routine with argument: */
|
||||
curthread->native_thr.Initialize();
|
||||
void* ret = Core::ExecuteGuest(curthread->start_routine, curthread->arg);
|
||||
|
||||
/* Remove thread from tracking */
|
||||
@ -242,6 +243,13 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
|
||||
static int TidCounter = 1;
|
||||
new_thread->tid = ++TidCounter;
|
||||
|
||||
if (new_thread->attr.stackaddr_attr == 0) {
|
||||
/* Enforce minimum stack size of 64 KB */
|
||||
static constexpr size_t MinimumStack = 64_KB;
|
||||
auto& stacksize = new_thread->attr.stacksize_attr;
|
||||
stacksize = std::max(stacksize, MinimumStack);
|
||||
}
|
||||
|
||||
if (thread_state->CreateStack(&new_thread->attr) != 0) {
|
||||
/* Insufficient memory to create a stack: */
|
||||
thread_state->Free(curthread, new_thread);
|
||||
@ -280,7 +288,7 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
|
||||
(*thread) = new_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);
|
||||
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", 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) {
|
||||
LOG_INFO(Kernel_Pthread, "name = {}", name);
|
||||
Common::SetThreadName(reinterpret_cast<void*>(thread->native_thr.GetHandle()), name);
|
||||
thread->name = name;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -412,6 +421,33 @@ int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* poli
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_setschedparam(PthreadT pthread, SchedPolicy policy,
|
||||
const SchedParam* param) {
|
||||
if (pthread == nullptr || param == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
auto* thread_state = ThrState::Instance();
|
||||
if (pthread == g_curthread) {
|
||||
g_curthread->lock.lock();
|
||||
} else if (int ret = thread_state->FindThread(pthread, /*include dead*/ 0); ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pthread->attr.sched_policy == policy &&
|
||||
(policy == SchedPolicy::Other || pthread->attr.prio == param->sched_priority)) {
|
||||
pthread->attr.prio = param->sched_priority;
|
||||
pthread->lock.unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: _thr_setscheduler
|
||||
pthread->attr.sched_policy = policy;
|
||||
pthread->attr.prio = param->sched_priority;
|
||||
pthread->lock.unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) {
|
||||
SchedParam param;
|
||||
SchedPolicy policy;
|
||||
@ -495,6 +531,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate);
|
||||
LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio);
|
||||
LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam);
|
||||
LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setschedparam);
|
||||
LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", 1, 1, sched_yield);
|
||||
|
||||
// Posix-Kernel
|
||||
@ -510,6 +547,8 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join));
|
||||
LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_getschedparam));
|
||||
LIB_FUNCTION("oIRFTjoILbg", "libkernel", 1, "libkernel", 1, 1,
|
||||
ORBIS(posix_pthread_setschedparam));
|
||||
LIB_FUNCTION("How7B8Oet6k", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_getname_np));
|
||||
LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit);
|
||||
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "common/enum.h"
|
||||
#include "core/libraries/kernel/sync/mutex.h"
|
||||
#include "core/libraries/kernel/sync/semaphore.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/thread.h"
|
||||
#include "core/tls.h"
|
||||
@ -44,7 +46,7 @@ enum class PthreadMutexProt : u32 {
|
||||
};
|
||||
|
||||
struct PthreadMutex {
|
||||
std::timed_mutex m_lock;
|
||||
TimedMutex m_lock;
|
||||
PthreadMutexFlags m_flags;
|
||||
Pthread* m_owner;
|
||||
int m_count;
|
||||
@ -121,7 +123,7 @@ struct PthreadCond {
|
||||
|
||||
int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, u64 usec = 0);
|
||||
|
||||
int Signal();
|
||||
int Signal(Pthread* thread);
|
||||
int Broadcast();
|
||||
};
|
||||
using PthreadCondT = PthreadCond*;
|
||||
@ -259,7 +261,7 @@ struct Pthread {
|
||||
int refcount;
|
||||
PthreadEntryFunc start_routine;
|
||||
void* arg;
|
||||
Core::Thread native_thr;
|
||||
Core::NativeThread native_thr;
|
||||
PthreadAttr attr;
|
||||
bool cancel_enable;
|
||||
bool cancel_pending;
|
||||
@ -288,14 +290,14 @@ struct Pthread {
|
||||
int report_events;
|
||||
int event_mask;
|
||||
std::string name;
|
||||
std::binary_semaphore wake_sema{0};
|
||||
BinarySemaphore wake_sema{0};
|
||||
SleepQueue* sleepqueue;
|
||||
void* wchan;
|
||||
PthreadMutex* mutex_obj;
|
||||
bool will_sleep;
|
||||
bool has_user_waiters;
|
||||
int nwaiter_defer;
|
||||
std::binary_semaphore* defer_waiters[MaxDeferWaiters];
|
||||
BinarySemaphore* defer_waiters[MaxDeferWaiters];
|
||||
|
||||
bool InCritical() const noexcept {
|
||||
return locklevel > 0 || critical_count > 0;
|
||||
|
@ -177,13 +177,13 @@ int PS4_SYSV_ABI posix_pthread_rwlock_unlock(PthreadRwlockT* rwlock) {
|
||||
}
|
||||
|
||||
if (prwlock->owner == curthread) {
|
||||
prwlock->lock.unlock();
|
||||
prwlock->owner = nullptr;
|
||||
prwlock->lock.unlock();
|
||||
} else {
|
||||
prwlock->lock.unlock_shared();
|
||||
if (prwlock->owner == nullptr) {
|
||||
curthread->rdlock_count--;
|
||||
}
|
||||
prwlock->lock.unlock_shared();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -6,7 +6,10 @@
|
||||
#include <mutex>
|
||||
#include <semaphore>
|
||||
|
||||
#include "core/libraries/kernel/sync/semaphore.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/slot_vector.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/orbis_error.h"
|
||||
#include "core/libraries/kernel/posix_error.h"
|
||||
@ -21,7 +24,7 @@ constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF;
|
||||
struct PthreadSem {
|
||||
explicit PthreadSem(s32 value_) : semaphore{value_}, value{value_} {}
|
||||
|
||||
std::counting_semaphore<ORBIS_KERNEL_SEM_VALUE_MAX> semaphore;
|
||||
CountingSemaphore semaphore;
|
||||
std::atomic<s32> value;
|
||||
};
|
||||
|
||||
@ -75,7 +78,7 @@ public:
|
||||
it = wait_list.erase(it);
|
||||
token_count -= waiter->need_count;
|
||||
waiter->was_signaled = true;
|
||||
waiter->cv.notify_one();
|
||||
waiter->sem.release();
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -88,7 +91,7 @@ public:
|
||||
}
|
||||
for (auto* waiter : wait_list) {
|
||||
waiter->was_cancled = true;
|
||||
waiter->cv.notify_one();
|
||||
waiter->sem.release();
|
||||
}
|
||||
wait_list.clear();
|
||||
token_count = set_count < 0 ? init_count : set_count;
|
||||
@ -99,25 +102,29 @@ public:
|
||||
std::scoped_lock lk{mutex};
|
||||
for (auto* waiter : wait_list) {
|
||||
waiter->was_deleted = true;
|
||||
waiter->cv.notify_one();
|
||||
waiter->sem.release();
|
||||
}
|
||||
wait_list.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
struct WaitingThread {
|
||||
std::condition_variable cv;
|
||||
BinarySemaphore sem;
|
||||
u32 priority;
|
||||
s32 need_count;
|
||||
std::string thr_name;
|
||||
bool was_signaled{};
|
||||
bool was_deleted{};
|
||||
bool was_cancled{};
|
||||
|
||||
explicit WaitingThread(s32 need_count, bool is_fifo) : need_count{need_count} {
|
||||
explicit WaitingThread(s32 need_count, bool is_fifo)
|
||||
: sem{0}, priority{0}, need_count{need_count} {
|
||||
// Retrieve calling thread priority for sorting into waiting threads list.
|
||||
if (!is_fifo) {
|
||||
priority = g_curthread->attr.prio;
|
||||
}
|
||||
|
||||
thr_name = g_curthread->name;
|
||||
}
|
||||
|
||||
int GetResult(bool timed_out) {
|
||||
@ -134,24 +141,26 @@ public:
|
||||
}
|
||||
|
||||
int Wait(std::unique_lock<std::mutex>& lk, u32* timeout) {
|
||||
lk.unlock();
|
||||
if (!timeout) {
|
||||
// Wait indefinitely until we are woken up.
|
||||
cv.wait(lk);
|
||||
sem.acquire();
|
||||
lk.lock();
|
||||
return GetResult(false);
|
||||
}
|
||||
// Wait until timeout runs out, recording how much remaining time there was.
|
||||
const auto start = std::chrono::high_resolution_clock::now();
|
||||
const auto signaled = cv.wait_for(lk, std::chrono::microseconds(*timeout),
|
||||
[this] { return was_signaled; });
|
||||
sem.try_acquire_for(std::chrono::microseconds(*timeout));
|
||||
const auto end = std::chrono::high_resolution_clock::now();
|
||||
const auto time =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
|
||||
if (signaled) {
|
||||
lk.lock();
|
||||
if (was_signaled) {
|
||||
*timeout -= time;
|
||||
} else {
|
||||
*timeout = 0;
|
||||
}
|
||||
return GetResult(!signaled);
|
||||
return GetResult(!was_signaled);
|
||||
}
|
||||
};
|
||||
|
||||
@ -180,7 +189,9 @@ public:
|
||||
bool is_fifo;
|
||||
};
|
||||
|
||||
using OrbisKernelSema = OrbisSem*;
|
||||
using OrbisKernelSema = Common::SlotId;
|
||||
|
||||
static Common::SlotVector<std::unique_ptr<OrbisSem>> orbis_sems;
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u32 attr,
|
||||
s32 initCount, s32 maxCount, const void* pOptParam) {
|
||||
@ -188,46 +199,48 @@ s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u3
|
||||
LOG_ERROR(Lib_Kernel, "Semaphore creation parameters are invalid!");
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
*sem = new OrbisSem(initCount, maxCount, pName, attr == 1);
|
||||
*sem = orbis_sems.insert(
|
||||
std::move(std::make_unique<OrbisSem>(initCount, maxCount, pName, attr == 1)));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelWaitSema(OrbisKernelSema sem, s32 needCount, u32* pTimeout) {
|
||||
if (!sem) {
|
||||
if (!orbis_sems.is_allocated(sem)) {
|
||||
return ORBIS_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
return sem->Wait(true, needCount, pTimeout);
|
||||
return orbis_sems[sem]->Wait(true, needCount, pTimeout);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelSignalSema(OrbisKernelSema sem, s32 signalCount) {
|
||||
if (!sem) {
|
||||
if (!orbis_sems.is_allocated(sem)) {
|
||||
return ORBIS_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
if (!sem->Signal(signalCount)) {
|
||||
if (!orbis_sems[sem]->Signal(signalCount)) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelPollSema(OrbisKernelSema sem, s32 needCount) {
|
||||
if (!sem) {
|
||||
if (!orbis_sems.is_allocated(sem)) {
|
||||
return ORBIS_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
return sem->Wait(false, needCount, nullptr);
|
||||
return orbis_sems[sem]->Wait(false, needCount, nullptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCancelSema(OrbisKernelSema sem, s32 setCount, s32* pNumWaitThreads) {
|
||||
if (!sem) {
|
||||
if (!orbis_sems.is_allocated(sem)) {
|
||||
return ORBIS_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
return sem->Cancel(setCount, pNumWaitThreads);
|
||||
return orbis_sems[sem]->Cancel(setCount, pNumWaitThreads);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) {
|
||||
if (!sem) {
|
||||
if (!orbis_sems.is_allocated(sem)) {
|
||||
return ORBIS_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
sem->Delete();
|
||||
orbis_sems[sem]->Delete();
|
||||
orbis_sems.erase(sem);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -242,6 +255,16 @@ int PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, int pshared, u32 value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
delete *sem;
|
||||
*sem = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
@ -292,16 +315,6 @@ int PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
}
|
||||
delete *sem;
|
||||
*sem = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
@ -313,6 +326,77 @@ int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI scePthreadSemInit(PthreadSem** sem, int flag, u32 value, const char* name) {
|
||||
if (flag != 0) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
s32 ret = posix_sem_init(sem, 0, value);
|
||||
if (ret != 0) {
|
||||
return ErrnoToSceKernelError(*__Error());
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI scePthreadSemDestroy(PthreadSem** sem) {
|
||||
s32 ret = posix_sem_destroy(sem);
|
||||
if (ret != 0) {
|
||||
return ErrnoToSceKernelError(*__Error());
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI scePthreadSemWait(PthreadSem** sem) {
|
||||
s32 ret = posix_sem_wait(sem);
|
||||
if (ret != 0) {
|
||||
return ErrnoToSceKernelError(*__Error());
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI scePthreadSemTrywait(PthreadSem** sem) {
|
||||
s32 ret = posix_sem_trywait(sem);
|
||||
if (ret != 0) {
|
||||
return ErrnoToSceKernelError(*__Error());
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI scePthreadSemTimedwait(PthreadSem** sem, u32 usec) {
|
||||
OrbisKernelTimespec time{};
|
||||
time.tv_sec = usec / 1000000;
|
||||
time.tv_nsec = (usec % 1000000) * 1000;
|
||||
|
||||
s32 ret = posix_sem_timedwait(sem, &time);
|
||||
if (ret != 0) {
|
||||
return ErrnoToSceKernelError(*__Error());
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI scePthreadSemPost(PthreadSem** sem) {
|
||||
s32 ret = posix_sem_post(sem);
|
||||
if (ret != 0) {
|
||||
return ErrnoToSceKernelError(*__Error());
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI scePthreadSemGetvalue(PthreadSem** sem, int* sval) {
|
||||
s32 ret = posix_sem_getvalue(sem, sval);
|
||||
if (ret != 0) {
|
||||
return ErrnoToSceKernelError(*__Error());
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterSemaphore(Core::Loader::SymbolsResolver* sym) {
|
||||
// Orbis
|
||||
LIB_FUNCTION("188x57JYp0g", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateSema);
|
||||
@ -324,12 +408,20 @@ void RegisterSemaphore(Core::Loader::SymbolsResolver* sym) {
|
||||
|
||||
// Posix
|
||||
LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init);
|
||||
LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy);
|
||||
LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait);
|
||||
LIB_FUNCTION("WBWzsRifCEA", "libScePosix", 1, "libkernel", 1, 1, posix_sem_trywait);
|
||||
LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait);
|
||||
LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post);
|
||||
LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy);
|
||||
LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue);
|
||||
|
||||
LIB_FUNCTION("GEnUkDZoUwY", "libkernel", 1, "libkernel", 1, 1, scePthreadSemInit);
|
||||
LIB_FUNCTION("Vwc+L05e6oE", "libkernel", 1, "libkernel", 1, 1, scePthreadSemDestroy);
|
||||
LIB_FUNCTION("C36iRE0F5sE", "libkernel", 1, "libkernel", 1, 1, scePthreadSemWait);
|
||||
LIB_FUNCTION("H2a+IN9TP0E", "libkernel", 1, "libkernel", 1, 1, scePthreadSemTrywait);
|
||||
LIB_FUNCTION("fjN6NQHhK8k", "libkernel", 1, "libkernel", 1, 1, scePthreadSemTimedwait);
|
||||
LIB_FUNCTION("aishVAiFaYM", "libkernel", 1, "libkernel", 1, 1, scePthreadSemPost);
|
||||
LIB_FUNCTION("DjpBvGlaWbQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSemGetvalue);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
@ -52,7 +52,22 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() {
|
||||
|
||||
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
|
||||
#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;
|
||||
#else
|
||||
timespec start;
|
||||
|
@ -1,11 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/system/commondialog.h"
|
||||
#include "magic_enum.hpp"
|
||||
#include "savedatadialog.h"
|
||||
#include "savedatadialog_ui.h"
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/elf_info.h"
|
||||
#include "common/singleton.h"
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <mutex>
|
||||
#include <semaphore>
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "save_backup.h"
|
||||
#include "save_instance.h"
|
||||
@ -79,7 +79,7 @@ static void backup(const std::filesystem::path& dir_name) {
|
||||
}
|
||||
|
||||
static void BackupThreadBody() {
|
||||
Common::SetCurrentThreadName("shadPS4:SaveData_BackupThread");
|
||||
Common::SetCurrentThreadName("shadPS4:SaveData:BackupThread");
|
||||
while (g_backup_status != WorkerStatus::Stopping) {
|
||||
g_backup_status = WorkerStatus::Waiting;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
|
@ -66,7 +66,7 @@ static void SaveFileSafe(void* buf, size_t count, const std::filesystem::path& p
|
||||
}
|
||||
|
||||
[[noreturn]] void SaveThreadLoop() {
|
||||
Common::SetCurrentThreadName("shadPS4:SaveData_SaveDataMemoryThread");
|
||||
Common::SetCurrentThreadName("shadPS4:SaveData:SaveDataMemoryThread");
|
||||
std::mutex mtx;
|
||||
while (true) {
|
||||
{
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <core/libraries/system/msgdialog_ui.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/cstring.h"
|
||||
|
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#define MAGIC_ENUM_RANGE_MIN 0
|
||||
#define MAGIC_ENUM_RANGE_MAX 300
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
|
@ -7,16 +7,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/videodec/videodec_error.h"
|
||||
|
||||
// The av_err2str macro in libavutil/error.h does not play nice with C++
|
||||
#ifdef av_err2str
|
||||
#undef av_err2str
|
||||
#include <string>
|
||||
av_always_inline std::string av_err2string(int errnum) {
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum);
|
||||
}
|
||||
#define av_err2str(err) av_err2string(err).c_str()
|
||||
#endif // av_err2str
|
||||
#include "common/support/avdec.h"
|
||||
|
||||
namespace Libraries::Vdec2 {
|
||||
|
||||
|
@ -8,16 +8,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/videodec/videodec_error.h"
|
||||
|
||||
// The av_err2str macro in libavutil/error.h does not play nice with C++
|
||||
#ifdef av_err2str
|
||||
#undef av_err2str
|
||||
#include <string>
|
||||
av_always_inline std::string av_err2string(int errnum) {
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum);
|
||||
}
|
||||
#define av_err2str(err) av_err2string(err).c_str()
|
||||
#endif // av_err2str
|
||||
#include "common/support/avdec.h"
|
||||
|
||||
namespace Libraries::Videodec {
|
||||
|
||||
|
@ -85,6 +85,15 @@ public:
|
||||
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 {
|
||||
return max_tls_index;
|
||||
}
|
||||
|
@ -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,
|
||||
int memory_type) {
|
||||
std::scoped_lock lk{mutex};
|
||||
alignment = alignment > 0 ? alignment : 16_KB;
|
||||
|
||||
auto dmem_area = FindDmemArea(search_start);
|
||||
|
||||
const auto is_suitable = [&] {
|
||||
const auto aligned_base = alignment > 0 ? Common::AlignUp(dmem_area->second.base, alignment)
|
||||
: dmem_area->second.base;
|
||||
const auto aligned_base = Common::AlignUp(dmem_area->second.base, alignment);
|
||||
const auto alignment_size = aligned_base - dmem_area->second.base;
|
||||
const auto remaining_size =
|
||||
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
|
||||
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.
|
||||
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.
|
||||
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
|
||||
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");
|
||||
}
|
||||
|
||||
void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
|
||||
s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
|
||||
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& vma_base = it->second;
|
||||
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,
|
||||
has_backing, readonly_file);
|
||||
TRACK_FREE(virtual_addr, "VMEM");
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
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_direct.Assign(vma.type == VMAType::Direct);
|
||||
info->is_stack.Assign(vma.type == VMAType::Stack);
|
||||
info->is_pooled.Assign(vma.type == VMAType::Pooled);
|
||||
info->is_committed.Assign(vma.type != VMAType::Free && vma.type != VMAType::Reserved &&
|
||||
vma.type != VMAType::PoolReserved);
|
||||
info->is_pooled.Assign(vma.type == VMAType::PoolReserved);
|
||||
info->is_committed.Assign(vma.type == VMAType::Pooled);
|
||||
vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size()));
|
||||
if (vma.type == VMAType::Direct) {
|
||||
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");
|
||||
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) {
|
||||
// If the requested address is below the mapped range, start search from the lowest address
|
||||
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;
|
||||
|
||||
return dmem_map.emplace_hint(std::next(dmem_handle), new_area.base, new_area);
|
||||
};
|
||||
}
|
||||
|
||||
int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut,
|
||||
void** directMemoryStartOut, void** directMemoryEndOut) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user