diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ceb915f6a..588236b14 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -76,18 +76,13 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- - name: Setup VS Environment
- uses: ilammy/msvc-dev-cmd@v1.13.0
- with:
- arch: amd64
-
- name: Configure CMake
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
@@ -111,7 +106,7 @@ jobs:
- name: Setup Qt
uses: jurplel/install-qt-action@v4
with:
- version: 6.9.0
+ version: 6.9.1
host: windows
target: desktop
arch: win64_msvc2022_64
@@ -130,18 +125,13 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-build
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- - name: Setup VS Environment
- uses: ilammy/msvc-dev-cmd@v1.13.0
- with:
- arch: amd64
-
- name: Configure CMake
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
@@ -186,7 +176,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{runner.os}}-sdl-cache-cmake-build
with:
@@ -228,7 +218,7 @@ jobs:
- name: Setup Qt
uses: jurplel/install-qt-action@v4
with:
- version: 6.9.0
+ version: 6.9.1
host: mac
target: desktop
arch: clang_64
@@ -247,7 +237,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{runner.os}}-qt-cache-cmake-build
with:
@@ -301,7 +291,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with:
@@ -362,7 +352,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-build
with:
@@ -409,7 +399,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-build
with:
@@ -445,7 +435,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.17
+ uses: hendrikmuhs/ccache-action@v1.2.18
env:
cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-build
with:
@@ -494,7 +484,7 @@ jobs:
with:
token: ${{ secrets.SHADPS4_TOKEN_REPO }}
name: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
- tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
+ tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}"
draft: false
prerelease: true
body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.fullhash }})"
@@ -530,14 +520,14 @@ jobs:
# Check if release already exists and get ID
release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
- "https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" | jq -r '.id')
+ "https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}" | jq -r '.id')
if [[ "$release_id" == "null" ]]; then
echo "Creating release in $REPO for $filename"
release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-d '{
- "tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
+ "tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}",
"name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
"draft": false,
"prerelease": true,
diff --git a/.gitmodules b/.gitmodules
index 065a4570f..25b5d307b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -30,10 +30,6 @@
path = externals/xbyak
url = https://github.com/herumi/xbyak.git
shallow = true
-[submodule "externals/winpthreads"]
- path = externals/winpthreads
- url = https://github.com/shadps4-emu/winpthreads.git
- shallow = true
[submodule "externals/magic_enum"]
path = externals/magic_enum
url = https://github.com/Neargye/magic_enum.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d8ca9e75c..243c84b2e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -54,9 +54,9 @@ else()
endif()
if (ARCHITECTURE STREQUAL "x86_64")
- # Target the same CPU architecture as the PS4, to maintain the same level of compatibility.
- # Exclude SSE4a as it is only available on AMD CPUs.
- add_compile_options(-march=btver2 -mtune=generic -mno-sse4a)
+ # Target x86-64-v3 CPU architecture as this is a good balance between supporting performance critical
+ # instructions like AVX2 and maintaining support for older CPUs.
+ add_compile_options(-march=x86-64-v3)
endif()
if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
@@ -134,6 +134,7 @@ if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "")
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
+ message("got remote: ${GIT_REMOTE_NAME}")
endif()
# If running in GitHub Actions and the above fails
@@ -177,7 +178,7 @@ if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "")
set(GIT_BRANCH "${GITHUB_BRANCH}")
elseif ("${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_REF}" STREQUAL "")
set(GIT_BRANCH "${GITHUB_REF}")
- else()
+ elseif("${GIT_BRANCH}" STREQUAL "")
message("couldn't find branch")
set(GIT_BRANCH "detached-head")
endif()
@@ -186,8 +187,8 @@ else()
string(FIND "${GIT_REMOTE_NAME}" "/" INDEX)
if (INDEX GREATER -1)
string(SUBSTRING "${GIT_REMOTE_NAME}" 0 "${INDEX}" GIT_REMOTE_NAME)
- else()
- # If no remote is present (only a branch name), default to origin
+ elseif("${GIT_REMOTE_NAME}" STREQUAL "")
+ message("reset to origin")
set(GIT_REMOTE_NAME "origin")
endif()
endif()
@@ -202,7 +203,7 @@ execute_process(
# Set Version
set(EMULATOR_VERSION_MAJOR "0")
-set(EMULATOR_VERSION_MINOR "8")
+set(EMULATOR_VERSION_MINOR "9")
set(EMULATOR_VERSION_PATCH "1")
set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}")
@@ -226,7 +227,7 @@ 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.4.309 CONFIG)
+find_package(VulkanHeaders 1.4.314 CONFIG)
find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE)
@@ -239,13 +240,6 @@ if (APPLE)
endif()
list(POP_BACK CMAKE_MODULE_PATH)
-# Note: Windows always has these functions through winpthreads
-include(CheckSymbolExists)
-check_symbol_exists(pthread_mutex_timedlock "pthread.h" HAVE_PTHREAD_MUTEX_TIMEDLOCK)
-if(HAVE_PTHREAD_MUTEX_TIMEDLOCK OR WIN32)
- add_compile_options(-DHAVE_PTHREAD_MUTEX_TIMEDLOCK)
-endif()
-
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
# libc++ requires -fexperimental-library to enable std::jthread and std::stop_token support.
include(CheckCXXSymbolExists)
@@ -302,6 +296,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp
set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
src/core/libraries/audio/audioin.h
+ src/core/libraries/voice/voice.cpp
+ src/core/libraries/voice/voice.h
src/core/libraries/audio/audioout.cpp
src/core/libraries/audio/audioout.h
src/core/libraries/audio/audioout_backend.h
@@ -416,6 +412,7 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/save_data/save_memory.h
src/core/libraries/save_data/savedata.cpp
src/core/libraries/save_data/savedata.h
+ src/core/libraries/save_data/savedata_error.h
src/core/libraries/save_data/dialog/savedatadialog.cpp
src/core/libraries/save_data/dialog/savedatadialog.h
src/core/libraries/save_data/dialog/savedatadialog_ui.cpp
@@ -601,6 +598,17 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
src/core/libraries/signin_dialog/signindialog.h
)
+set(CAMERA_LIBS src/core/libraries/camera/camera.cpp
+ src/core/libraries/camera/camera.h
+ src/core/libraries/camera/camera_error.h
+)
+
+set(COMPANION_LIBS src/core/libraries/companion/companion_httpd.cpp
+ src/core/libraries/companion/companion_httpd.h
+ src/core/libraries/companion/companion_util.cpp
+ src/core/libraries/companion/companion_util.h
+ src/core/libraries/companion/companion_error.h
+)
set(DEV_TOOLS src/core/devtools/layer.cpp
src/core/devtools/layer.h
src/core/devtools/options.cpp
@@ -618,6 +626,8 @@ set(DEV_TOOLS src/core/devtools/layer.cpp
src/core/devtools/widget/imgui_memory_editor.h
src/core/devtools/widget/memory_map.cpp
src/core/devtools/widget/memory_map.h
+ src/core/devtools/widget/module_list.cpp
+ src/core/devtools/widget/module_list.h
src/core/devtools/widget/reg_popup.cpp
src/core/devtools/widget/reg_popup.h
src/core/devtools/widget/reg_view.cpp
@@ -671,6 +681,8 @@ set(COMMON src/common/logging/backend.cpp
src/common/polyfill_thread.h
src/common/rdtsc.cpp
src/common/rdtsc.h
+ src/common/recursive_lock.cpp
+ src/common/recursive_lock.h
src/common/sha1.h
src/common/signal_context.h
src/common/signal_context.cpp
@@ -765,6 +777,8 @@ set(CORE src/core/aerolib/stubs.cpp
${FIBER_LIB}
${VDEC_LIB}
${VR_LIBS}
+ ${CAMERA_LIBS}
+ ${COMPANION_LIBS}
${DEV_TOOLS}
src/core/debug_state.cpp
src/core/debug_state.h
@@ -857,8 +871,10 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/ir/passes/ring_access_elimination.cpp
src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp
src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp
+ src/shader_recompiler/ir/passes/shared_memory_simplify_pass.cpp
src/shader_recompiler/ir/passes/shared_memory_to_storage_pass.cpp
src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp
+ src/shader_recompiler/ir/abstract_syntax_list.cpp
src/shader_recompiler/ir/abstract_syntax_list.h
src/shader_recompiler/ir/attribute.cpp
src/shader_recompiler/ir/attribute.h
@@ -952,6 +968,7 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/texture_cache/tile_manager.cpp
src/video_core/texture_cache/tile_manager.h
src/video_core/texture_cache/types.h
+ src/video_core/texture_cache/host_compatibility.cpp
src/video_core/texture_cache/host_compatibility.h
src/video_core/page_manager.cpp
src/video_core/page_manager.h
@@ -1041,6 +1058,10 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
src/qt_gui/settings_dialog.h
src/qt_gui/settings_dialog.ui
src/qt_gui/main.cpp
+ src/qt_gui/gui_settings.cpp
+ src/qt_gui/gui_settings.h
+ src/qt_gui/settings.cpp
+ src/qt_gui/settings.h
${EMULATOR}
${RESOURCE_FILES}
${TRANSLATIONS}
@@ -1091,9 +1112,13 @@ if (ENABLE_DISCORD_RPC)
target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC)
endif()
-# Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND ENABLE_USERFAULTFD)
- target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD)
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+ # Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704
+ if (ENABLE_USERFAULTFD)
+ target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD)
+ endif()
+
+ target_link_libraries(shadps4 PRIVATE uuid)
endif()
if (APPLE)
@@ -1102,6 +1127,10 @@ if (APPLE)
set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d")
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}")
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH})
+
+ add_custom_command(
+ OUTPUT ${MVK_DST}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST})
else()
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path")
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR})
@@ -1112,9 +1141,6 @@ if (APPLE)
set(MVK_ICD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json)
set(MVK_ICD_DST ${MVK_DST}/MoltenVK_icd.json)
- add_custom_command(
- OUTPUT ${MVK_DST}
- COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST})
add_custom_command(
OUTPUT ${MVK_ICD_DST}
DEPENDS ${MVK_ICD_SRC} ${MVK_DST}
@@ -1129,17 +1155,13 @@ if (APPLE)
if (ARCHITECTURE STREQUAL "x86_64")
# Reserve system-managed memory space.
- target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000)
+ target_link_options(shadps4 PRIVATE -Wl,-ld_classic,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000)
endif()
# Replacement for std::chrono::time_zone
target_link_libraries(shadps4 PRIVATE date::date-tz)
endif()
-if (NOT ENABLE_QT_GUI)
- target_link_libraries(shadps4 PRIVATE SDL3::SDL3)
-endif()
-
if (ENABLE_QT_GUI)
target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
add_definitions(-DENABLE_QT_GUI)
@@ -1149,7 +1171,7 @@ if (ENABLE_QT_GUI)
endif()
if (WIN32)
- target_link_libraries(shadps4 PRIVATE mincore winpthreads)
+ target_link_libraries(shadps4 PRIVATE mincore)
if (MSVC)
# MSVC likes putting opinions on what people can use, disable:
diff --git a/README.md b/README.md
index 985bba586..22fc27a33 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
**shadPS4** is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++.
-If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/Quickstart.md).\
+If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/wiki/I.-Quick-start-%5BUsers%5D).\
To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility).\
To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).\
To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/).\
@@ -124,8 +124,8 @@ Keyboard and mouse inputs can be customized in the settings menu by clicking the
# Firmware files
-shadPS4 can load some PlayStation 4 firmware files, these must be dumped from your legally owned PlayStation 4 console.\
-The following firmware modules are supported and must be placed in shadPS4's `user/sys_modules` folder.
+shadPS4 can load some PlayStation 4 firmware files, these must be dumped from your legally owned PlayStation 4 console.
+The following firmware modules are supported and must be placed in shadPS4's `sys_modules` folder.
@@ -138,8 +138,7 @@ The following firmware modules are supported and must be placed in shadPS4's `us
> [!Caution]
-> The above modules are required to run the games properly and must be extracted from your PlayStation 4.\
-> **We do not provide any information or support on how to do this**.
+> The above modules are required to run the games properly and must be extracted from your PlayStation 4.
@@ -148,7 +147,7 @@ The following firmware modules are supported and must be placed in shadPS4's `us
- [**georgemoralis**](https://github.com/georgemoralis)
- [**psucien**](https://github.com/psucien)
- [**viniciuslrangel**](https://github.com/viniciuslrangel)
-- [**roamic**](https://github.com/vladmikhalin)
+- [**roamic**](https://github.com/roamic)
- [**squidbus**](https://github.com/squidbus)
- [**frodo**](https://github.com/baggins183)
- [**Stephen Miller**](https://github.com/StevenMiller123)
@@ -158,7 +157,7 @@ Logo is done by [**Xphalnos**](https://github.com/Xphalnos)
# Contributing
-If you want to contribute, please look the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.\
+If you want to contribute, please read the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.\
Open a PR and we'll check it :)
# Translations
diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml
index 9f7b4f9c5..493dc0df6 100644
--- a/dist/net.shadps4.shadPS4.metainfo.xml
+++ b/dist/net.shadps4.shadPS4.metainfo.xml
@@ -37,7 +37,10 @@
Game
-
+
+ https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.9.0
+
+
https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.8.0
diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md
index 62df95e71..e2145ebbd 100644
--- a/documents/Quickstart/Quickstart.md
+++ b/documents/Quickstart/Quickstart.md
@@ -21,9 +21,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
- A processor with at least 4 cores and 6 threads
- Above 2.5 GHz frequency
-- A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM
+- A CPU supporting the x86-64-v3 baseline.
- **Intel**: Haswell generation or newer
- - **AMD**: Jaguar generation or newer
+ - **AMD**: Excavator generation or newer
- **Apple**: Rosetta 2 on macOS 15.4 or newer
### GPU
@@ -55,4 +55,4 @@ To configure the emulator, you can go through the interface and go to "settings"
You can also configure the emulator by editing the `config.toml` file located in the `user` folder created after the application is started (Mostly useful if you are using the SDL version).
Some settings may be related to more technical development and debugging.\
-For more information on this, see [**Debugging**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration).
\ No newline at end of file
+For more information on this, see [**Debugging**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration).
diff --git a/documents/building-linux.md b/documents/building-linux.md
index cdc8ba12f..61d067881 100644
--- a/documents/building-linux.md
+++ b/documents/building-linux.md
@@ -25,11 +25,11 @@ sudo apt install build-essential clang git cmake libasound2-dev \
```bash
sudo dnf install clang git cmake libatomic alsa-lib-devel \
- pipewire-jack-audio-connection-kit-devel openal-devel \
+ pipewire-jack-audio-connection-kit-devel openal-soft-devel \
openssl-devel libevdev-devel libudev-devel libXext-devel \
qt6-qtbase-devel qt6-qtbase-private-devel \
qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \
- vulkan-devel vulkan-validation-layers libpng-devel
+ vulkan-devel vulkan-validation-layers libpng-devel libuuid-devel
```
#### Arch Linux
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index b92e13795..89b0fbfdd 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -137,12 +137,6 @@ if (NOT TARGET Zydis::Zydis)
add_subdirectory(zydis)
endif()
-# Winpthreads
-if (WIN32)
- add_subdirectory(winpthreads)
- target_include_directories(winpthreads INTERFACE winpthreads/include)
-endif()
-
# sirit
add_subdirectory(sirit)
if (WIN32)
diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK
index 87a8e8b13..00abd384c 160000
--- a/externals/MoltenVK/MoltenVK
+++ b/externals/MoltenVK/MoltenVK
@@ -1 +1 @@
-Subproject commit 87a8e8b13d4ad8835367fea1ebad1896d0460946
+Subproject commit 00abd384ce01cbd439045905d2fa6cf799dfa2f6
diff --git a/externals/MoltenVK/SPIRV-Cross b/externals/MoltenVK/SPIRV-Cross
index 791877574..1a69a919f 160000
--- a/externals/MoltenVK/SPIRV-Cross
+++ b/externals/MoltenVK/SPIRV-Cross
@@ -1 +1 @@
-Subproject commit 7918775748c5e2f5c40d9918ce68825035b5a1e1
+Subproject commit 1a69a919fa302e92b337594bd0a8aaea61037d91
diff --git a/externals/sirit b/externals/sirit
index 09a1416ab..6b450704f 160000
--- a/externals/sirit
+++ b/externals/sirit
@@ -1 +1 @@
-Subproject commit 09a1416ab1b59ddfebd2618412f118f2004f3b2c
+Subproject commit 6b450704f6fedb9413d0c89a9eb59d028eb1e6c0
diff --git a/externals/vulkan-headers b/externals/vulkan-headers
index 5ceb9ed48..9c77de5c3 160000
--- a/externals/vulkan-headers
+++ b/externals/vulkan-headers
@@ -1 +1 @@
-Subproject commit 5ceb9ed481e58e705d0d9b5326537daedd06b97d
+Subproject commit 9c77de5c3dd216f28e407eec65ed9c0a296c1f74
diff --git a/externals/winpthreads b/externals/winpthreads
deleted file mode 160000
index f35b0948d..000000000
--- a/externals/winpthreads
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit f35b0948d36a736e6a2d052ae295a3ffde09703f
diff --git a/src/common/config.cpp b/src/common/config.cpp
index 111c0cfa9..d8f46a17d 100644
--- a/src/common/config.cpp
+++ b/src/common/config.cpp
@@ -33,9 +33,7 @@ namespace Config {
static bool isNeo = false;
static bool isDevKit = false;
-static bool playBGM = false;
static bool isTrophyPopupDisabled = false;
-static int BGMvolume = 50;
static bool enableDiscordRPC = false;
static u32 screenWidth = 1280;
static u32 screenHeight = 720;
@@ -43,7 +41,6 @@ static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto
static std::string logFilter;
static std::string logType = "sync";
static std::string userName = "shadPS4";
-static std::string updateChannel;
static std::string chooseHomeTab;
static std::string backButtonBehavior = "left";
static bool useSpecialPad = false;
@@ -52,8 +49,6 @@ static bool isMotionControlsEnabled = true;
static bool isDebugDump = false;
static bool isShaderDebug = false;
static bool isShowSplash = false;
-static bool isAutoUpdate = false;
-static bool isAlwaysShowChangelog = false;
static std::string isSideTrophy = "right";
static bool isNullGpu = false;
static bool shouldCopyGPUBuffers = false;
@@ -69,7 +64,7 @@ static bool vkGuestMarkers = false;
static bool rdocEnable = false;
static bool isFpsColor = true;
static bool isSeparateLogFilesEnabled = false;
-static s16 cursorState = HideCursorState::Idle;
+static int cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default)
static double trophyNotificationDuration = 6.0;
static bool useUnifiedInputConfig = true;
@@ -78,6 +73,7 @@ static int controllerCustomColorRGB[3] = {0, 0, 255};
static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false;
static std::string trophyKey;
+static bool isPSNSignedIn = false;
// Gui
static bool load_game_size = true;
@@ -85,27 +81,13 @@ static std::vector settings_install_dirs = {};
std::vector install_dirs_enabled = {};
std::filesystem::path settings_addon_install_dir = {};
std::filesystem::path save_data_path = {};
-u32 main_window_geometry_x = 400;
-u32 main_window_geometry_y = 400;
-u32 main_window_geometry_w = 1280;
-u32 main_window_geometry_h = 720;
u32 mw_themes = 0;
-u32 m_icon_size = 36;
-u32 m_icon_size_grid = 69;
-u32 m_slider_pos = 0;
-u32 m_slider_pos_grid = 0;
-u32 m_table_mode = 0;
-u32 m_window_size_W = 1280;
-u32 m_window_size_H = 720;
std::vector m_elf_viewer;
std::vector m_recent_files;
std::string emulator_language = "en_US";
-static int backgroundImageOpacity = 50;
-static bool showBackgroundImage = true;
static bool isFullscreen = false;
static std::string fullscreenMode = "Windowed";
static bool isHDRAllowed = false;
-static bool showLabelsUnderIcons = true;
// Language
u32 m_language = 1; // english
@@ -154,7 +136,7 @@ bool GetLoadGameSizeEnabled() {
std::filesystem::path GetSaveDataPath() {
if (save_data_path.empty()) {
- return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir);
+ return Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "savedata";
}
return save_data_path;
}
@@ -175,14 +157,6 @@ bool getIsFullscreen() {
return isFullscreen;
}
-bool getShowLabelsUnderIcons() {
- return showLabelsUnderIcons;
-}
-
-bool setShowLabelsUnderIcons() {
- return false;
-}
-
std::string getFullscreenMode() {
return fullscreenMode;
}
@@ -191,14 +165,6 @@ bool getisTrophyPopupDisabled() {
return isTrophyPopupDisabled;
}
-bool getPlayBGM() {
- return playBGM;
-}
-
-int getBGMvolume() {
- return BGMvolume;
-}
-
bool getEnableDiscordRPC() {
return enableDiscordRPC;
}
@@ -239,10 +205,6 @@ std::string getUserName() {
return userName;
}
-std::string getUpdateChannel() {
- return updateChannel;
-}
-
std::string getChooseHomeTab() {
return chooseHomeTab;
}
@@ -275,14 +237,6 @@ bool showSplash() {
return isShowSplash;
}
-bool autoUpdate() {
- return isAutoUpdate;
-}
-
-bool alwaysShowChangelog() {
- return isAlwaysShowChangelog;
-}
-
std::string sideTrophy() {
return isSideTrophy;
}
@@ -383,14 +337,6 @@ void setShowSplash(bool enable) {
isShowSplash = enable;
}
-void setAutoUpdate(bool enable) {
- isAutoUpdate = enable;
-}
-
-void setAlwaysShowChangelog(bool enable) {
- isAlwaysShowChangelog = enable;
-}
-
void setSideTrophy(std::string side) {
isSideTrophy = side;
}
@@ -430,9 +376,6 @@ void setVblankDiv(u32 value) {
void setIsFullscreen(bool enable) {
isFullscreen = enable;
}
-static void setShowLabelsUnderIcons(bool enable) {
- showLabelsUnderIcons = enable;
-}
void setFullscreenMode(std::string mode) {
fullscreenMode = mode;
@@ -442,14 +385,6 @@ void setisTrophyPopupDisabled(bool disable) {
isTrophyPopupDisabled = disable;
}
-void setPlayBGM(bool enable) {
- playBGM = enable;
-}
-
-void setBGMvolume(int volume) {
- BGMvolume = volume;
-}
-
void setEnableDiscordRPC(bool enable) {
enableDiscordRPC = enable;
}
@@ -489,9 +424,6 @@ void setUserName(const std::string& type) {
userName = type;
}
-void setUpdateChannel(const std::string& type) {
- updateChannel = type;
-}
void setChooseHomeTab(const std::string& type) {
chooseHomeTab = type;
}
@@ -520,13 +452,6 @@ void setCheckCompatibilityOnStartup(bool use) {
checkCompatibilityOnStartup = use;
}
-void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
- main_window_geometry_x = x;
- main_window_geometry_y = y;
- main_window_geometry_w = w;
- main_window_geometry_h = h;
-}
-
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled) {
for (const auto& install_dir : settings_install_dirs) {
if (install_dir.path == dir) {
@@ -563,34 +488,6 @@ void setMainWindowTheme(u32 theme) {
mw_themes = theme;
}
-void setIconSize(u32 size) {
- m_icon_size = size;
-}
-
-void setIconSizeGrid(u32 size) {
- m_icon_size_grid = size;
-}
-
-void setSliderPosition(u32 pos) {
- m_slider_pos = pos;
-}
-
-void setSliderPositionGrid(u32 pos) {
- m_slider_pos_grid = pos;
-}
-
-void setTableMode(u32 mode) {
- m_table_mode = mode;
-}
-
-void setMainWindowWidth(u32 width) {
- m_window_size_W = width;
-}
-
-void setMainWindowHeight(u32 height) {
- m_window_size_H = height;
-}
-
void setElfViewer(const std::vector& elfList) {
m_elf_viewer.resize(elfList.size());
m_elf_viewer = elfList;
@@ -620,22 +517,6 @@ void setSaveDataPath(const std::filesystem::path& path) {
save_data_path = path;
}
-u32 getMainWindowGeometryX() {
- return main_window_geometry_x;
-}
-
-u32 getMainWindowGeometryY() {
- return main_window_geometry_y;
-}
-
-u32 getMainWindowGeometryW() {
- return main_window_geometry_w;
-}
-
-u32 getMainWindowGeometryH() {
- return main_window_geometry_h;
-}
-
const std::vector getGameInstallDirs() {
std::vector enabled_dirs;
for (const auto& dir : settings_install_dirs) {
@@ -666,34 +547,6 @@ u32 getMainWindowTheme() {
return mw_themes;
}
-u32 getIconSize() {
- return m_icon_size;
-}
-
-u32 getIconSizeGrid() {
- return m_icon_size_grid;
-}
-
-u32 getSliderPosition() {
- return m_slider_pos;
-}
-
-u32 getSliderPositionGrid() {
- return m_slider_pos_grid;
-}
-
-u32 getTableMode() {
- return m_table_mode;
-}
-
-u32 getMainWindowWidth() {
- return m_window_size_W;
-}
-
-u32 getMainWindowHeight() {
- return m_window_size_H;
-}
-
std::vector getElfViewer() {
return m_elf_viewer;
}
@@ -714,20 +567,12 @@ bool getSeparateLogFilesEnabled() {
return isSeparateLogFilesEnabled;
}
-int getBackgroundImageOpacity() {
- return backgroundImageOpacity;
+bool getPSNSignedIn() {
+ return isPSNSignedIn;
}
-void setBackgroundImageOpacity(int opacity) {
- backgroundImageOpacity = std::clamp(opacity, 0, 100);
-}
-
-bool getShowBackgroundImage() {
- return showBackgroundImage;
-}
-
-void setShowBackgroundImage(bool show) {
- showBackgroundImage = show;
+void setPSNSignedIn(bool sign) {
+ isPSNSignedIn = sign;
}
void load(const std::filesystem::path& path) {
@@ -754,23 +599,15 @@ void load(const std::filesystem::path& path) {
isNeo = toml::find_or(general, "isPS4Pro", false);
isDevKit = toml::find_or(general, "isDevKit", false);
- playBGM = toml::find_or(general, "playBGM", false);
+ isPSNSignedIn = toml::find_or(general, "isPSNSignedIn", false);
isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false);
trophyNotificationDuration =
toml::find_or(general, "trophyNotificationDuration", 5.0);
- BGMvolume = toml::find_or(general, "BGMvolume", 50);
enableDiscordRPC = toml::find_or(general, "enableDiscordRPC", true);
logFilter = toml::find_or(general, "logFilter", "");
logType = toml::find_or(general, "logType", "sync");
userName = toml::find_or(general, "userName", "shadPS4");
- if (Common::g_is_release) {
- updateChannel = toml::find_or(general, "updateChannel", "Release");
- } else {
- updateChannel = toml::find_or(general, "updateChannel", "Nightly");
- }
isShowSplash = toml::find_or(general, "showSplash", true);
- isAutoUpdate = toml::find_or(general, "autoUpdate", false);
- isAlwaysShowChangelog = toml::find_or(general, "alwaysShowChangelog", false);
isSideTrophy = toml::find_or(general, "sideTrophy", "right");
compatibilityData = toml::find_or(general, "compatibilityEnabled", false);
checkCompatibilityOnStartup =
@@ -831,13 +668,7 @@ void load(const std::filesystem::path& path) {
const toml::value& gui = data.at("GUI");
load_game_size = toml::find_or(gui, "loadGameSizeEnabled", true);
- m_icon_size = toml::find_or(gui, "iconSize", 0);
- m_icon_size_grid = toml::find_or(gui, "iconSizeGrid", 0);
- m_slider_pos = toml::find_or(gui, "sliderPos", 0);
- m_slider_pos_grid = toml::find_or(gui, "sliderPosGrid", 0);
mw_themes = toml::find_or(gui, "theme", 0);
- m_window_size_W = toml::find_or(gui, "mw_width", 0);
- m_window_size_H = toml::find_or(gui, "mw_height", 0);
const auto install_dir_array =
toml::find_or>(gui, "installDirs", {});
@@ -862,16 +693,9 @@ void load(const std::filesystem::path& path) {
save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {});
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {});
- main_window_geometry_x = toml::find_or(gui, "geometry_x", 0);
- main_window_geometry_y = toml::find_or(gui, "geometry_y", 0);
- main_window_geometry_w = toml::find_or(gui, "geometry_w", 0);
- main_window_geometry_h = toml::find_or(gui, "geometry_h", 0);
m_elf_viewer = toml::find_or>(gui, "elfDirs", {});
m_recent_files = toml::find_or>(gui, "recentFiles", {});
- m_table_mode = toml::find_or(gui, "gameTableMode", 0);
emulator_language = toml::find_or(gui, "emulatorLanguage", "en_US");
- backgroundImageOpacity = toml::find_or(gui, "backgroundImageOpacity", 50);
- showBackgroundImage = toml::find_or(gui, "showBackgroundImage", true);
}
if (data.contains("Settings")) {
@@ -887,9 +711,10 @@ void load(const std::filesystem::path& path) {
// Check if the loaded language is in the allowed list
const std::vector allowed_languages = {
- "ar_SA", "da_DK", "de_DE", "el_GR", "en_US", "es_ES", "fa_IR", "fi_FI", "fr_FR", "hu_HU",
- "id_ID", "it_IT", "ja_JP", "ko_KR", "lt_LT", "nb_NO", "nl_NL", "pl_PL", "pt_BR", "pt_PT",
- "ro_RO", "ru_RU", "sq_AL", "sv_SE", "tr_TR", "uk_UA", "vi_VN", "zh_CN", "zh_TW"};
+ "ar_SA", "da_DK", "de_DE", "el_GR", "en_US", "es_ES", "fa_IR", "fi_FI",
+ "fr_FR", "hu_HU", "id_ID", "it_IT", "ja_JP", "ko_KR", "lt_LT", "nb_NO",
+ "nl_NL", "pl_PL", "pt_BR", "pt_PT", "ro_RO", "ru_RU", "sq_AL", "sv_SE",
+ "tr_TR", "uk_UA", "vi_VN", "zh_CN", "zh_TW", "ca_ES", "sr_CS"};
if (std::find(allowed_languages.begin(), allowed_languages.end(), emulator_language) ==
allowed_languages.end()) {
@@ -953,19 +778,15 @@ void save(const std::filesystem::path& path) {
data["General"]["isPS4Pro"] = isNeo;
data["General"]["isDevKit"] = isDevKit;
+ data["General"]["isPSNSignedIn"] = isPSNSignedIn;
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
data["General"]["trophyNotificationDuration"] = trophyNotificationDuration;
- data["General"]["playBGM"] = playBGM;
- data["General"]["BGMvolume"] = BGMvolume;
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
data["General"]["logFilter"] = logFilter;
data["General"]["logType"] = logType;
data["General"]["userName"] = userName;
- data["General"]["updateChannel"] = updateChannel;
data["General"]["chooseHomeTab"] = chooseHomeTab;
data["General"]["showSplash"] = isShowSplash;
- data["General"]["autoUpdate"] = isAutoUpdate;
- data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog;
data["General"]["sideTrophy"] = isSideTrophy;
data["General"]["compatibilityEnabled"] = compatibilityData;
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
@@ -1035,8 +856,6 @@ void save(const std::filesystem::path& path) {
data["GUI"]["addonInstallDir"] =
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
data["GUI"]["emulatorLanguage"] = emulator_language;
- data["GUI"]["backgroundImageOpacity"] = backgroundImageOpacity;
- data["GUI"]["showBackgroundImage"] = showBackgroundImage;
data["Settings"]["consoleLanguage"] = m_language;
// Sorting of TOML sections
@@ -1071,18 +890,7 @@ void saveMainWindow(const std::filesystem::path& path) {
fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string()));
}
- data["GUI"]["mw_width"] = m_window_size_W;
- data["GUI"]["mw_height"] = m_window_size_H;
data["GUI"]["theme"] = mw_themes;
- data["GUI"]["iconSize"] = m_icon_size;
- data["GUI"]["sliderPos"] = m_slider_pos;
- data["GUI"]["iconSizeGrid"] = m_icon_size_grid;
- data["GUI"]["sliderPosGrid"] = m_slider_pos_grid;
- data["GUI"]["gameTableMode"] = m_table_mode;
- data["GUI"]["geometry_x"] = main_window_geometry_x;
- data["GUI"]["geometry_y"] = main_window_geometry_y;
- data["GUI"]["geometry_w"] = main_window_geometry_w;
- data["GUI"]["geometry_h"] = main_window_geometry_h;
data["GUI"]["elfDirs"] = m_elf_viewer;
data["GUI"]["recentFiles"] = m_recent_files;
@@ -1098,21 +906,16 @@ void setDefaultValues() {
isHDRAllowed = false;
isNeo = false;
isDevKit = false;
+ isPSNSignedIn = false;
isFullscreen = false;
isTrophyPopupDisabled = false;
- playBGM = false;
- BGMvolume = 50;
enableDiscordRPC = true;
screenWidth = 1280;
screenHeight = 720;
logFilter = "";
logType = "sync";
userName = "shadPS4";
- if (Common::g_is_release) {
- updateChannel = "Release";
- } else {
- updateChannel = "Nightly";
- }
+
chooseHomeTab = "General";
cursorState = HideCursorState::Idle;
cursorHideTimeout = 5;
@@ -1123,8 +926,6 @@ void setDefaultValues() {
isDebugDump = false;
isShaderDebug = false;
isShowSplash = false;
- isAutoUpdate = false;
- isAlwaysShowChangelog = false;
isSideTrophy = "right";
isNullGpu = false;
shouldDumpShaders = false;
@@ -1141,8 +942,6 @@ void setDefaultValues() {
gpuId = -1;
compatibilityData = false;
checkCompatibilityOnStartup = false;
- backgroundImageOpacity = 50;
- showBackgroundImage = true;
}
constexpr std::string_view GetDefaultKeyboardConfig() {
diff --git a/src/common/config.h b/src/common/config.h
index aba23621c..414bc122e 100644
--- a/src/common/config.h
+++ b/src/common/config.h
@@ -14,7 +14,7 @@ struct GameInstallDir {
bool enabled;
};
-enum HideCursorState : s16 { Never, Idle, Always };
+enum HideCursorState : int { Never, Idle, Always };
void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path);
@@ -26,24 +26,18 @@ bool GetLoadGameSizeEnabled();
std::filesystem::path GetSaveDataPath();
void setLoadGameSizeEnabled(bool enable);
bool getIsFullscreen();
-bool getShowLabelsUnderIcons();
-bool setShowLabelsUnderIcons();
std::string getFullscreenMode();
bool isNeoModeConsole();
bool isDevKitConsole();
-bool getPlayBGM();
-int getBGMvolume();
bool getisTrophyPopupDisabled();
bool getEnableDiscordRPC();
bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup();
-int getBackgroundImageOpacity();
-bool getShowBackgroundImage();
+bool getPSNSignedIn();
std::string getLogFilter();
std::string getLogType();
std::string getUserName();
-std::string getUpdateChannel();
std::string getChooseHomeTab();
s16 getCursorState();
@@ -68,8 +62,6 @@ bool allowHDR();
bool debugDump();
bool collectShadersForDebug();
bool showSplash();
-bool autoUpdate();
-bool alwaysShowChangelog();
std::string sideTrophy();
bool nullGpu();
bool copyGPUCmdBuffers();
@@ -82,8 +74,6 @@ u32 vblankDiv();
void setDebugDump(bool enable);
void setCollectShaderForDebug(bool enable);
void setShowSplash(bool enable);
-void setAutoUpdate(bool enable);
-void setAlwaysShowChangelog(bool enable);
void setSideTrophy(std::string side);
void setNullGpu(bool enable);
void setAllowHDR(bool enable);
@@ -96,21 +86,17 @@ void setScreenHeight(u32 height);
void setIsFullscreen(bool enable);
void setFullscreenMode(std::string mode);
void setisTrophyPopupDisabled(bool disable);
-void setPlayBGM(bool enable);
-void setBGMvolume(int volume);
void setEnableDiscordRPC(bool enable);
void setLanguage(u32 language);
void setNeoMode(bool enable);
void setUserName(const std::string& type);
-void setUpdateChannel(const std::string& type);
void setChooseHomeTab(const std::string& type);
void setGameInstallDirs(const std::vector& dirs_config);
void setAllGameInstallDirs(const std::vector& dirs_config);
void setSaveDataPath(const std::filesystem::path& path);
void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use);
-void setBackgroundImageOpacity(int opacity);
-void setShowBackgroundImage(bool show);
+void setPSNSignedIn(bool sign);
void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout);
@@ -139,38 +125,19 @@ void setVkHostMarkersEnabled(bool enable);
void setVkGuestMarkersEnabled(bool enable);
// Gui
-void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled = true);
void removeGameInstallDir(const std::filesystem::path& dir);
void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled);
void setAddonInstallDir(const std::filesystem::path& dir);
void setMainWindowTheme(u32 theme);
-void setIconSize(u32 size);
-void setIconSizeGrid(u32 size);
-void setSliderPosition(u32 pos);
-void setSliderPositionGrid(u32 pos);
-void setTableMode(u32 mode);
-void setMainWindowWidth(u32 width);
-void setMainWindowHeight(u32 height);
void setElfViewer(const std::vector& elfList);
void setRecentFiles(const std::vector& recentFiles);
void setEmulatorLanguage(std::string language);
-u32 getMainWindowGeometryX();
-u32 getMainWindowGeometryY();
-u32 getMainWindowGeometryW();
-u32 getMainWindowGeometryH();
const std::vector getGameInstallDirs();
const std::vector getGameInstallDirsEnabled();
std::filesystem::path getAddonInstallDir();
u32 getMainWindowTheme();
-u32 getIconSize();
-u32 getIconSizeGrid();
-u32 getSliderPosition();
-u32 getSliderPositionGrid();
-u32 getTableMode();
-u32 getMainWindowWidth();
-u32 getMainWindowHeight();
std::vector getElfViewer();
std::vector getRecentFiles();
std::string getEmulatorLanguage();
diff --git a/src/common/elf_info.h b/src/common/elf_info.h
index 062cee012..02b845cb5 100644
--- a/src/common/elf_info.h
+++ b/src/common/elf_info.h
@@ -71,6 +71,7 @@ class ElfInfo {
PSFAttributes psf_attributes{};
std::filesystem::path splash_path{};
+ std::filesystem::path game_folder{};
public:
static constexpr u32 FW_15 = 0x1500000;
@@ -123,6 +124,10 @@ public:
[[nodiscard]] const std::filesystem::path& GetSplashPath() const {
return splash_path;
}
+
+ [[nodiscard]] const std::filesystem::path& GetGameFolder() const {
+ return game_folder;
+ }
};
} // namespace Common
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 622af93cc..05935fbdc 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -138,6 +138,10 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Zlib) \
SUB(Lib, Hmd) \
SUB(Lib, SigninDialog) \
+ SUB(Lib, Camera) \
+ SUB(Lib, CompanionHttpd) \
+ SUB(Lib, CompanionUtil) \
+ SUB(Lib, Voice) \
CLS(Frontend) \
CLS(Render) \
SUB(Render, Vulkan) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 27a87e082..1da84b219 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -98,6 +98,7 @@ enum class Class : u8 {
Lib_Fiber, ///< The LibSceFiber implementation.
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
Lib_Videodec, ///< The LibSceVideodec implementation.
+ Lib_Voice, ///< The LibSceVoice implementation.
Lib_RazorCpu, ///< The LibRazorCpu implementation.
Lib_Mouse, ///< The LibSceMouse implementation
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
@@ -105,6 +106,9 @@ enum class Class : u8 {
Lib_Zlib, ///< The LibSceZlib implementation.
Lib_Hmd, ///< The LibSceHmd implementation.
Lib_SigninDialog, ///< The LibSigninDialog implementation.
+ Lib_Camera, ///< The LibCamera implementation.
+ Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
+ Lib_CompanionUtil, ///< The LibCompanionUtil implementation.
Frontend, ///< Emulator UI
Render, ///< Video Core
Render_Vulkan, ///< Vulkan backend
diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp
index 1a6ff9ec8..3270c24dd 100644
--- a/src/common/path_util.cpp
+++ b/src/common/path_util.cpp
@@ -128,7 +128,6 @@ static auto UserPaths = [] {
create_path(PathType::LogDir, user_dir / LOG_DIR);
create_path(PathType::ScreenshotsDir, user_dir / SCREENSHOTS_DIR);
create_path(PathType::ShaderDir, user_dir / SHADER_DIR);
- create_path(PathType::SaveDataDir, user_dir / SAVEDATA_DIR);
create_path(PathType::GameDataDir, user_dir / GAMEDATA_DIR);
create_path(PathType::TempDataDir, user_dir / TEMPDATA_DIR);
create_path(PathType::SysModuleDir, user_dir / SYSMODULES_DIR);
diff --git a/src/common/path_util.h b/src/common/path_util.h
index 2fd9b1588..b8053a229 100644
--- a/src/common/path_util.h
+++ b/src/common/path_util.h
@@ -18,7 +18,6 @@ enum class PathType {
LogDir, // Where log files are stored.
ScreenshotsDir, // Where screenshots are stored.
ShaderDir, // Where shaders are stored.
- SaveDataDir, // Where guest save data is stored.
TempDataDir, // Where game temp data is stored.
GameDataDir, // Where game data is stored.
SysModuleDir, // Where system modules are stored.
@@ -36,7 +35,6 @@ constexpr auto PORTABLE_DIR = "user";
constexpr auto LOG_DIR = "log";
constexpr auto SCREENSHOTS_DIR = "screenshots";
constexpr auto SHADER_DIR = "shader";
-constexpr auto SAVEDATA_DIR = "savedata";
constexpr auto GAMEDATA_DIR = "data";
constexpr auto TEMPDATA_DIR = "temp";
constexpr auto SYSMODULES_DIR = "sys_modules";
diff --git a/src/common/recursive_lock.cpp b/src/common/recursive_lock.cpp
new file mode 100644
index 000000000..2471a2ee0
--- /dev/null
+++ b/src/common/recursive_lock.cpp
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include
+#include "common/assert.h"
+#include "common/recursive_lock.h"
+
+namespace Common::Detail {
+
+struct RecursiveLockState {
+ RecursiveLockType type;
+ int count;
+};
+
+thread_local std::unordered_map g_recursive_locks;
+
+bool IncrementRecursiveLock(void* mutex, RecursiveLockType type) {
+ auto& state = g_recursive_locks[mutex];
+ if (state.count == 0) {
+ ASSERT(state.type == RecursiveLockType::None);
+ state.type = type;
+ }
+ ASSERT(state.type == type);
+ return state.count++ == 0;
+}
+
+bool DecrementRecursiveLock(void* mutex, RecursiveLockType type) {
+ auto& state = g_recursive_locks[mutex];
+ ASSERT(state.type == type && state.count > 0);
+ if (--state.count == 0) {
+ g_recursive_locks.erase(mutex);
+ return true;
+ }
+ return false;
+}
+
+} // namespace Common::Detail
diff --git a/src/common/recursive_lock.h b/src/common/recursive_lock.h
new file mode 100644
index 000000000..5a5fc6658
--- /dev/null
+++ b/src/common/recursive_lock.h
@@ -0,0 +1,67 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+#include
+#include
+
+namespace Common {
+
+namespace Detail {
+
+enum class RecursiveLockType { None, Shared, Exclusive };
+
+bool IncrementRecursiveLock(void* mutex, RecursiveLockType type);
+bool DecrementRecursiveLock(void* mutex, RecursiveLockType type);
+
+} // namespace Detail
+
+template
+class RecursiveScopedLock {
+public:
+ explicit RecursiveScopedLock(MutexType& mutex) : m_mutex(mutex), m_locked(false) {
+ if (Detail::IncrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Exclusive)) {
+ m_locked = true;
+ m_lock.emplace(m_mutex);
+ }
+ }
+
+ ~RecursiveScopedLock() {
+ Detail::DecrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Exclusive);
+ if (m_locked) {
+ m_lock.reset();
+ }
+ }
+
+private:
+ MutexType& m_mutex;
+ std::optional> m_lock;
+ bool m_locked = false;
+};
+
+template
+class RecursiveSharedLock {
+public:
+ explicit RecursiveSharedLock(MutexType& mutex) : m_mutex(mutex), m_locked(false) {
+ if (Detail::IncrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Shared)) {
+ m_locked = true;
+ m_lock.emplace(m_mutex);
+ }
+ }
+
+ ~RecursiveSharedLock() {
+ Detail::DecrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Shared);
+ if (m_locked) {
+ m_lock.reset();
+ }
+ }
+
+private:
+ MutexType& m_mutex;
+ std::optional> m_lock;
+ bool m_locked = false;
+};
+
+} // namespace Common
\ No newline at end of file
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in
index 71c4c2d0a..0b113eb31 100644
--- a/src/common/scm_rev.cpp.in
+++ b/src/common/scm_rev.cpp.in
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
+
#include "common/scm_rev.h"
namespace Common {
@@ -15,5 +17,26 @@ constexpr char g_scm_remote_name[] = "@GIT_REMOTE_NAME@";
constexpr char g_scm_remote_url[] = "@GIT_REMOTE_URL@";
constexpr char g_scm_date[] = "@BUILD_DATE@";
+const std::string GetRemoteNameFromLink() {
+ std::string remote_url(Common::g_scm_remote_url);
+ std::string remote_host;
+ try {
+ if (remote_url.starts_with("http")) {
+ if (*remote_url.rbegin() == '/') {
+ remote_url.pop_back();
+ }
+ remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
+ } else if (remote_url.starts_with("git@")) {
+ auto after_comma_pos = remote_url.find(':') + 1, slash_pos = remote_url.find('/');
+ remote_host = remote_url.substr(after_comma_pos, slash_pos - after_comma_pos);
+ } else {
+ remote_host = "unknown";
+ }
+ } catch (...) {
+ remote_host = "unknown";
+ }
+ return remote_host;
+}
+
} // namespace
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h
index 36b844e94..2f6d770bb 100644
--- a/src/common/scm_rev.h
+++ b/src/common/scm_rev.h
@@ -3,6 +3,8 @@
#pragma once
+#include
+
namespace Common {
extern const char g_version[];
@@ -15,4 +17,6 @@ extern const char g_scm_remote_name[];
extern const char g_scm_remote_url[];
extern const char g_scm_date[];
+const std::string GetRemoteNameFromLink();
+
} // namespace Common
diff --git a/src/common/slot_vector.h b/src/common/slot_vector.h
index d4ac51361..2f693fb28 100644
--- a/src/common/slot_vector.h
+++ b/src/common/slot_vector.h
@@ -14,6 +14,9 @@ namespace Common {
struct SlotId {
static constexpr u32 INVALID_INDEX = std::numeric_limits::max();
+ SlotId() noexcept = default;
+ constexpr SlotId(u32 index) noexcept : index(index) {}
+
constexpr auto operator<=>(const SlotId&) const noexcept = default;
constexpr explicit operator bool() const noexcept {
@@ -28,6 +31,63 @@ class SlotVector {
constexpr static std::size_t InitialCapacity = 2048;
public:
+ template
+ class Iterator {
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = ValueType;
+ using difference_type = std::ptrdiff_t;
+ using pointer = Pointer;
+ using reference = Reference;
+
+ Iterator(SlotVector& vector_, SlotId index_) : vector(vector_), slot(index_) {
+ AdvanceToValid();
+ }
+
+ reference operator*() const {
+ return vector[slot];
+ }
+
+ pointer operator->() const {
+ return &vector[slot];
+ }
+
+ Iterator& operator++() {
+ ++slot.index;
+ AdvanceToValid();
+ return *this;
+ }
+
+ Iterator operator++(int) {
+ Iterator temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ bool operator==(const Iterator& other) const {
+ return slot == other.slot;
+ }
+
+ bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+
+ private:
+ void AdvanceToValid() {
+ while (slot < vector.values_capacity && !vector.ReadStorageBit(slot.index)) {
+ ++slot.index;
+ }
+ }
+
+ SlotVector& vector;
+ SlotId slot;
+ };
+
+ using iterator = Iterator;
+ using const_iterator = Iterator;
+ using reverse_iterator = std::reverse_iterator;
+ using const_reverse_iterator = std::reverse_iterator;
+
SlotVector() {
Reserve(InitialCapacity);
}
@@ -60,7 +120,7 @@ public:
}
template
- [[nodiscard]] SlotId insert(Args&&... args) noexcept {
+ SlotId insert(Args&&... args) noexcept {
const u32 index = FreeValueIndex();
new (&values[index].object) T(std::forward(args)...);
SetStorageBit(index);
@@ -78,6 +138,54 @@ public:
return values_capacity - free_list.size();
}
+ iterator begin() noexcept {
+ return iterator(*this, 0);
+ }
+
+ const_iterator begin() const noexcept {
+ return const_iterator(*this, 0);
+ }
+
+ const_iterator cbegin() const noexcept {
+ return begin();
+ }
+
+ iterator end() noexcept {
+ return iterator(*this, values_capacity);
+ }
+
+ const_iterator end() const noexcept {
+ return const_iterator(*this, values_capacity);
+ }
+
+ const_iterator cend() const noexcept {
+ return end();
+ }
+
+ reverse_iterator rbegin() noexcept {
+ return reverse_iterator(end());
+ }
+
+ const_reverse_iterator rbegin() const noexcept {
+ return const_reverse_iterator(end());
+ }
+
+ const_reverse_iterator crbegin() const noexcept {
+ return rbegin();
+ }
+
+ reverse_iterator rend() noexcept {
+ return reverse_iterator(begin());
+ }
+
+ const_reverse_iterator rend() const noexcept {
+ return const_reverse_iterator(begin());
+ }
+
+ const_reverse_iterator crend() const noexcept {
+ return rend();
+ }
+
private:
struct NonTrivialDummy {
NonTrivialDummy() noexcept {}
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 9ef1e86d8..982041ebb 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -2,6 +2,7 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
#include
#include
@@ -104,14 +105,24 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
SetThreadPriority(handle, windows_priority);
}
-static void AccurateSleep(std::chrono::nanoseconds duration) {
+bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
+ const bool interruptible) {
+ const auto begin_sleep = std::chrono::high_resolution_clock::now();
+
LARGE_INTEGER interval{
.QuadPart = -1 * (duration.count() / 100u),
};
HANDLE timer = ::CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &interval, 0, NULL, NULL, 0);
- WaitForSingleObject(timer, INFINITE);
+ const auto ret = WaitForSingleObjectEx(timer, INFINITE, interruptible);
::CloseHandle(timer);
+
+ if (remaining) {
+ const auto end_sleep = std::chrono::high_resolution_clock::now();
+ const auto sleep_time = end_sleep - begin_sleep;
+ *remaining = duration > sleep_time ? duration - sleep_time : std::chrono::nanoseconds(0);
+ }
+ return ret == WAIT_OBJECT_0;
}
#else
@@ -134,8 +145,24 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
pthread_setschedparam(this_thread, scheduling_type, ¶ms);
}
-static void AccurateSleep(std::chrono::nanoseconds duration) {
- std::this_thread::sleep_for(duration);
+bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
+ const bool interruptible) {
+ timespec request = {
+ .tv_sec = duration.count() / 1'000'000'000,
+ .tv_nsec = duration.count() % 1'000'000'000,
+ };
+ timespec remain;
+ int ret;
+ while ((ret = nanosleep(&request, &remain)) < 0 && errno == EINTR) {
+ if (interruptible) {
+ break;
+ }
+ request = remain;
+ }
+ if (remaining) {
+ *remaining = std::chrono::nanoseconds(remain.tv_sec * 1'000'000'000 + remain.tv_nsec);
+ }
+ return ret == 0 || errno != EINTR;
}
#endif
@@ -196,9 +223,9 @@ AccurateTimer::AccurateTimer(std::chrono::nanoseconds target_interval)
: target_interval(target_interval) {}
void AccurateTimer::Start() {
- auto begin_sleep = std::chrono::high_resolution_clock::now();
+ const auto begin_sleep = std::chrono::high_resolution_clock::now();
if (total_wait.count() > 0) {
- AccurateSleep(total_wait);
+ AccurateSleep(total_wait, nullptr, false);
}
start_time = std::chrono::high_resolution_clock::now();
total_wait -= std::chrono::duration_cast(start_time - begin_sleep);
diff --git a/src/common/thread.h b/src/common/thread.h
index 92cc0c59d..5bd83d35c 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -25,6 +25,9 @@ void SetCurrentThreadName(const char* name);
void SetThreadName(void* thread, const char* name);
+bool AccurateSleep(std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
+ bool interruptible);
+
class AccurateTimer {
std::chrono::nanoseconds target_interval{};
std::chrono::nanoseconds total_wait{};
diff --git a/src/common/va_ctx.h b/src/common/va_ctx.h
index e0b8c0bab..cffe468ff 100644
--- a/src/common/va_ctx.h
+++ b/src/common/va_ctx.h
@@ -8,7 +8,7 @@
#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, ...
+ __m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7
#define VA_CTX(ctx) \
alignas(16)::Common::VaCtx ctx{}; \
diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp
index 8937ef04b..8512858e9 100644
--- a/src/core/cpu_patches.cpp
+++ b/src/core/cpu_patches.cpp
@@ -88,7 +88,8 @@ static bool FilterTcbAccess(const ZydisDecodedOperand* operands) {
dst_op.reg.value <= ZYDIS_REGISTER_R15;
}
-static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
+static void GenerateTcbAccess(void* /* address */, const ZydisDecodedOperand* operands,
+ Xbyak::CodeGenerator& c) {
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
#if defined(_WIN32)
@@ -126,7 +127,8 @@ static bool FilterNoSSE4a(const ZydisDecodedOperand*) {
return !cpu.has(Cpu::tSSE4a);
}
-static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
+static void GenerateEXTRQ(void* /* address */, const ZydisDecodedOperand* operands,
+ Xbyak::CodeGenerator& c) {
bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
@@ -245,7 +247,8 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
}
}
-static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
+static void GenerateINSERTQ(void* /* address */, const ZydisDecodedOperand* operands,
+ Xbyak::CodeGenerator& c) {
bool immediateForm = operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
operands[3].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
@@ -383,8 +386,44 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
}
}
+static void ReplaceMOVNT(void* address, u8 rep_prefix) {
+ // Find the opcode byte
+ // There can be any amount of prefixes but the instruction can't be more than 15 bytes
+ // And we know for sure this is a MOVNTSS/MOVNTSD
+ bool found = false;
+ bool rep_prefix_found = false;
+ int index = 0;
+ u8* ptr = reinterpret_cast(address);
+ for (int i = 0; i < 15; i++) {
+ if (ptr[i] == rep_prefix) {
+ rep_prefix_found = true;
+ } else if (ptr[i] == 0x2B) {
+ index = i;
+ found = true;
+ break;
+ }
+ }
+
+ // Some sanity checks
+ ASSERT(found);
+ ASSERT(index >= 2);
+ ASSERT(ptr[index - 1] == 0x0F);
+ ASSERT(rep_prefix_found);
+
+ // This turns the MOVNTSS/MOVNTSD to a MOVSS/MOVSD m, xmm
+ ptr[index] = 0x11;
+}
+
+static void ReplaceMOVNTSS(void* address, const ZydisDecodedOperand*, Xbyak::CodeGenerator&) {
+ ReplaceMOVNT(address, 0xF3);
+}
+
+static void ReplaceMOVNTSD(void* address, const ZydisDecodedOperand*, Xbyak::CodeGenerator&) {
+ ReplaceMOVNT(address, 0xF2);
+}
+
using PatchFilter = bool (*)(const ZydisDecodedOperand*);
-using InstructionGenerator = void (*)(const ZydisDecodedOperand*, Xbyak::CodeGenerator&);
+using InstructionGenerator = void (*)(void*, const ZydisDecodedOperand*, Xbyak::CodeGenerator&);
struct PatchInfo {
/// Filter for more granular patch conditions past just the instruction mnemonic.
PatchFilter filter;
@@ -400,6 +439,8 @@ static const std::unordered_map Patches = {
// SSE4a
{ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
{ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
+ {ZYDIS_MNEMONIC_MOVNTSS, {FilterNoSSE4a, ReplaceMOVNTSS, false}},
+ {ZYDIS_MNEMONIC_MOVNTSD, {FilterNoSSE4a, ReplaceMOVNTSD, false}},
#if defined(_WIN32)
// Windows needs a trampoline.
@@ -477,7 +518,7 @@ static std::pair TryPatch(u8* code, PatchModule* module) {
auto& trampoline_gen = module->trampoline_gen;
const auto trampoline_ptr = trampoline_gen.getCurr();
- patch_info.generator(operands, trampoline_gen);
+ patch_info.generator(code, operands, trampoline_gen);
// Return to the following instruction at the end of the trampoline.
trampoline_gen.jmp(code + instruction.length);
@@ -485,7 +526,7 @@ static std::pair TryPatch(u8* code, PatchModule* module) {
// Replace instruction with near jump to the trampoline.
patch_gen.jmp(trampoline_ptr, Xbyak::CodeGenerator::LabelType::T_NEAR);
} else {
- patch_info.generator(operands, patch_gen);
+ patch_info.generator(code, operands, patch_gen);
}
const auto patch_size = patch_gen.getCurr() - code;
diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp
index a93178de5..5380d3be9 100644
--- a/src/core/devtools/layer.cpp
+++ b/src/core/devtools/layer.cpp
@@ -17,6 +17,7 @@
#include "widget/frame_dump.h"
#include "widget/frame_graph.h"
#include "widget/memory_map.h"
+#include "widget/module_list.h"
#include "widget/shader_list.h"
extern std::unique_ptr presenter;
@@ -40,6 +41,7 @@ static bool just_opened_options = false;
static Widget::MemoryMapViewer memory_map;
static Widget::ShaderList shader_list;
+static Widget::ModuleList module_list;
// clang-format off
static std::string help_text =
@@ -108,6 +110,9 @@ void L::DrawMenuBar() {
if (MenuItem("Memory map")) {
memory_map.open = true;
}
+ if (MenuItem("Module list")) {
+ module_list.open = true;
+ }
ImGui::EndMenu();
}
@@ -256,6 +261,9 @@ void L::DrawAdvanced() {
if (shader_list.open) {
shader_list.Draw();
}
+ if (module_list.open) {
+ module_list.Draw();
+ }
}
void L::DrawSimple() {
diff --git a/src/core/devtools/widget/module_list.cpp b/src/core/devtools/widget/module_list.cpp
new file mode 100644
index 000000000..73afe3462
--- /dev/null
+++ b/src/core/devtools/widget/module_list.cpp
@@ -0,0 +1,55 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "module_list.h"
+
+#include
+
+#include "common.h"
+#include "core/debug_state.h"
+#include "imgui/imgui_std.h"
+
+using namespace ImGui;
+
+namespace Core::Devtools::Widget {
+void ModuleList::Draw() {
+ SetNextWindowSize({550.0f, 600.0f}, ImGuiCond_FirstUseEver);
+ if (!Begin("Module List", &open)) {
+ End();
+ return;
+ }
+
+ if (BeginTable("ModuleTable", 3,
+ ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
+ ImGuiTableFlags_RowBg)) {
+ TableSetupColumn("Modulname", ImGuiTableColumnFlags_WidthStretch);
+ TableHeadersRow();
+
+ std::scoped_lock lock(modules_mutex);
+ for (const auto& module : modules) {
+ TableNextRow();
+
+ TableSetColumnIndex(0);
+ TextUnformatted(module.name.c_str());
+
+ TableSetColumnIndex(1);
+ if (module.is_sys_module) {
+ TextColored({0.2f, 0.6f, 0.8f, 1.0f}, "System Module");
+ } else {
+ TextColored({0.8f, 0.4f, 0.2f, 1.0f}, "Game Module");
+ }
+
+ TableSetColumnIndex(2);
+ if (module.is_lle) {
+ TextColored({0.4f, 0.7f, 0.4f, 1.0f}, "LLE");
+ } else {
+ TextColored({0.7f, 0.4f, 0.5f, 1.0f}, "HLE");
+ }
+ }
+ EndTable();
+ }
+
+ End();
+}
+
+} // namespace Core::Devtools::Widget
\ No newline at end of file
diff --git a/src/core/devtools/widget/module_list.h b/src/core/devtools/widget/module_list.h
new file mode 100644
index 000000000..4c961919e
--- /dev/null
+++ b/src/core/devtools/widget/module_list.h
@@ -0,0 +1,82 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include "common/elf_info.h"
+#include "common/path_util.h"
+
+namespace Core::Devtools::Widget {
+
+class ModuleList {
+public:
+ ModuleList() = default;
+ ~ModuleList() = default;
+
+ void Draw();
+ bool open = false;
+
+ static bool IsSystemModule(const std::filesystem::path& path) {
+ const auto sys_modules_path = Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir);
+
+ const auto abs_path = std::filesystem::absolute(path).lexically_normal();
+ const auto abs_sys_path = std::filesystem::absolute(sys_modules_path).lexically_normal();
+
+ const auto path_str = abs_path.string();
+ const auto sys_path_str = abs_sys_path.string();
+
+ return path_str.starts_with(sys_path_str);
+ }
+
+ static bool IsSystemModule(const std::string& name) {
+ const auto game_modules_path = Common::ElfInfo::Instance().GetGameFolder() / "sce_module";
+ const auto prx_path = game_modules_path / name;
+
+ if (!std::filesystem::exists(prx_path)) {
+ return true;
+ }
+ return false;
+ }
+
+ static void AddModule(const std::string& name, std::filesystem::path path) {
+ if (name == "eboot.bin") {
+ return;
+ }
+ std::scoped_lock lock(modules_mutex);
+ modules.push_back({name, IsSystemModule(path), true});
+ }
+
+ static void AddModule(std::string name) {
+ name = name + ".prx";
+ std::scoped_lock lock(modules_mutex);
+
+ bool is_sys_module = IsSystemModule(name);
+ bool is_lle = false;
+ auto it = std::find_if(modules.begin(), modules.end(),
+ [&name, is_sys_module, is_lle](const ModuleInfo& entry) {
+ return entry.name == name && !entry.is_lle;
+ });
+
+ if (it == modules.end()) {
+ modules.push_back({name, is_sys_module, is_lle});
+ }
+ }
+
+private:
+ struct ModuleInfo {
+ std::string name;
+ bool is_sys_module;
+ bool is_lle;
+ };
+
+ static inline std::mutex modules_mutex;
+
+ static inline std::vector modules;
+};
+
+} // namespace Core::Devtools::Widget
\ No newline at end of file
diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp
index 4dad44874..b237ab7d9 100644
--- a/src/core/file_sys/fs.cpp
+++ b/src/core/file_sys/fs.cpp
@@ -10,6 +10,8 @@
namespace Core::FileSys {
+bool MntPoints::ignore_game_patches = false;
+
std::string RemoveTrailingSlashes(const std::string& path) {
// Remove trailing slashes to make comparisons simpler.
std::string path_sanitized = path;
@@ -77,7 +79,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
patch_path /= rel_path;
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
- !force_base_path && std::filesystem::exists(patch_path)) {
+ !force_base_path && !ignore_game_patches && std::filesystem::exists(patch_path)) {
return patch_path;
}
@@ -137,7 +139,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
return std::optional(current_path);
};
- if (!force_base_path) {
+ if (!force_base_path && !ignore_game_patches) {
if (const auto path = search(patch_path)) {
return *path;
}
diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h
index 6638b48e8..4a2aa56c1 100644
--- a/src/core/file_sys/fs.h
+++ b/src/core/file_sys/fs.h
@@ -21,6 +21,7 @@ class MntPoints {
static constexpr bool NeedsCaseInsensitiveSearch = true;
#endif
public:
+ static bool ignore_game_patches;
struct MntPair {
std::filesystem::path host_path;
std::string mount; // e.g /app0
diff --git a/src/core/libraries/camera/camera.cpp b/src/core/libraries/camera/camera.cpp
new file mode 100644
index 000000000..996d1c895
--- /dev/null
+++ b/src/core/libraries/camera/camera.cpp
@@ -0,0 +1,517 @@
+// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "core/libraries/camera/camera.h"
+#include "core/libraries/camera/camera_error.h"
+#include "core/libraries/error_codes.h"
+#include "core/libraries/libs.h"
+
+namespace Libraries::Camera {
+
+s32 PS4_SYSV_ABI sceCameraAccGetData() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraAudioClose() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraAudioGetData() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraAudioGetData2() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraAudioOpen() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraAudioReset() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraChangeAppModuleState() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraClose(s32 handle) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraCloseByHandle() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraDeviceOpen() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetAttribute(s32 handle, OrbisCameraAttribute* pAttribute) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetAutoExposureGain(s32 handle, OrbisCameraChannel channel, u32* pEnable,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetAutoWhiteBalance(s32 handle, OrbisCameraChannel channel, u32* pEnable,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetCalibData() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetCalibDataFromDevice() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetCalibrationData() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetConfig(s32 handle, OrbisCameraConfig* pConfig) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetContrast(s32 handle, OrbisCameraChannel channel, u32* pContrast,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetDefectivePixelCancellation(s32 handle, OrbisCameraChannel channel,
+ u32* pEnable, void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetDeviceConfig() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetDeviceConfigWithoutHandle() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetDeviceID() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetDeviceIDWithoutOpen() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetDeviceInfo(s32 reserved, OrbisCameraDeviceInfo* pDeviceInfo) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetExposureGain(s32 handle, OrbisCameraChannel channel,
+ OrbisCameraExposureGain* pExposureGain, void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetFrameData(int handle, OrbisCameraFrameData* pFrameData) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetGamma(s32 handle, OrbisCameraChannel channel, OrbisCameraGamma* pGamma,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetHue(s32 handle, OrbisCameraChannel channel, s32* pHue, void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetLensCorrection(s32 handle, OrbisCameraChannel channel, u32* pEnable,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetMmapConnectedCount() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetProductInfo() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetRegister() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetRegistryInfo() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetSaturation(s32 handle, OrbisCameraChannel channel, u32* pSaturation,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetSharpness(s32 handle, OrbisCameraChannel channel, u32* pSharpness,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetVrCaptureInfo() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraGetWhiteBalance(s32 handle, OrbisCameraChannel channel,
+ OrbisCameraWhiteBalance* pWhiteBalance, void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraInitializeRegistryCalibData() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraIsAttached(s32 index) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraIsConfigChangeDone() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraIsValidFrameData(int handle, OrbisCameraFrameData* pFrameData) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 type,
+ s32 index, OrbisCameraOpenParameter* pParam) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraOpenByModuleId() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraRemoveAppModuleFocus() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetAppModuleFocus() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetAttribute(s32 handle, OrbisCameraAttribute* pAttribute) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetAttributeInternal() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetAutoExposureGain(s32 handle, OrbisCameraChannel channel, u32 enable,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetAutoWhiteBalance(s32 handle, OrbisCameraChannel channel, u32 enable,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetCalibData() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetConfig(s32 handle, OrbisCameraConfig* pConfig) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetConfigInternal() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetContrast(s32 handle, OrbisCameraChannel channel, u32 contrast,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetDebugStop() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetDefectivePixelCancellation(s32 handle, OrbisCameraChannel channel,
+ u32 enable, void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetDefectivePixelCancellationInternal() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetExposureGain(s32 handle, OrbisCameraChannel channel,
+ OrbisCameraExposureGain* pExposureGain, void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetForceActivate() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetGamma(s32 handle, OrbisCameraChannel channel, OrbisCameraGamma* pGamma,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetHue(s32 handle, OrbisCameraChannel channel, s32 hue, void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetLensCorrection(s32 handle, OrbisCameraChannel channel, u32 enable,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetLensCorrectionInternal() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetProcessFocus() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetProcessFocusByHandle() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetRegister() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetSaturation(s32 handle, OrbisCameraChannel channel, u32 saturation,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetSharpness(s32 handle, OrbisCameraChannel channel, u32 sharpness,
+ void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetTrackerMode() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetUacModeInternal() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetVideoSync(s32 handle, OrbisCameraVideoSyncParameter* pVideoSync) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetVideoSyncInternal() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraSetWhiteBalance(s32 handle, OrbisCameraChannel channel,
+ OrbisCameraWhiteBalance* pWhiteBalance, void* pOption) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraStart(s32 handle, OrbisCameraStartParameter* pParam) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraStartByHandle() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraStop(s32 handle) {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCameraStopByHandle() {
+ LOG_ERROR(Lib_Camera, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+void RegisterlibSceCamera(Core::Loader::SymbolsResolver* sym) {
+ LIB_FUNCTION("QhjrPkRPUZQ", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAccGetData);
+ LIB_FUNCTION("UFonL7xopFM", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAudioClose);
+ LIB_FUNCTION("fkZE7Hup2ro", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAudioGetData);
+ LIB_FUNCTION("hftC5A1C8OQ", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAudioGetData2);
+ LIB_FUNCTION("DhqqFiBU+6g", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAudioOpen);
+ LIB_FUNCTION("wyU98EXAYxU", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraAudioReset);
+ LIB_FUNCTION("Y0pCDajzkVQ", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraChangeAppModuleState);
+ LIB_FUNCTION("OMS9LlcrvBo", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraClose);
+ LIB_FUNCTION("ztqH5qNTpTk", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraCloseByHandle);
+ LIB_FUNCTION("nBH6i2s4Glc", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraDeviceOpen);
+ LIB_FUNCTION("0btIPD5hg5A", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetAttribute);
+ LIB_FUNCTION("oEi6vM-3E2c", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraGetAutoExposureGain);
+ LIB_FUNCTION("qTPRMh4eY60", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraGetAutoWhiteBalance);
+ LIB_FUNCTION("hHA1frlMxYE", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetCalibData);
+ LIB_FUNCTION("5Oie5RArfWs", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraGetCalibDataFromDevice);
+ LIB_FUNCTION("RHYJ7GKOSMg", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraGetCalibrationData);
+ LIB_FUNCTION("ZaqmGEtYuL0", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetConfig);
+ LIB_FUNCTION("a5xFueMZIMs", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetContrast);
+ LIB_FUNCTION("tslCukqFE+E", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraGetDefectivePixelCancellation);
+ LIB_FUNCTION("DSOLCrc3Kh8", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetDeviceConfig);
+ LIB_FUNCTION("n+rFeP1XXyM", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraGetDeviceConfigWithoutHandle);
+ LIB_FUNCTION("jTJCdyv9GLU", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetDeviceID);
+ LIB_FUNCTION("-H3UwGQvNZI", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraGetDeviceIDWithoutOpen);
+ LIB_FUNCTION("WZpxnSAM-ds", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetDeviceInfo);
+ LIB_FUNCTION("ObIste7hqdk", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetExposureGain);
+ LIB_FUNCTION("mxgMmR+1Kr0", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetFrameData);
+ LIB_FUNCTION("WVox2rwGuSc", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetGamma);
+ LIB_FUNCTION("zrIUDKZx0iE", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetHue);
+ LIB_FUNCTION("XqYRHc4aw3w", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraGetLensCorrection);
+ LIB_FUNCTION("B260o9pSzM8", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraGetMmapConnectedCount);
+ LIB_FUNCTION("ULxbwqiYYuU", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetProductInfo);
+ LIB_FUNCTION("olojYZKYiYs", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetRegister);
+ LIB_FUNCTION("hawKak+Auw4", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetRegistryInfo);
+ LIB_FUNCTION("RTDOsWWqdME", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetSaturation);
+ LIB_FUNCTION("c6Fp9M1EXXc", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetSharpness);
+ LIB_FUNCTION("IAz2HgZQWzE", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetVrCaptureInfo);
+ LIB_FUNCTION("HX5524E5tMY", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraGetWhiteBalance);
+ LIB_FUNCTION("0wnf2a60FqI", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraInitializeRegistryCalibData);
+ LIB_FUNCTION("p6n3Npi3YY4", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraIsAttached);
+ LIB_FUNCTION("wQfd7kfRZvo", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraIsConfigChangeDone);
+ LIB_FUNCTION("U3BVwQl2R5Q", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraIsValidFrameData);
+ LIB_FUNCTION("BHn83xrF92E", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraOpen);
+ LIB_FUNCTION("eTywOSWsEiI", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraOpenByModuleId);
+ LIB_FUNCTION("py8p6kZcHmA", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraRemoveAppModuleFocus);
+ LIB_FUNCTION("j5isFVIlZLk", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetAppModuleFocus);
+ LIB_FUNCTION("doPlf33ab-U", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetAttribute);
+ LIB_FUNCTION("96F7zp1Xo+k", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetAttributeInternal);
+ LIB_FUNCTION("yfSdswDaElo", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetAutoExposureGain);
+ LIB_FUNCTION("zIKL4kZleuc", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetAutoWhiteBalance);
+ LIB_FUNCTION("LEMk5cTHKEA", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetCalibData);
+ LIB_FUNCTION("VQ+5kAqsE2Q", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetConfig);
+ LIB_FUNCTION("9+SNhbctk64", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetConfigInternal);
+ LIB_FUNCTION("3i5MEzrC1pg", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetContrast);
+ LIB_FUNCTION("vejouEusC7g", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetDebugStop);
+ LIB_FUNCTION("jMv40y2A23g", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetDefectivePixelCancellation);
+ LIB_FUNCTION("vER3cIMBHqI", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetDefectivePixelCancellationInternal);
+ LIB_FUNCTION("wgBMXJJA6K4", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetExposureGain);
+ LIB_FUNCTION("jeTpU0MqKU0", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetForceActivate);
+ LIB_FUNCTION("lhEIsHzB8r4", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetGamma);
+ LIB_FUNCTION("QI8GVJUy2ZY", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetHue);
+ LIB_FUNCTION("K7W7H4ZRwbc", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetLensCorrection);
+ LIB_FUNCTION("eHa3vhGu2rQ", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetLensCorrectionInternal);
+ LIB_FUNCTION("lS0tM6n+Q5E", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetProcessFocus);
+ LIB_FUNCTION("NVITuK83Z7o", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetProcessFocusByHandle);
+ LIB_FUNCTION("8MjO05qk5hA", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetRegister);
+ LIB_FUNCTION("bSKEi2PzzXI", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetSaturation);
+ LIB_FUNCTION("P-7MVfzvpsM", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetSharpness);
+ LIB_FUNCTION("3VJOpzKoIeM", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetTrackerMode);
+ LIB_FUNCTION("nnR7KAIDPv8", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetUacModeInternal);
+ LIB_FUNCTION("wpeyFwJ+UEI", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetVideoSync);
+ LIB_FUNCTION("8WtmqmE4edw", "libSceCamera", 1, "libSceCamera", 1, 1,
+ sceCameraSetVideoSyncInternal);
+ LIB_FUNCTION("k3zPIcgFNv0", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraSetWhiteBalance);
+ LIB_FUNCTION("9EpRYMy7rHU", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraStart);
+ LIB_FUNCTION("cLxF1QtHch0", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraStartByHandle);
+ LIB_FUNCTION("2G2C0nmd++M", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraStop);
+ LIB_FUNCTION("+X1Kgnn3bzg", "libSceCamera", 1, "libSceCamera", 1, 1, sceCameraStopByHandle);
+};
+
+} // namespace Libraries::Camera
\ No newline at end of file
diff --git a/src/core/libraries/camera/camera.h b/src/core/libraries/camera/camera.h
new file mode 100644
index 000000000..51aa8b729
--- /dev/null
+++ b/src/core/libraries/camera/camera.h
@@ -0,0 +1,308 @@
+// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+#include "common/types.h"
+
+namespace Core::Loader {
+class SymbolsResolver;
+}
+
+namespace Libraries::Camera {
+
+constexpr int ORBIS_CAMERA_MAX_DEVICE_NUM = 2;
+constexpr int ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM = 4;
+
+enum OrbisCameraChannel {
+ ORBIS_CAMERA_CHANNEL_0 = 1,
+ ORBIS_CAMERA_CHANNEL_1 = 2,
+ ORBIS_CAMERA_CHANNEL_BOTH = 3,
+};
+
+struct OrbisCameraOpenParameter {
+ u32 sizeThis;
+ u32 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+};
+
+enum OrbisCameraConfigType {
+ ORBIS_CAMERA_CONFIG_TYPE1 = 0x01,
+ ORBIS_CAMERA_CONFIG_TYPE2 = 0x02,
+ ORBIS_CAMERA_CONFIG_TYPE3 = 0x03,
+ ORBIS_CAMERA_CONFIG_TYPE4 = 0x04,
+ ORBIS_CAMERA_CONFIG_TYPE5 = 0x05,
+ ORBIS_CAMERA_CONFIG_EXTENTION = 0x10,
+};
+
+enum OrbisCameraResolution {
+ ORBIS_CAMERA_RESOLUTION_1280X800 = 0x0,
+ ORBIS_CAMERA_RESOLUTION_640X400 = 0x1,
+ ORBIS_CAMERA_RESOLUTION_320X200 = 0x2,
+ ORBIS_CAMERA_RESOLUTION_160X100 = 0x3,
+ ORBIS_CAMERA_RESOLUTION_320X192 = 0x4,
+ ORBIS_CAMERA_RESOLUTION_SPECIFIED_WIDTH_HEIGHT,
+ ORBIS_CAMERA_RESOLUTION_UNKNOWN = 0xFF,
+};
+
+enum OrbisCameraFramerate {
+ ORBIS_CAMERA_FRAMERATE_UNKNOWN = 0,
+ ORBIS_CAMERA_FRAMERATE_7_5 = 7,
+ ORBIS_CAMERA_FRAMERATE_15 = 15,
+ ORBIS_CAMERA_FRAMERATE_30 = 30,
+ ORBIS_CAMERA_FRAMERATE_60 = 60,
+ ORBIS_CAMERA_FRAMERATE_120 = 120,
+ ORBIS_CAMERA_FRAMERATE_240 = 240,
+};
+
+enum OrbisCameraBaseFormat {
+ ORBIS_CAMERA_FORMAT_YUV422 = 0x0,
+ ORBIS_CAMERA_FORMAT_RAW16,
+ ORBIS_CAMERA_FORMAT_RAW8,
+ ORBIS_CAMERA_FORMAT_NO_USE = 0x10,
+ ORBIS_CAMERA_FORMAT_UNKNOWN = 0xFF,
+};
+
+enum OrbisCameraScaleFormat {
+ ORBIS_CAMERA_SCALE_FORMAT_YUV422 = 0x0,
+ ORBIS_CAMERA_SCALE_FORMAT_Y16 = 0x3,
+ ORBIS_CAMERA_SCALE_FORMAT_Y8,
+ ORBIS_CAMERA_SCALE_FORMAT_NO_USE = 0x10,
+ ORBIS_CAMERA_SCALE_FORMAT_UNKNOWN = 0xFF,
+};
+
+struct OrbisCameraFormat {
+ OrbisCameraBaseFormat formatLevel0;
+ OrbisCameraScaleFormat formatLevel1;
+ OrbisCameraScaleFormat formatLevel2;
+ OrbisCameraScaleFormat formatLevel3;
+};
+
+struct OrbisCameraConfigExtention {
+ OrbisCameraFormat format;
+ OrbisCameraResolution resolution;
+ OrbisCameraFramerate framerate;
+ u32 width;
+ u32 height;
+ u32 reserved1;
+ void* pBaseOption;
+};
+
+struct OrbisCameraConfig {
+ u32 sizeThis;
+ OrbisCameraConfigType configType;
+ OrbisCameraConfigExtention configExtention[ORBIS_CAMERA_MAX_DEVICE_NUM];
+};
+
+enum OrbisCameraAecAgcTarget {
+ ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_DEF = 0x00,
+ ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_2_0 = 0x20,
+ ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_1_6 = 0x16,
+ ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_1_4 = 0x14,
+ ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_1_2 = 0x12,
+ ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_1_0 = 0x10,
+ ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_0_8 = 0x08,
+ ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_0_6 = 0x06,
+ ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_0_4 = 0x04,
+ ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_0_2 = 0x02,
+};
+
+struct OrbisCameraDeviceInfo {
+ u32 sizeThis;
+ u32 infoRevision;
+ u32 deviceRevision;
+ u32 padding;
+};
+
+struct OrbisCameraStartParameter {
+ u32 sizeThis;
+ u32 formatLevel[ORBIS_CAMERA_MAX_DEVICE_NUM];
+ void* pStartOption;
+};
+
+struct OrbisCameraVideoSyncParameter {
+ u32 sizeThis;
+ u32 videoSyncMode;
+ void* pModeOption;
+};
+
+struct OrbisCameraFramePosition {
+ u32 x;
+ u32 y;
+ u32 xSize;
+ u32 ySize;
+};
+
+struct OrbisCameraAutoExposureGainTarget {
+ u32 sizeThis;
+ OrbisCameraAecAgcTarget target;
+};
+
+struct OrbisCameraExposureGain {
+ u32 exposureControl;
+ u32 exposure;
+ u32 gain;
+ u32 mode;
+};
+
+struct OrbisCameraWhiteBalance {
+ u32 whiteBalanceControl;
+ u32 gainRed;
+ u32 gainBlue;
+ u32 gainGreen;
+};
+
+struct OrbisCameraGamma {
+ u32 gammaControl;
+ u32 value;
+ u8 reserved[16];
+};
+
+struct OrbisCameraMeta {
+ u32 metaMode;
+ u32 format[ORBIS_CAMERA_MAX_DEVICE_NUM][ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM];
+ u64 frame[ORBIS_CAMERA_MAX_DEVICE_NUM];
+ u64 timestamp[ORBIS_CAMERA_MAX_DEVICE_NUM];
+ u32 deviceTimestamp[ORBIS_CAMERA_MAX_DEVICE_NUM];
+ OrbisCameraExposureGain exposureGain[ORBIS_CAMERA_MAX_DEVICE_NUM];
+ OrbisCameraWhiteBalance whiteBalance[ORBIS_CAMERA_MAX_DEVICE_NUM];
+ OrbisCameraGamma gamma[ORBIS_CAMERA_MAX_DEVICE_NUM];
+ u32 luminance[ORBIS_CAMERA_MAX_DEVICE_NUM];
+ float acceleration_x;
+ float acceleration_y;
+ float acceleration_z;
+ u64 vcounter;
+ u32 reserved[14];
+};
+
+struct OrbisCameraFrameData {
+ u32 sizeThis;
+ u32 readMode;
+ OrbisCameraFramePosition framePosition[ORBIS_CAMERA_MAX_DEVICE_NUM]
+ [ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM];
+ void* pFramePointerList[ORBIS_CAMERA_MAX_DEVICE_NUM][ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM];
+ u32 frameSize[ORBIS_CAMERA_MAX_DEVICE_NUM][ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM];
+ u32 status[ORBIS_CAMERA_MAX_DEVICE_NUM];
+ OrbisCameraMeta meta;
+ void* pFramePointerListGarlic[ORBIS_CAMERA_MAX_DEVICE_NUM][ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM];
+};
+
+struct OrbisCameraAttribute {
+ u32 sizeThis;
+ OrbisCameraChannel channel;
+ OrbisCameraFramePosition framePosition;
+ OrbisCameraExposureGain exposureGain;
+ OrbisCameraWhiteBalance whiteBalance;
+ OrbisCameraGamma gamma;
+ u32 saturation;
+ u32 contrast;
+ u32 sharpness;
+ s32 hue;
+ u32 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+ u32 reserved4;
+};
+
+s32 PS4_SYSV_ABI sceCameraAccGetData();
+s32 PS4_SYSV_ABI sceCameraAudioClose();
+s32 PS4_SYSV_ABI sceCameraAudioGetData();
+s32 PS4_SYSV_ABI sceCameraAudioGetData2();
+s32 PS4_SYSV_ABI sceCameraAudioOpen();
+s32 PS4_SYSV_ABI sceCameraAudioReset();
+s32 PS4_SYSV_ABI sceCameraChangeAppModuleState();
+s32 PS4_SYSV_ABI sceCameraClose(s32 handle);
+s32 PS4_SYSV_ABI sceCameraCloseByHandle();
+s32 PS4_SYSV_ABI sceCameraDeviceOpen();
+s32 PS4_SYSV_ABI sceCameraGetAttribute(s32 handle, OrbisCameraAttribute* pAttribute);
+s32 PS4_SYSV_ABI sceCameraGetAutoExposureGain(s32 handle, OrbisCameraChannel channel, u32* pEnable,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraGetAutoWhiteBalance(s32 handle, OrbisCameraChannel channel, u32* pEnable,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraGetCalibData();
+s32 PS4_SYSV_ABI sceCameraGetCalibDataFromDevice();
+s32 PS4_SYSV_ABI sceCameraGetCalibrationData();
+s32 PS4_SYSV_ABI sceCameraGetConfig(s32 handle, OrbisCameraConfig* pConfig);
+s32 PS4_SYSV_ABI sceCameraGetContrast(s32 handle, OrbisCameraChannel channel, u32* pContrast,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraGetDefectivePixelCancellation(s32 handle, OrbisCameraChannel channel,
+ u32* pEnable, void* pOption);
+s32 PS4_SYSV_ABI sceCameraGetDeviceConfig();
+s32 PS4_SYSV_ABI sceCameraGetDeviceConfigWithoutHandle();
+s32 PS4_SYSV_ABI sceCameraGetDeviceID();
+s32 PS4_SYSV_ABI sceCameraGetDeviceIDWithoutOpen();
+s32 PS4_SYSV_ABI sceCameraGetDeviceInfo(s32 reserved, OrbisCameraDeviceInfo* pDeviceInfo);
+s32 PS4_SYSV_ABI sceCameraGetExposureGain(s32 handle, OrbisCameraChannel channel,
+ OrbisCameraExposureGain* pExposureGain, void* pOption);
+s32 PS4_SYSV_ABI sceCameraGetFrameData(int handle, OrbisCameraFrameData* pFrameData);
+s32 PS4_SYSV_ABI sceCameraGetGamma(s32 handle, OrbisCameraChannel channel, OrbisCameraGamma* pGamma,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraGetHue(s32 handle, OrbisCameraChannel channel, s32* pHue, void* pOption);
+s32 PS4_SYSV_ABI sceCameraGetLensCorrection(s32 handle, OrbisCameraChannel channel, u32* pEnable,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraGetMmapConnectedCount();
+s32 PS4_SYSV_ABI sceCameraGetProductInfo();
+s32 PS4_SYSV_ABI sceCameraGetRegister();
+s32 PS4_SYSV_ABI sceCameraGetRegistryInfo();
+s32 PS4_SYSV_ABI sceCameraGetSaturation(s32 handle, OrbisCameraChannel channel, u32* pSaturation,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraGetSharpness(s32 handle, OrbisCameraChannel channel, u32* pSharpness,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraGetVrCaptureInfo();
+s32 PS4_SYSV_ABI sceCameraGetWhiteBalance(s32 handle, OrbisCameraChannel channel,
+ OrbisCameraWhiteBalance* pWhiteBalance, void* pOption);
+s32 PS4_SYSV_ABI sceCameraInitializeRegistryCalibData();
+s32 PS4_SYSV_ABI sceCameraIsAttached(s32 index);
+s32 PS4_SYSV_ABI sceCameraIsConfigChangeDone();
+s32 PS4_SYSV_ABI sceCameraIsValidFrameData(int handle, OrbisCameraFrameData* pFrameData);
+s32 PS4_SYSV_ABI sceCameraOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 type,
+ s32 index, OrbisCameraOpenParameter* pParam);
+s32 PS4_SYSV_ABI sceCameraOpenByModuleId();
+s32 PS4_SYSV_ABI sceCameraRemoveAppModuleFocus();
+s32 PS4_SYSV_ABI sceCameraSetAppModuleFocus();
+s32 PS4_SYSV_ABI sceCameraSetAttribute(s32 handle, OrbisCameraAttribute* pAttribute);
+s32 PS4_SYSV_ABI sceCameraSetAttributeInternal();
+s32 PS4_SYSV_ABI sceCameraSetAutoExposureGain(s32 handle, OrbisCameraChannel channel, u32 enable,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraSetAutoWhiteBalance(s32 handle, OrbisCameraChannel channel, u32 enable,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraSetCalibData();
+s32 PS4_SYSV_ABI sceCameraSetConfig(s32 handle, OrbisCameraConfig* pConfig);
+s32 PS4_SYSV_ABI sceCameraSetConfigInternal();
+s32 PS4_SYSV_ABI sceCameraSetContrast(s32 handle, OrbisCameraChannel channel, u32 contrast,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraSetDebugStop();
+s32 PS4_SYSV_ABI sceCameraSetDefectivePixelCancellation(s32 handle, OrbisCameraChannel channel,
+ u32 enable, void* pOption);
+s32 PS4_SYSV_ABI sceCameraSetDefectivePixelCancellationInternal();
+s32 PS4_SYSV_ABI sceCameraSetExposureGain(s32 handle, OrbisCameraChannel channel,
+ OrbisCameraExposureGain* pExposureGain, void* pOption);
+s32 PS4_SYSV_ABI sceCameraSetForceActivate();
+s32 PS4_SYSV_ABI sceCameraSetGamma(s32 handle, OrbisCameraChannel channel, OrbisCameraGamma* pGamma,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraSetHue(s32 handle, OrbisCameraChannel channel, s32 hue, void* pOption);
+s32 PS4_SYSV_ABI sceCameraSetLensCorrection(s32 handle, OrbisCameraChannel channel, u32 enable,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraSetLensCorrectionInternal();
+s32 PS4_SYSV_ABI sceCameraSetProcessFocus();
+s32 PS4_SYSV_ABI sceCameraSetProcessFocusByHandle();
+s32 PS4_SYSV_ABI sceCameraSetRegister();
+s32 PS4_SYSV_ABI sceCameraSetSaturation(s32 handle, OrbisCameraChannel channel, u32 saturation,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraSetSharpness(s32 handle, OrbisCameraChannel channel, u32 sharpness,
+ void* pOption);
+s32 PS4_SYSV_ABI sceCameraSetTrackerMode();
+s32 PS4_SYSV_ABI sceCameraSetUacModeInternal();
+s32 PS4_SYSV_ABI sceCameraSetVideoSync(s32 handle, OrbisCameraVideoSyncParameter* pVideoSync);
+s32 PS4_SYSV_ABI sceCameraSetVideoSyncInternal();
+s32 PS4_SYSV_ABI sceCameraSetWhiteBalance(s32 handle, OrbisCameraChannel channel,
+ OrbisCameraWhiteBalance* pWhiteBalance, void* pOption);
+s32 PS4_SYSV_ABI sceCameraStart(s32 handle, OrbisCameraStartParameter* pParam);
+s32 PS4_SYSV_ABI sceCameraStartByHandle();
+s32 PS4_SYSV_ABI sceCameraStop(s32 handle);
+s32 PS4_SYSV_ABI sceCameraStopByHandle();
+
+void RegisterlibSceCamera(Core::Loader::SymbolsResolver* sym);
+} // namespace Libraries::Camera
\ No newline at end of file
diff --git a/src/core/libraries/camera/camera_error.h b/src/core/libraries/camera/camera_error.h
new file mode 100644
index 000000000..acb04dd02
--- /dev/null
+++ b/src/core/libraries/camera/camera_error.h
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+constexpr int ORBIS_CAMERA_ERROR_PARAM = 0x802E0000;
+constexpr int ORBIS_CAMERA_ERROR_ALREADY_INIT = 0x802E0001;
+constexpr int ORBIS_CAMERA_ERROR_NOT_INIT = 0x802E0002;
+constexpr int ORBIS_CAMERA_ERROR_ALREADY_OPEN = 0x802E0003;
+constexpr int ORBIS_CAMERA_ERROR_NOT_OPEN = 0x802E0004;
+constexpr int ORBIS_CAMERA_ERROR_ALREADY_START = 0x802E0005;
+constexpr int ORBIS_CAMERA_ERROR_NOT_START = 0x802E0006;
+constexpr int ORBIS_CAMERA_ERROR_FORMAT_UNKNOWN = 0x802E0007;
+constexpr int ORBIS_CAMERA_ERROR_RESOLUTION_UNKNOWN = 0x802E0008;
+constexpr int ORBIS_CAMERA_ERROR_BAD_FRAMERATE = 0x802E0009;
+constexpr int ORBIS_CAMERA_ERROR_TIMEOUT = 0x802E000A;
+constexpr int ORBIS_CAMERA_ERROR_ATTRIBUTE_UNKNOWN = 0x802E000B;
+constexpr int ORBIS_CAMERA_ERROR_BUSY = 0x802E000C;
+constexpr int ORBIS_CAMERA_ERROR_UNKNOWN_CONFIG = 0x802E000D;
+constexpr int ORBIS_CAMERA_ERROR_ALREADY_READ = 0x802E000F;
+constexpr int ORBIS_CAMERA_ERROR_NOT_CONNECTED = 0x802E0010;
+constexpr int ORBIS_CAMERA_ERROR_NOT_SUPPORTED = 0x802E0011;
+constexpr int ORBIS_CAMERA_ERROR_INVALID_CONFIG = 0x802E0013;
+constexpr int ORBIS_CAMERA_ERROR_MAX_HANDLE = 0x802E0014;
+constexpr int ORBIS_CAMERA_ERROR_MAX_PROCESS = 0x802E00FB;
+constexpr int ORBIS_CAMERA_ERROR_COPYOUT_FAILED = 0x802E00FC;
+constexpr int ORBIS_CAMERA_ERROR_COPYIN_FAILED = 0x802E00FD;
+constexpr int ORBIS_CAMERA_ERROR_KPROC_CREATE = 0x802E00FE;
+constexpr int ORBIS_CAMERA_ERROR_FATAL = 0x802E00FF;
\ No newline at end of file
diff --git a/src/core/libraries/companion/companion_error.h b/src/core/libraries/companion/companion_error.h
new file mode 100644
index 000000000..0459c33f8
--- /dev/null
+++ b/src/core/libraries/companion/companion_error.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/types.h"
+
+// companion_httpd error codes
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_UNKNOWN = 0x80E40001;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_FATAL = 0x80E40002;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOMEM = 0x80E40003;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_INVALID_PARAM = 0x80E40004;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_INVALID_OPERATION = 0x80E40005;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_INITIALIZED = 0x80E40006;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_ALREADY_INITIALIZED = 0x80E40007;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_NO_EVENT = 0x80E40008;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_GENERATE_RESPONSE = 0x80E40009;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_ALREADY_STARTED = 0x80E4000A;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_STARTED = 0x80E4000B;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_ALREADY_REGISTERED = 0x80E4000;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_CONNECTED = 0x80E4000D;
+constexpr int ORBIS_COMPANION_HTTPD_ERROR_USER_NOT_FOUND = 0x80E4000E;
+
+// companion_util error codes
+constexpr u32 ORBIS_COMPANION_UTIL_INVALID_ARGUMENT = 0x80AD0004;
+constexpr u32 ORBIS_COMPANION_UTIL_INVALID_POINTER = 0x80AD0006;
+constexpr u32 ORBIS_COMPANION_UTIL_NO_EVENT = 0x80AD0008;
\ No newline at end of file
diff --git a/src/core/libraries/companion/companion_httpd.cpp b/src/core/libraries/companion/companion_httpd.cpp
new file mode 100644
index 000000000..39081fa4e
--- /dev/null
+++ b/src/core/libraries/companion/companion_httpd.cpp
@@ -0,0 +1,142 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "companion_error.h"
+#include "core/libraries/companion/companion_httpd.h"
+#include "core/libraries/error_codes.h"
+#include "core/libraries/libs.h"
+
+namespace Libraries::CompanionHttpd {
+
+s32 PS4_SYSV_ABI sceCompanionHttpdAddHeader(const char* key, const char* value,
+ OrbisCompanionHttpdResponse* response) {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI
+sceCompanionHttpdGet2ndScreenStatus(Libraries::UserService::OrbisUserServiceUserId) {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdGetEvent(OrbisCompanionHttpdEvent* pEvent) {
+ pEvent->event = ORBIS_COMPANION_HTTPD_EVENT_DISCONNECT; // disconnected
+ LOG_DEBUG(Lib_CompanionHttpd, "device disconnected");
+ return ORBIS_COMPANION_HTTPD_ERROR_NO_EVENT; // No events to obtain
+}
+
+s32 PS4_SYSV_ABI
+sceCompanionHttpdGetUserId(u32 addr, Libraries::UserService::OrbisUserServiceUserId* userId) {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdInitialize() {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdInitialize2() {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdOptParamInitialize() {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdRegisterRequestBodyReceptionCallback(
+ OrbisCompanionHttpdRequestBodyReceptionCallback function, void* param) {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI
+sceCompanionHttpdRegisterRequestCallback(OrbisCompanionHttpdRequestCallback function, void* param) {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdRegisterRequestCallback2(
+ OrbisCompanionHttpdRequestCallback function, void* param) {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdSetBody(const char* body, u64 bodySize,
+ OrbisCompanionHttpdResponse* response) {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdSetStatus(s32 status, OrbisCompanionHttpdResponse* response) {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdStart() {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdStop() {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdTerminate() {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdUnregisterRequestBodyReceptionCallback() {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionHttpdUnregisterRequestCallback() {
+ LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+void RegisterlibSceCompanionHttpd(Core::Loader::SymbolsResolver* sym) {
+ LIB_FUNCTION("8pWltDG7h6A", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdAddHeader);
+ LIB_FUNCTION("B-QBMeFdNgY", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdGet2ndScreenStatus);
+ LIB_FUNCTION("Vku4big+IYM", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdGetEvent);
+ LIB_FUNCTION("0SySxcuVNG0", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdGetUserId);
+ LIB_FUNCTION("ykNpWs3ktLY", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdInitialize);
+ LIB_FUNCTION("OA6FbORefbo", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdInitialize2);
+ LIB_FUNCTION("r-2-a0c7Kfc", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdOptParamInitialize);
+ LIB_FUNCTION("fHNmij7kAUM", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdRegisterRequestBodyReceptionCallback);
+ LIB_FUNCTION("OaWw+IVEdbI", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdRegisterRequestCallback);
+ LIB_FUNCTION("-0c9TCTwnGs", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdRegisterRequestCallback2);
+ LIB_FUNCTION("h3OvVxzX4qM", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdSetBody);
+ LIB_FUNCTION("w7oz0AWHpT4", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdSetStatus);
+ LIB_FUNCTION("k7F0FcDM-Xc", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdStart);
+ LIB_FUNCTION("0SCgzfVQHpo", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdStop);
+ LIB_FUNCTION("+-du9tWgE9s", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdTerminate);
+ LIB_FUNCTION("ZSHiUfYK+QI", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdUnregisterRequestBodyReceptionCallback);
+ LIB_FUNCTION("xweOi2QT-BE", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
+ sceCompanionHttpdUnregisterRequestCallback);
+};
+
+} // namespace Libraries::CompanionHttpd
\ No newline at end of file
diff --git a/src/core/libraries/companion/companion_httpd.h b/src/core/libraries/companion/companion_httpd.h
new file mode 100644
index 000000000..b6d441653
--- /dev/null
+++ b/src/core/libraries/companion/companion_httpd.h
@@ -0,0 +1,91 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/types.h"
+#include "core/libraries/network/net.h"
+#include "core/libraries/system/userservice.h"
+
+namespace Core::Loader {
+class SymbolsResolver;
+}
+
+namespace Libraries::CompanionHttpd {
+
+// OrbisCompanionHttpdEvent event codes
+constexpr int ORBIS_COMPANION_HTTPD_EVENT_CONNECT = 0x10000001;
+constexpr int ORBIS_COMPANION_HTTPD_EVENT_DISCONNECT = 0x10000002;
+
+struct OrbisCompanionHttpdHeader {
+ char* key;
+ char* value;
+ struct OrbisCompanionHttpdHeader* header;
+};
+
+struct OrbisCompanionHttpdRequest {
+ s32 method;
+ char* url;
+ OrbisCompanionHttpdHeader* header;
+ char* body;
+ u64 bodySize;
+};
+
+struct OrbisCompanionHttpdResponse {
+ s32 status;
+ OrbisCompanionHttpdHeader* header;
+ char* body;
+ u64 bodySize;
+};
+
+using OrbisCompanionHttpdRequestBodyReceptionCallback =
+ PS4_SYSV_ABI s32 (*)(s32 event, Libraries::UserService::OrbisUserServiceUserId userId,
+ const OrbisCompanionHttpdRequest* httpRequest, void* param);
+
+using OrbisCompanionHttpdRequestCallback =
+ PS4_SYSV_ABI s32 (*)(Libraries::UserService::OrbisUserServiceUserId userId,
+ const OrbisCompanionHttpdRequest* httpRequest,
+ OrbisCompanionHttpdResponse* httpResponse, void* param);
+
+struct OrbisCompanionUtilDeviceInfo {
+ Libraries::UserService::OrbisUserServiceUserId userId;
+ Libraries::Net::OrbisNetSockaddrIn addr;
+ char reserved[236];
+};
+
+struct OrbisCompanionHttpdEvent {
+ s32 event;
+ union {
+ OrbisCompanionUtilDeviceInfo deviceInfo;
+ Libraries::UserService::OrbisUserServiceUserId userId;
+ char reserved[256];
+ } data;
+};
+
+s32 PS4_SYSV_ABI sceCompanionHttpdAddHeader(const char* key, const char* value,
+ OrbisCompanionHttpdResponse* response);
+s32 PS4_SYSV_ABI
+sceCompanionHttpdGet2ndScreenStatus(Libraries::UserService::OrbisUserServiceUserId userId);
+s32 PS4_SYSV_ABI sceCompanionHttpdGetEvent(OrbisCompanionHttpdEvent* pEvent);
+s32 PS4_SYSV_ABI sceCompanionHttpdGetUserId(u32 addr,
+ Libraries::UserService::OrbisUserServiceUserId* userId);
+s32 PS4_SYSV_ABI sceCompanionHttpdInitialize();
+s32 PS4_SYSV_ABI sceCompanionHttpdInitialize2();
+s32 PS4_SYSV_ABI sceCompanionHttpdOptParamInitialize();
+s32 PS4_SYSV_ABI sceCompanionHttpdRegisterRequestBodyReceptionCallback(
+ OrbisCompanionHttpdRequestBodyReceptionCallback function, void* param);
+s32 PS4_SYSV_ABI
+sceCompanionHttpdRegisterRequestCallback(OrbisCompanionHttpdRequestCallback function, void* param);
+s32 PS4_SYSV_ABI
+sceCompanionHttpdRegisterRequestCallback2(OrbisCompanionHttpdRequestCallback function, void* param);
+s32 PS4_SYSV_ABI sceCompanionHttpdSetBody(const char* body, u64 bodySize,
+ OrbisCompanionHttpdResponse* response);
+s32 PS4_SYSV_ABI sceCompanionHttpdSetStatus(s32 status, OrbisCompanionHttpdResponse* response);
+s32 PS4_SYSV_ABI sceCompanionHttpdStart();
+s32 PS4_SYSV_ABI sceCompanionHttpdStop();
+s32 PS4_SYSV_ABI sceCompanionHttpdTerminate();
+s32 PS4_SYSV_ABI sceCompanionHttpdUnregisterRequestBodyReceptionCallback();
+s32 PS4_SYSV_ABI sceCompanionHttpdUnregisterRequestCallback();
+
+void RegisterlibSceCompanionHttpd(Core::Loader::SymbolsResolver* sym);
+} // namespace Libraries::CompanionHttpd
\ No newline at end of file
diff --git a/src/core/libraries/companion/companion_util.cpp b/src/core/libraries/companion/companion_util.cpp
new file mode 100644
index 000000000..c144ebdcc
--- /dev/null
+++ b/src/core/libraries/companion/companion_util.cpp
@@ -0,0 +1,72 @@
+// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "companion_error.h"
+#include "core/libraries/companion/companion_util.h"
+#include "core/libraries/error_codes.h"
+#include "core/libraries/libs.h"
+
+namespace Libraries::CompanionUtil {
+
+u32 PS4_SYSV_ABI getEvent(sceCompanionUtilContext* ctx, sceCompanionUtilEvent* outEvent,
+ s32 param_3) {
+ if (outEvent == 0) {
+ return ORBIS_COMPANION_UTIL_INVALID_ARGUMENT;
+ }
+
+ if (ctx == nullptr) {
+ return ORBIS_COMPANION_UTIL_INVALID_POINTER;
+ }
+
+ uint8_t* base = ctx->blob;
+ int flag = *reinterpret_cast(base + 0x178);
+ if (flag == 0) {
+ return ORBIS_COMPANION_UTIL_NO_EVENT;
+ }
+
+ return ORBIS_COMPANION_UTIL_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionUtilGetEvent(sceCompanionUtilEvent* outEvent) {
+ sceCompanionUtilContext* ctx = nullptr;
+ u32 ret = getEvent(ctx, outEvent, 1);
+
+ LOG_DEBUG(Lib_CompanionUtil, "(STUBBED) called ret: {}", ret);
+ return ret;
+}
+
+s32 PS4_SYSV_ABI sceCompanionUtilGetRemoteOskEvent() {
+ LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionUtilInitialize() {
+ LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionUtilOptParamInitialize() {
+ LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceCompanionUtilTerminate() {
+ LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+void RegisterlibSceCompanionUtil(Core::Loader::SymbolsResolver* sym) {
+ LIB_FUNCTION("cE5Msy11WhU", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
+ sceCompanionUtilGetEvent);
+ LIB_FUNCTION("MaVrz79mT5o", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
+ sceCompanionUtilGetRemoteOskEvent);
+ LIB_FUNCTION("xb1xlIhf0QY", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
+ sceCompanionUtilInitialize);
+ LIB_FUNCTION("IPN-FRSrafk", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
+ sceCompanionUtilOptParamInitialize);
+ LIB_FUNCTION("H1fYQd5lFAI", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
+ sceCompanionUtilTerminate);
+};
+
+} // namespace Libraries::CompanionUtil
\ No newline at end of file
diff --git a/src/core/libraries/companion/companion_util.h b/src/core/libraries/companion/companion_util.h
new file mode 100644
index 000000000..921b5b21e
--- /dev/null
+++ b/src/core/libraries/companion/companion_util.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/types.h"
+
+namespace Core::Loader {
+class SymbolsResolver;
+}
+
+namespace Libraries::CompanionUtil {
+
+constexpr u32 ORBIS_COMPANION_UTIL_OK = 0;
+
+struct sceCompanionUtilEvent {
+ std::uint8_t blob[0x104]{}; /// 0x104 bytes of data, dont know what it is exactly
+};
+
+struct sceCompanionUtilContext {
+ std::uint8_t blob[0x27B]{}; /// 0x27B bytes of data, dont know what it is exactly
+};
+
+u32 PS4_SYSV_ABI getEvent(sceCompanionUtilContext* ctx, sceCompanionUtilEvent* outEvent,
+ s32 param_3);
+s32 PS4_SYSV_ABI sceCompanionUtilGetEvent(sceCompanionUtilEvent* outEvent);
+s32 PS4_SYSV_ABI sceCompanionUtilGetRemoteOskEvent();
+s32 PS4_SYSV_ABI sceCompanionUtilInitialize();
+s32 PS4_SYSV_ABI sceCompanionUtilOptParamInitialize();
+s32 PS4_SYSV_ABI sceCompanionUtilTerminate();
+
+void RegisterlibSceCompanionUtil(Core::Loader::SymbolsResolver* sym);
+} // namespace Libraries::CompanionUtil
\ No newline at end of file
diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp
index f2f40e0e3..9cf340050 100644
--- a/src/core/libraries/gnmdriver/gnmdriver.cpp
+++ b/src/core/libraries/gnmdriver/gnmdriver.cpp
@@ -179,7 +179,7 @@ s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t add
auto* wait_reg_mem = reinterpret_cast(cmdbuf);
wait_reg_mem->header = PM4Type3Header{PM4ItOpcode::WaitRegMem, 5};
wait_reg_mem->raw = (is_mem << 4u) | (cmp_func & 7u);
- wait_reg_mem->poll_addr_lo = u32(addr & addr_mask);
+ wait_reg_mem->poll_addr_lo_raw = u32(addr & addr_mask);
wait_reg_mem->poll_addr_hi = u32(addr >> 32u);
wait_reg_mem->ref = ref;
wait_reg_mem->mask = mask;
diff --git a/src/core/libraries/kernel/aio.cpp b/src/core/libraries/kernel/aio.cpp
index e017010cb..1d746860b 100644
--- a/src/core/libraries/kernel/aio.cpp
+++ b/src/core/libraries/kernel/aio.cpp
@@ -19,7 +19,7 @@ namespace Libraries::Kernel {
static s32* id_state;
static s32 id_index;
-s32 sceKernelAioInitializeImpl(void* p, s32 size) {
+s32 PS4_SYSV_ABI sceKernelAioInitializeImpl(void* p, s32 size) {
return 0;
}
diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp
index a4916208a..4d1b116c5 100644
--- a/src/core/libraries/kernel/equeue.cpp
+++ b/src/core/libraries/kernel/equeue.cpp
@@ -12,12 +12,25 @@
namespace Libraries::Kernel {
+extern boost::asio::io_context io_context;
+extern void KernelSignalRequest();
+
+static constexpr auto HrTimerSpinlockThresholdUs = 1200u;
+
// Events are uniquely identified by id and filter.
bool EqueueInternal::AddEvent(EqueueEvent& event) {
std::scoped_lock lock{m_mutex};
event.time_added = std::chrono::steady_clock::now();
+ if (event.event.filter == SceKernelEvent::Filter::Timer ||
+ event.event.filter == SceKernelEvent::Filter::HrTimer) {
+ // HrTimer events are offset by the threshold of time at the end that we spinlock for
+ // greater accuracy.
+ const auto offset =
+ event.event.filter == SceKernelEvent::Filter::HrTimer ? HrTimerSpinlockThresholdUs : 0u;
+ event.timer_interval = std::chrono::microseconds(event.event.data - offset);
+ }
const auto& it = std::ranges::find(m_events, event);
if (it != m_events.cend()) {
@@ -29,6 +42,47 @@ bool EqueueInternal::AddEvent(EqueueEvent& event) {
return true;
}
+bool EqueueInternal::ScheduleEvent(u64 id, s16 filter,
+ void (*callback)(SceKernelEqueue, const SceKernelEvent&)) {
+ std::scoped_lock lock{m_mutex};
+
+ const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) {
+ return ev.event.ident == id && ev.event.filter == filter;
+ });
+ if (it == m_events.cend()) {
+ return false;
+ }
+
+ const auto& event = *it;
+ ASSERT(event.event.filter == SceKernelEvent::Filter::Timer ||
+ event.event.filter == SceKernelEvent::Filter::HrTimer);
+
+ if (!it->timer) {
+ it->timer = std::make_unique(io_context, event.timer_interval);
+ } else {
+ // If the timer already exists we are scheduling a reoccurrence after the next period.
+ // Set the expiration time to the previous occurrence plus the period.
+ it->timer->expires_at(it->timer->expiry() + event.timer_interval);
+ }
+
+ it->timer->async_wait(
+ [this, event_data = event.event, callback](const boost::system::error_code& ec) {
+ if (ec) {
+ if (ec != boost::system::errc::operation_canceled) {
+ LOG_ERROR(Kernel_Event, "Timer callback error: {}", ec.message());
+ } else {
+ // Timer was cancelled (removed) before it triggered
+ LOG_DEBUG(Kernel_Event, "Timer cancelled");
+ }
+ return;
+ }
+ callback(this, event_data);
+ });
+ KernelSignalRequest();
+
+ return true;
+}
+
bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
bool has_found = false;
std::scoped_lock lock{m_mutex};
@@ -44,6 +98,11 @@ bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
}
int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) {
+ if (HasSmallTimer()) {
+ // If a small timer is set, just wait for it to expire.
+ return WaitForSmallTimer(ev, num, micros);
+ }
+
int count = 0;
const auto predicate = [&] {
@@ -66,7 +125,6 @@ int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) {
.count();
count = WaitForSmallTimer(ev, num, std::max(0l, long(micros - time_waited)));
}
- small_timer_event.event.data = 0;
}
if (ev->flags & SceKernelEvent::Flags::OneShot) {
@@ -86,6 +144,8 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
if (event.event.ident == ident && event.event.filter == filter) {
if (filter == SceKernelEvent::Filter::VideoOut) {
event.TriggerDisplay(trigger_data);
+ } else if (filter == SceKernelEvent::Filter::User) {
+ event.TriggerUser(trigger_data);
} else {
event.Trigger(trigger_data);
}
@@ -118,52 +178,56 @@ int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) {
}
bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) {
- // We assume that only one timer event (with the same ident across calls)
- // can be posted to the queue, based on observations so far. In the opposite case,
- // the small timer storage and wait logic should be reworked.
- ASSERT(!HasSmallTimer() || small_timer_event.event.ident == ev.event.ident);
- ev.time_added = std::chrono::steady_clock::now();
- small_timer_event = std::move(ev);
+ SmallTimer st;
+ st.event = ev.event;
+ st.added = std::chrono::steady_clock::now();
+ st.interval = std::chrono::microseconds{ev.event.data};
+ {
+ std::scoped_lock lock{m_mutex};
+ m_small_timers[st.event.ident] = std::move(st);
+ }
return true;
}
int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) {
- int count{};
-
- ASSERT(num == 1);
+ ASSERT(num >= 1);
auto curr_clock = std::chrono::steady_clock::now();
- const auto wait_end_us = curr_clock + std::chrono::microseconds{micros};
-
+ const auto wait_end_us = (micros == 0) ? std::chrono::steady_clock::time_point::max()
+ : curr_clock + std::chrono::microseconds{micros};
+ int count = 0;
do {
curr_clock = std::chrono::steady_clock::now();
{
std::scoped_lock lock{m_mutex};
- if ((curr_clock - small_timer_event.time_added) >
- std::chrono::microseconds{small_timer_event.event.data}) {
- ev[count++] = small_timer_event.event;
- small_timer_event.event.data = 0;
- break;
+ for (auto it = m_small_timers.begin(); it != m_small_timers.end() && count < num;) {
+ const SmallTimer& st = it->second;
+
+ if (curr_clock - st.added >= st.interval) {
+ ev[count++] = st.event;
+ it = m_small_timers.erase(it);
+ } else {
+ ++it;
+ }
}
+
+ if (count > 0)
+ return count;
}
std::this_thread::yield();
} while (curr_clock < wait_end_us);
- return count;
+ return 0;
}
-extern boost::asio::io_context io_context;
-extern void KernelSignalRequest();
+bool EqueueInternal::EventExists(u64 id, s16 filter) {
+ std::scoped_lock lock{m_mutex};
-static constexpr auto HrTimerSpinlockThresholdUs = 1200u;
+ const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) {
+ return ev.event.ident == id && ev.event.filter == filter;
+ });
-static void SmallTimerCallback(const boost::system::error_code& error, SceKernelEqueue eq,
- SceKernelEvent kevent) {
- static EqueueEvent event;
- event.event = kevent;
- event.event.data = HrTimerSpinlockThresholdUs;
- eq->AddSmallTimer(event);
- eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::HrTimer, kevent.udata);
+ return it != m_events.cend();
}
int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) {
@@ -216,24 +280,15 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
return ORBIS_KERNEL_ERROR_EINVAL;
}
- if (eq->HasSmallTimer()) {
- ASSERT(timo && *timo);
- *out = eq->WaitForSmallTimer(ev, num, *timo);
+ if (timo == nullptr) {
+ // When the timeout is nullptr, we wait indefinitely
+ *out = eq->WaitForEvents(ev, num, 0);
+ } else if (*timo == 0) {
+ // Only events that have already arrived at the time of this function call can be received
+ *out = eq->GetTriggeredEvents(ev, num);
} else {
- if (timo == nullptr) { // wait until an event arrives without timing out
- *out = eq->WaitForEvents(ev, num, 0);
- }
-
- if (timo != nullptr) {
- // Only events that have already arrived at the time of this function call can be
- // received
- if (*timo == 0) {
- *out = eq->GetTriggeredEvents(ev, num);
- } else {
- // Wait until an event arrives with timing out
- *out = eq->WaitForEvents(ev, num, *timo);
- }
- }
+ // Wait for up to the specified timeout value
+ *out = eq->WaitForEvents(ev, num, *timo);
}
if (*out == 0) {
@@ -243,6 +298,14 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
return ORBIS_OK;
}
+static void HrTimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) {
+ static EqueueEvent event;
+ event.event = kevent;
+ event.event.data = HrTimerSpinlockThresholdUs;
+ eq->AddSmallTimer(event);
+ eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::HrTimer, kevent.udata);
+}
+
s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata) {
if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
@@ -269,21 +332,19 @@ s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec*
// `HrTimerSpinlockThresholdUs`) and fall back to boost asio timers if the time to tick is
// large. Even for large delays, we truncate a small portion to complete the wait
// using the spinlock, prioritizing precision.
+
+ if (eq->EventExists(event.event.ident, event.event.filter)) {
+ eq->RemoveEvent(id, SceKernelEvent::Filter::HrTimer);
+ }
+
if (total_us < HrTimerSpinlockThresholdUs) {
return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
}
- event.timer = std::make_unique(
- io_context, std::chrono::microseconds(total_us - HrTimerSpinlockThresholdUs));
-
- event.timer->async_wait(std::bind(SmallTimerCallback, std::placeholders::_1, eq, event.event));
-
- if (!eq->AddEvent(event)) {
+ if (!eq->AddEvent(event) ||
+ !eq->ScheduleEvent(id, SceKernelEvent::Filter::HrTimer, HrTimerCallback)) {
return ORBIS_KERNEL_ERROR_ENOMEM;
}
-
- KernelSignalRequest();
-
return ORBIS_OK;
}
@@ -300,6 +361,57 @@ int PS4_SYSV_ABI sceKernelDeleteHRTimerEvent(SceKernelEqueue eq, int id) {
}
}
+static void TimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) {
+ if (eq->EventExists(kevent.ident, kevent.filter)) {
+ eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::Timer, kevent.udata);
+
+ if (!(kevent.flags & SceKernelEvent::Flags::OneShot)) {
+ // Reschedule the event for its next period.
+ eq->ScheduleEvent(kevent.ident, kevent.filter, TimerCallback);
+ }
+ }
+}
+
+int PS4_SYSV_ABI sceKernelAddTimerEvent(SceKernelEqueue eq, int id, SceKernelUseconds usec,
+ void* udata) {
+ if (eq == nullptr) {
+ return ORBIS_KERNEL_ERROR_EBADF;
+ }
+
+ EqueueEvent event{};
+ event.event.ident = static_cast(id);
+ event.event.filter = SceKernelEvent::Filter::Timer;
+ event.event.flags = SceKernelEvent::Flags::Add;
+ event.event.fflags = 0;
+ event.event.data = usec;
+ event.event.udata = udata;
+
+ if (eq->EventExists(event.event.ident, event.event.filter)) {
+ eq->RemoveEvent(id, SceKernelEvent::Filter::Timer);
+ LOG_DEBUG(Kernel_Event,
+ "Timer event already exists, removing it: queue name={}, queue id={}",
+ eq->GetName(), event.event.ident);
+ }
+
+ LOG_DEBUG(Kernel_Event, "Added timing event: queue name={}, queue id={}, usec={}, pointer={:x}",
+ eq->GetName(), event.event.ident, usec, reinterpret_cast(udata));
+
+ if (!eq->AddEvent(event) ||
+ !eq->ScheduleEvent(id, SceKernelEvent::Filter::Timer, TimerCallback)) {
+ return ORBIS_KERNEL_ERROR_ENOMEM;
+ }
+ return ORBIS_OK;
+}
+
+int PS4_SYSV_ABI sceKernelDeleteTimerEvent(SceKernelEqueue eq, int id) {
+ if (eq == nullptr) {
+ return ORBIS_KERNEL_ERROR_EBADF;
+ }
+
+ return eq->RemoveEvent(id, SceKernelEvent::Filter::Timer) ? ORBIS_OK
+ : ORBIS_KERNEL_ERROR_ENOENT;
+}
+
int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) {
if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
@@ -380,6 +492,8 @@ void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge);
LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent);
LIB_FUNCTION("J+LF6LwObXU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteHRTimerEvent);
+ LIB_FUNCTION("57ZK+ODEXWY", "libkernel", 1, "libkernel", 1, 1, sceKernelAddTimerEvent);
+ LIB_FUNCTION("YWQFUyXIVdU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteTimerEvent);
LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent);
LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent);
LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId);
diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h
index 2bd7ef510..fbc209265 100644
--- a/src/core/libraries/kernel/equeue.h
+++ b/src/core/libraries/kernel/equeue.h
@@ -9,6 +9,7 @@
#include
#include
+#include
#include "common/rdtsc.h"
#include "common/types.h"
@@ -21,6 +22,9 @@ namespace Libraries::Kernel {
class EqueueInternal;
struct EqueueEvent;
+using SceKernelUseconds = u32;
+using SceKernelEqueue = EqueueInternal*;
+
struct SceKernelEvent {
enum Filter : s16 {
None = 0,
@@ -77,6 +81,7 @@ struct EqueueEvent {
SceKernelEvent event;
void* data = nullptr;
std::chrono::steady_clock::time_point time_added;
+ std::chrono::microseconds timer_interval;
std::unique_ptr timer;
void ResetTriggerState() {
@@ -94,6 +99,12 @@ struct EqueueEvent {
event.data = reinterpret_cast(data);
}
+ void TriggerUser(void* data) {
+ is_triggered = true;
+ event.fflags++;
+ event.udata = data;
+ }
+
void TriggerDisplay(void* data) {
is_triggered = true;
if (data != nullptr) {
@@ -125,6 +136,12 @@ private:
};
class EqueueInternal {
+ struct SmallTimer {
+ SceKernelEvent event;
+ std::chrono::steady_clock::time_point added;
+ std::chrono::microseconds interval;
+ };
+
public:
explicit EqueueInternal(std::string_view name) : m_name(name) {}
@@ -133,36 +150,38 @@ public:
}
bool AddEvent(EqueueEvent& event);
+ bool ScheduleEvent(u64 id, s16 filter,
+ void (*callback)(SceKernelEqueue, const SceKernelEvent&));
bool RemoveEvent(u64 id, s16 filter);
int WaitForEvents(SceKernelEvent* ev, int num, u32 micros);
bool TriggerEvent(u64 ident, s16 filter, void* trigger_data);
int GetTriggeredEvents(SceKernelEvent* ev, int num);
bool AddSmallTimer(EqueueEvent& event);
- bool HasSmallTimer() const {
- return small_timer_event.event.data != 0;
+ bool HasSmallTimer() {
+ std::scoped_lock lock{m_mutex};
+ return !m_small_timers.empty();
}
bool RemoveSmallTimer(u64 id) {
- if (HasSmallTimer() && small_timer_event.event.ident == id) {
- small_timer_event = {};
- return true;
+ if (HasSmallTimer()) {
+ std::scoped_lock lock{m_mutex};
+ return m_small_timers.erase(id) > 0;
}
return false;
}
int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros);
+ bool EventExists(u64 id, s16 filter);
+
private:
std::string m_name;
std::mutex m_mutex;
std::vector m_events;
- EqueueEvent small_timer_event{};
std::condition_variable m_cond;
+ std::unordered_map m_small_timers;
};
-using SceKernelUseconds = u32;
-using SceKernelEqueue = EqueueInternal*;
-
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev);
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym);
diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp
index ad372325c..fecc606fd 100644
--- a/src/core/libraries/kernel/file_system.cpp
+++ b/src/core/libraries/kernel/file_system.cpp
@@ -1050,6 +1050,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("kAt6VDbHmro", "libkernel", 1, "libkernel", 1, 1, sceKernelWritev);
LIB_FUNCTION("Oy6IpwgtYOk", "libScePosix", 1, "libkernel", 1, 1, posix_lseek);
LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek);
LIB_FUNCTION("oib76F-12fk", "libkernel", 1, "libkernel", 1, 1, sceKernelLseek);
diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp
index 959a8605a..930640d0e 100644
--- a/src/core/libraries/kernel/kernel.cpp
+++ b/src/core/libraries/kernel/kernel.cpp
@@ -28,8 +28,12 @@
#ifdef _WIN64
#include
+#else
+#include
#endif
#include
+#include
+#include
#include "aio.h"
namespace Libraries::Kernel {
@@ -104,6 +108,9 @@ void SetPosixErrno(int e) {
case EACCES:
g_posix_errno = POSIX_EACCES;
break;
+ case EFAULT:
+ g_posix_errno = POSIX_EFAULT;
+ break;
case EINVAL:
g_posix_errno = POSIX_EINVAL;
break;
@@ -150,23 +157,23 @@ struct OrbisKernelUuid {
u8 clockSeqLow;
u8 node[6];
};
+static_assert(sizeof(OrbisKernelUuid) == 0x10);
int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
+ if (!orbisUuid) {
+ return ORBIS_KERNEL_ERROR_EINVAL;
+ }
#ifdef _WIN64
UUID uuid;
- UuidCreate(&uuid);
- orbisUuid->timeLow = uuid.Data1;
- orbisUuid->timeMid = uuid.Data2;
- orbisUuid->timeHiAndVersion = uuid.Data3;
- orbisUuid->clockSeqHiAndReserved = uuid.Data4[0];
- orbisUuid->clockSeqLow = uuid.Data4[1];
- for (int i = 0; i < 6; i++) {
- orbisUuid->node[i] = uuid.Data4[2 + i];
+ if (UuidCreate(&uuid) != RPC_S_OK) {
+ return ORBIS_KERNEL_ERROR_EFAULT;
}
#else
- LOG_ERROR(Kernel, "sceKernelUuidCreate: Add linux");
+ uuid_t uuid;
+ uuid_generate(uuid);
#endif
- return 0;
+ std::memcpy(orbisUuid, &uuid, sizeof(OrbisKernelUuid));
+ return ORBIS_OK;
}
int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) {
@@ -205,6 +212,24 @@ int PS4_SYSV_ABI posix_getpagesize() {
return 16_KB;
}
+int PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s,
+ Libraries::Net::OrbisNetSockaddr* addr, u32* paddrlen) {
+ auto* netcall = Common::Singleton::Instance();
+ auto sock = netcall->FindSocket(s);
+ if (!sock) {
+ *Libraries::Kernel::__Error() = ORBIS_NET_ERROR_EBADF;
+ LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
+ return -1;
+ }
+ int returncode = sock->GetSocketAddress(addr, paddrlen);
+ if (returncode >= 0) {
+ LOG_ERROR(Lib_Net, "return code : {:#x}", (u32)returncode);
+ return 0;
+ }
+ *Libraries::Kernel::__Error() = 0x20;
+ LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
+ return -1;
+}
void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
service_thread = std::jthread{KernelServiceThread};
@@ -242,13 +267,16 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("lUk6wrGXyMw", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_recvfrom);
LIB_FUNCTION("fFxGkxF2bVo", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sys_setsockopt);
- LIB_FUNCTION("RenI1lL1WFk", "libScePosix", 1, "libkernel", 1, 1,
- Libraries::Net::sys_getsockname);
+ // LIB_FUNCTION("RenI1lL1WFk", "libScePosix", 1, "libkernel", 1, 1, posix_getsockname);
LIB_FUNCTION("KuOmgKoqCdY", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_bind);
LIB_FUNCTION("5jRCs2axtr4", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ...
LIB_FUNCTION("4n51s0zEf0c", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sceNetInetPton); // TODO fix it to sys_ ...
+ LIB_FUNCTION("XVL8So3QJUk", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_connect);
+ LIB_FUNCTION("3e+4Iv7IJ8U", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_accept);
+ LIB_FUNCTION("aNeavPDNKzA", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_sendmsg);
+ LIB_FUNCTION("pxnCmagrtao", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_listen);
}
} // namespace Libraries::Kernel
diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h
index 4d68aa357..aaa22aec1 100644
--- a/src/core/libraries/kernel/kernel.h
+++ b/src/core/libraries/kernel/kernel.h
@@ -3,9 +3,6 @@
#pragma once
-#include
-#include
-#include "common/string_literal.h"
#include "common/types.h"
#include "core/libraries/kernel/orbis_error.h"
@@ -20,26 +17,21 @@ int ErrnoToSceKernelError(int e);
void SetPosixErrno(int e);
int* PS4_SYSV_ABI __Error();
-template
-struct WrapperImpl;
+template
+struct OrbisWrapperImpl;
-template
-struct WrapperImpl {
- static constexpr StringLiteral Name{name};
+template
+struct OrbisWrapperImpl {
static R PS4_SYSV_ABI wrap(Args... args) {
u32 ret = f(args...);
if (ret != 0) {
- // LOG_ERROR(Lib_Kernel, "Function {} returned {}", std::string_view{name.value}, ret);
ret += ORBIS_KERNEL_ERROR_UNKNOWN;
}
return ret;
}
};
-template
-constexpr auto OrbisWrapper = WrapperImpl::wrap;
-
-#define ORBIS(func) WrapperImpl<#func, decltype(&func), func>::wrap
+#define ORBIS(func) (Libraries::Kernel::OrbisWrapperImpl::wrap)
int* PS4_SYSV_ABI __Error();
diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp
index 495ddc52f..ea3998ddd 100644
--- a/src/core/libraries/kernel/memory.cpp
+++ b/src/core/libraries/kernel/memory.cpp
@@ -8,7 +8,6 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/singleton.h"
-#include "core/file_sys/fs.h"
#include "core/libraries/kernel/kernel.h"
#include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/orbis_error.h"
@@ -100,8 +99,8 @@ s32 PS4_SYSV_ABI sceKernelReleaseDirectMemory(u64 start, size_t len) {
s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchEnd,
size_t alignment, u64* physAddrOut,
size_t* sizeOut) {
- LOG_WARNING(Kernel_Vmm, "called searchStart = {:#x}, searchEnd = {:#x}, alignment = {:#x}",
- searchStart, searchEnd, alignment);
+ LOG_INFO(Kernel_Vmm, "called searchStart = {:#x}, searchEnd = {:#x}, alignment = {:#x}",
+ searchStart, searchEnd, alignment);
if (physAddrOut == nullptr || sizeOut == nullptr) {
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -152,7 +151,8 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u
const VAddr in_addr = reinterpret_cast(*addr);
const auto map_flags = static_cast(flags);
- s32 result = memory->Reserve(addr, in_addr, len, map_flags, alignment);
+ s32 result = memory->MapMemory(addr, in_addr, len, Core::MemoryProt::NoAccess, map_flags,
+ Core::VMAType::Reserved, "anon", false, -1, alignment);
if (result == 0) {
LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr));
}
@@ -209,9 +209,23 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
"anon");
}
-s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
- int flags, const char* name) {
+s32 PS4_SYSV_ABI sceKernelMapDirectMemory2(void** addr, u64 len, s32 type, s32 prot, s32 flags,
+ s64 phys_addr, u64 alignment) {
+ LOG_INFO(Kernel_Vmm, "called, redirected to sceKernelMapNamedDirectMemory");
+ const s32 ret =
+ sceKernelMapNamedDirectMemory(addr, len, prot, flags, phys_addr, alignment, "anon");
+ if (ret == 0) {
+ auto* memory = Core::Memory::Instance();
+ memory->SetDirectMemoryType(phys_addr, type);
+ }
+ return ret;
+}
+
+s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags,
+ const char* name) {
+ LOG_INFO(Kernel_Vmm, "in_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, name = '{}'",
+ fmt::ptr(*addr_in_out), len, prot, flags, name);
if (len == 0 || !Common::Is16KBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "len is 0 or not 16kb multiple");
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -230,18 +244,14 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t
const VAddr in_addr = reinterpret_cast(*addr_in_out);
const auto mem_prot = static_cast(prot);
const auto map_flags = static_cast(flags);
- SCOPE_EXIT {
- LOG_INFO(Kernel_Vmm,
- "in_addr = {:#x}, out_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}",
- in_addr, fmt::ptr(*addr_in_out), len, prot, flags);
- };
auto* memory = Core::Memory::Instance();
- return memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
- Core::VMAType::Flexible, name);
+ const auto ret = memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
+ Core::VMAType::Flexible, name);
+ LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr_in_out));
+ return ret;
}
-s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
- int flags) {
+s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags) {
return sceKernelMapNamedFlexibleMemory(addr_in_out, len, prot, flags, "anon");
}
@@ -250,13 +260,26 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void**
return memory->QueryProtection(std::bit_cast(addr), start, end, prot);
}
-int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot) {
+s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) {
+ LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
+ prot);
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast(prot);
return memory_manager->Protect(std::bit_cast(addr), size, protection_flags);
}
-int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot) {
+s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) {
+ s32 result = sceKernelMprotect(addr, size, prot);
+ if (result < 0) {
+ ErrSceToPosix(result);
+ return -1;
+ }
+ return result;
+}
+
+s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot) {
+ LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
+ prot);
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast(prot);
return memory_manager->Protect(std::bit_cast(addr), size, protection_flags);
@@ -264,7 +287,7 @@ int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype,
int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info,
size_t infoSize) {
- LOG_WARNING(Kernel_Vmm, "called offset = {:#x}, flags = {:#x}", offset, flags);
+ LOG_INFO(Kernel_Vmm, "called offset = {:#x}, flags = {:#x}", offset, flags);
auto* memory = Core::Memory::Instance();
return memory->DirectMemoryQuery(offset, flags == 1, query_info);
}
@@ -290,6 +313,12 @@ int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut
directMemoryEndOut);
}
+int PS4_SYSV_ABI sceKernelIsStack(void* addr, void** start, void** end) {
+ LOG_DEBUG(Kernel_Vmm, "called, addr = {}", fmt::ptr(addr));
+ auto* memory = Core::Memory::Instance();
+ return memory->IsStack(std::bit_cast(addr), start, end);
+}
+
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut) {
return sceKernelBatchMap2(entries, numEntries, numEntriesOut,
@@ -325,7 +354,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
break;
}
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_PROTECT: {
- result = sceKernelMProtect(entries[i].start, entries[i].length, entries[i].protection);
+ result = sceKernelMprotect(entries[i].start, entries[i].length, entries[i].protection);
LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
entries[i].operation, entries[i].length, result);
break;
@@ -340,7 +369,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
break;
}
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT: {
- result = sceKernelMTypeProtect(entries[i].start, entries[i].length, entries[i].type,
+ result = sceKernelMtypeprotect(entries[i].start, entries[i].length, entries[i].type,
entries[i].protection);
LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
entries[i].operation, entries[i].length, result);
@@ -361,7 +390,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
return result;
}
-s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name) {
+s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, u64 len, const char* name) {
if (name == nullptr) {
LOG_ERROR(Kernel_Vmm, "name is invalid!");
return ORBIS_KERNEL_ERROR_EFAULT;
@@ -377,19 +406,18 @@ s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, cons
return ORBIS_OK;
}
-s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len,
- size_t alignment, u64* physAddrOut) {
+s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
+ u64* physAddrOut) {
if (searchStart < 0 || searchEnd <= searchStart) {
LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL;
}
- const bool is_in_range = searchEnd - searchStart >= len;
- if (len <= 0 || !Common::Is64KBAligned(len) || !is_in_range) {
- LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!");
+ if (len <= 0 || !Common::Is64KBAligned(len)) {
+ LOG_ERROR(Kernel_Vmm, "Provided length {:#x} is invalid!", len);
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (alignment != 0 && !Common::Is64KBAligned(alignment)) {
- LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!");
+ LOG_ERROR(Kernel_Vmm, "Alignment {:#x} is invalid!", alignment);
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (physAddrOut == nullptr) {
@@ -397,8 +425,21 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_
return ORBIS_KERNEL_ERROR_EINVAL;
}
+ const bool is_in_range = searchEnd - searchStart >= len;
+ if (searchEnd <= searchStart || searchEnd < len || !is_in_range) {
+ LOG_ERROR(Kernel_Vmm,
+ "Provided address range is too small!"
+ " searchStart = {:#x}, searchEnd = {:#x}, length = {:#x}",
+ searchStart, searchEnd, len);
+ return ORBIS_KERNEL_ERROR_ENOMEM;
+ }
+
auto* memory = Core::Memory::Instance();
PAddr phys_addr = memory->PoolExpand(searchStart, searchEnd, len, alignment);
+ if (phys_addr == -1) {
+ return ORBIS_KERNEL_ERROR_ENOMEM;
+ }
+
*physAddrOut = static_cast(phys_addr);
LOG_INFO(Kernel_Vmm,
@@ -408,15 +449,11 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_
return ORBIS_OK;
}
-s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags,
- void** addrOut) {
- LOG_INFO(Kernel_Vmm, "addrIn = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}",
- fmt::ptr(addrIn), len, alignment, flags);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags,
+ void** addr_out) {
+ LOG_INFO(Kernel_Vmm, "addr_in = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}",
+ fmt::ptr(addr_in), len, alignment, flags);
- if (addrIn == nullptr) {
- LOG_ERROR(Kernel_Vmm, "Address is invalid!");
- return ORBIS_KERNEL_ERROR_EINVAL;
- }
if (len == 0 || !Common::Is2MBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!");
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -429,14 +466,16 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali
}
auto* memory = Core::Memory::Instance();
- const VAddr in_addr = reinterpret_cast(addrIn);
+ const VAddr in_addr = reinterpret_cast(addr_in);
const auto map_flags = static_cast(flags);
- memory->PoolReserve(addrOut, in_addr, len, map_flags, alignment);
+ u64 map_alignment = alignment == 0 ? 2_MB : alignment;
- return ORBIS_OK;
+ return memory->MapMemory(addr_out, std::bit_cast(addr_in), len,
+ Core::MemoryProt::NoAccess, map_flags, Core::VMAType::PoolReserved,
+ "anon", false, -1, map_alignment);
}
-s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags) {
+s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags) {
if (addr == nullptr) {
LOG_ERROR(Kernel_Vmm, "Address is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -455,7 +494,7 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int
return memory->PoolCommit(in_addr, len, mem_prot);
}
-s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags) {
+s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags) {
if (addr == nullptr) {
LOG_ERROR(Kernel_Vmm, "Address is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -469,35 +508,105 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags)
const VAddr pool_addr = reinterpret_cast(addr);
auto* memory = Core::Memory::Instance();
- memory->PoolDecommit(pool_addr, len);
- return ORBIS_OK;
+ return memory->PoolDecommit(pool_addr, len);
}
-int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset,
- void** res) {
- LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}",
- fmt::ptr(addr), len, prot, flags, fd, offset);
- auto* h = Common::Singleton::Instance();
+s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count,
+ s32* num_processed, s32 flags) {
+ if (entries == nullptr) {
+ return ORBIS_KERNEL_ERROR_EINVAL;
+ }
+ s32 result = ORBIS_OK;
+ s32 processed = 0;
+
+ for (s32 i = 0; i < count; i++, processed++) {
+ OrbisKernelMemoryPoolBatchEntry entry = entries[i];
+ switch (entry.opcode) {
+ case OrbisKernelMemoryPoolOpcode::Commit: {
+ result = sceKernelMemoryPoolCommit(entry.commit_params.addr, entry.commit_params.len,
+ entry.commit_params.type, entry.commit_params.prot,
+ entry.flags);
+ break;
+ }
+ case OrbisKernelMemoryPoolOpcode::Decommit: {
+ result = sceKernelMemoryPoolDecommit(entry.decommit_params.addr,
+ entry.decommit_params.len, entry.flags);
+ break;
+ }
+ case OrbisKernelMemoryPoolOpcode::Protect: {
+ result = sceKernelMprotect(entry.protect_params.addr, entry.protect_params.len,
+ entry.protect_params.prot);
+ break;
+ }
+ case OrbisKernelMemoryPoolOpcode::TypeProtect: {
+ result = sceKernelMtypeprotect(
+ entry.type_protect_params.addr, entry.type_protect_params.len,
+ entry.type_protect_params.type, entry.type_protect_params.prot);
+ break;
+ }
+ case OrbisKernelMemoryPoolOpcode::Move: {
+ UNREACHABLE_MSG("Unimplemented sceKernelMemoryPoolBatch opcode Move");
+ }
+ default: {
+ result = ORBIS_KERNEL_ERROR_EINVAL;
+ break;
+ }
+ }
+
+ if (result != ORBIS_OK) {
+ break;
+ }
+ }
+
+ if (num_processed != nullptr) {
+ *num_processed = processed;
+ }
+ return result;
+}
+
+void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr) {
+ LOG_INFO(Kernel_Vmm,
+ "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, phys_addr = {}",
+ fmt::ptr(addr), len, prot, flags, fd, phys_addr);
+
+ void* addr_out;
auto* memory = Core::Memory::Instance();
const auto mem_prot = static_cast(prot);
const auto mem_flags = static_cast(flags);
+
+ s32 result = ORBIS_OK;
if (fd == -1) {
- return memory->MapMemory(res, std::bit_cast(addr), len, mem_prot, mem_flags,
- Core::VMAType::Flexible);
+ result = memory->MapMemory(&addr_out, std::bit_cast(addr), len, mem_prot, mem_flags,
+ Core::VMAType::Flexible);
} else {
- const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping();
- return memory->MapFile(res, std::bit_cast(addr), len, mem_prot, mem_flags, handle,
- offset);
+ result = memory->MapFile(&addr_out, std::bit_cast(addr), len, mem_prot, mem_flags,
+ fd, phys_addr);
}
+
+ if (result != ORBIS_OK) {
+ // If the memory mappings fail, mmap sets errno to the appropriate error code,
+ // then returns (void*)-1;
+ ErrSceToPosix(result);
+ return reinterpret_cast(-1);
+ }
+
+ return addr_out;
}
-void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) {
- void* ptr;
- LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap");
- int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr);
- ASSERT(result == 0);
- return ptr;
+s32 PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr,
+ void** res) {
+ void* addr_out = posix_mmap(addr, len, prot, flags, fd, phys_addr);
+
+ if (addr_out == reinterpret_cast(-1)) {
+ // posix_mmap failed, calculate and return the appropriate kernel error code using errno.
+ LOG_ERROR(Kernel_Fs, "error = {}", *__Error());
+ return ErrnoToSceKernelError(*__Error());
+ }
+
+ // Set the outputted address
+ *res = addr_out;
+ return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) {
@@ -551,6 +660,9 @@ int PS4_SYSV_ABI sceKernelSetPrtAperture(int id, VAddr address, size_t size) {
"PRT aperture id = {}, address = {:#x}, size = {:#x} is set but not used", id,
address, size);
+ auto* memory = Core::Memory::Instance();
+ memory->SetPrtArea(id, address, size);
+
PrtApertures[id] = {address, size};
return ORBIS_OK;
}
@@ -576,8 +688,10 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange);
LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType);
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize);
+ LIB_FUNCTION("yDBwVAolDgg", "libkernel", 1, "libkernel", 1, 1, sceKernelIsStack);
LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory);
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory);
+ LIB_FUNCTION("BQQniolj9tQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory2);
LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection);
LIB_FUNCTION("BHouLQzh0X0", "libkernel", 1, "libkernel", 1, 1, sceKernelDirectMemoryQuery);
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
@@ -597,14 +711,16 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1,
sceKernelConfiguredFlexibleMemorySize);
- LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect);
- LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect);
+ LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMprotect);
+ LIB_FUNCTION("YQOfxL4QfeU", "libScePosix", 1, "libkernel", 1, 1, posix_mprotect);
+ LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMtypeprotect);
// Memory pool
LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand);
LIB_FUNCTION("pU-QydtGcGY", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolReserve);
LIB_FUNCTION("Vzl66WmfLvk", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolCommit);
LIB_FUNCTION("LXo1tpFqJGs", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolDecommit);
+ LIB_FUNCTION("YN878uKRBbE", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolBatch);
LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap);
LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap);
diff --git a/src/core/libraries/kernel/memory.h b/src/core/libraries/kernel/memory.h
index 6acb559d1..ea42e7546 100644
--- a/src/core/libraries/kernel/memory.h
+++ b/src/core/libraries/kernel/memory.h
@@ -61,13 +61,15 @@ struct OrbisVirtualQueryInfo {
size_t offset;
s32 protection;
s32 memory_type;
- u32 is_flexible : 1;
- u32 is_direct : 1;
- u32 is_stack : 1;
- u32 is_pooled : 1;
- u32 is_committed : 1;
+ u8 is_flexible : 1;
+ u8 is_direct : 1;
+ u8 is_stack : 1;
+ u8 is_pooled : 1;
+ u8 is_committed : 1;
char name[ORBIS_KERNEL_MAXIMUM_NAME_LENGTH];
};
+static_assert(sizeof(OrbisVirtualQueryInfo) == 72,
+ "OrbisVirtualQueryInfo struct size is incorrect");
struct OrbisKernelBatchMapEntry {
void* start;
@@ -79,6 +81,48 @@ struct OrbisKernelBatchMapEntry {
int operation;
};
+enum class OrbisKernelMemoryPoolOpcode : u32 {
+ Commit = 1,
+ Decommit = 2,
+ Protect = 3,
+ TypeProtect = 4,
+ Move = 5,
+};
+
+struct OrbisKernelMemoryPoolBatchEntry {
+ OrbisKernelMemoryPoolOpcode opcode;
+ u32 flags;
+ union {
+ struct {
+ void* addr;
+ u64 len;
+ u8 prot;
+ u8 type;
+ } commit_params;
+ struct {
+ void* addr;
+ u64 len;
+ } decommit_params;
+ struct {
+ void* addr;
+ u64 len;
+ u8 prot;
+ } protect_params;
+ struct {
+ void* addr;
+ u64 len;
+ u8 prot;
+ u8 type;
+ } type_protect_params;
+ struct {
+ void* dest_addr;
+ void* src_addr;
+ u64 len;
+ } move_params;
+ uintptr_t padding[3];
+ };
+};
+
u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize();
int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len,
u64 alignment, int memoryType, s64* physAddrOut);
@@ -97,15 +141,14 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE
s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
size_t infoSize);
s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u64 alignment);
-s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addrInOut, std::size_t len, int prot,
- int flags, const char* name);
-s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
- int flags);
+s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags,
+ const char* name);
+s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags);
int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot);
-int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot);
+s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot);
-int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot);
+s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot);
int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info,
size_t infoSize);
@@ -114,20 +157,23 @@ void PS4_SYSV_ABI _sceKernelRtldSetApplicationHeapAPI(void* func[]);
int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut,
void** directMemoryStartOut,
void** directMemoryEndOut);
+int PS4_SYSV_ABI sceKernelIsStack(void* addr, void** start, void** end);
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut);
s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut, int flags);
-s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name);
+s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, u64 len, const char* name);
-s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len,
- size_t alignment, u64* physAddrOut);
-s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags,
- void** addrOut);
-s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags);
-s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
+ u64* physAddrOut);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags,
+ void** addr_out);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags);
+s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count,
+ s32* num_processed, s32 flags);
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len);
diff --git a/src/core/libraries/kernel/threads.h b/src/core/libraries/kernel/threads.h
index 409136968..bcccf1695 100644
--- a/src/core/libraries/kernel/threads.h
+++ b/src/core/libraries/kernel/threads.h
@@ -17,6 +17,12 @@ int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr);
int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr);
+int PS4_SYSV_ABI posix_pthread_attr_getaffinity_np(const PthreadAttrT* pattr, size_t cpusetsize,
+ Cpuset* cpusetp);
+
+int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t cpusetsize,
+ const Cpuset* cpusetp);
+
int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr,
PthreadEntryFunc start_routine, void* arg);
@@ -35,7 +41,7 @@ public:
this->func = std::move(func);
PthreadAttrT attr{};
posix_pthread_attr_init(&attr);
- posix_pthread_create(&thread, &attr, RunWrapper, this);
+ posix_pthread_create(&thread, &attr, HOST_CALL(RunWrapper), this);
posix_pthread_attr_destroy(&attr);
}
diff --git a/src/core/libraries/kernel/threads/event_flag.cpp b/src/core/libraries/kernel/threads/event_flag.cpp
index 24ddcb927..91b17bd49 100644
--- a/src/core/libraries/kernel/threads/event_flag.cpp
+++ b/src/core/libraries/kernel/threads/event_flag.cpp
@@ -315,7 +315,7 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
auto result = ef->Poll(bitPattern, wait, clear, pResultPat);
if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_EBUSY) {
- LOG_ERROR(Kernel_Event, "returned {}", result);
+ LOG_DEBUG(Kernel_Event, "returned {:#x}", result);
}
return result;
@@ -361,7 +361,7 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
u32 result = ef->Wait(bitPattern, wait, clear, pResultPat, pTimeout);
if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_ETIMEDOUT) {
- LOG_ERROR(Kernel_Event, "returned {:#x}", result);
+ LOG_DEBUG(Kernel_Event, "returned {:#x}", result);
}
return result;
diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp
index 956e5ef65..3dbade96a 100644
--- a/src/core/libraries/kernel/threads/mutex.cpp
+++ b/src/core/libraries/kernel/threads/mutex.cpp
@@ -426,6 +426,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
// Posix
LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init);
LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock);
+ LIB_FUNCTION("Io9+nTKXZtA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_timedlock);
LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock);
LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy);
LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init);
diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp
index e791e74bf..59b427d22 100644
--- a/src/core/libraries/kernel/threads/pthread.cpp
+++ b/src/core/libraries/kernel/threads/pthread.cpp
@@ -6,6 +6,7 @@
#include "core/debug_state.h"
#include "core/libraries/kernel/kernel.h"
#include "core/libraries/kernel/posix_error.h"
+#include "core/libraries/kernel/threads.h"
#include "core/libraries/kernel/threads/pthread.h"
#include "core/libraries/kernel/threads/thread_state.h"
#include "core/libraries/libs.h"
@@ -535,8 +536,6 @@ int Pthread::SetAffinity(const Cpuset* cpuset) {
return POSIX_EINVAL;
}
- u64 mask = cpuset->bits;
-
uintptr_t handle = native_thr.GetHandle();
if (handle == 0) {
return POSIX_ESRCH;
@@ -545,6 +544,7 @@ int Pthread::SetAffinity(const Cpuset* cpuset) {
// We don't use this currently because some games gets performance problems
// when applying affinity even on strong hardware
/*
+ u64 mask = cpuset->bits;
#ifdef _WIN64
DWORD_PTR affinity_mask = static_cast(mask);
if (!SetThreadAffinityMask(reinterpret_cast(handle), affinity_mask)) {
@@ -572,21 +572,61 @@ int Pthread::SetAffinity(const Cpuset* cpuset) {
return 0;
}
+int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize, Cpuset* cpusetp) {
+ if (thread == nullptr || cpusetp == nullptr) {
+ return POSIX_EINVAL;
+ }
+
+ auto* thread_state = ThrState::Instance();
+ if (thread == g_curthread) {
+ g_curthread->lock.lock();
+ } else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) {
+ return ret;
+ }
+
+ auto* attr_ptr = &thread->attr;
+ auto ret = posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp);
+
+ thread->lock.unlock();
+ return ret;
+}
+
int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize,
const Cpuset* cpusetp) {
if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL;
}
- thread->attr.cpusetsize = cpusetsize;
- return thread->SetAffinity(cpusetp);
+
+ auto* thread_state = ThrState::Instance();
+ if (thread == g_curthread) {
+ g_curthread->lock.lock();
+ } else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) {
+ return ret;
+ }
+
+ auto* attr_ptr = &thread->attr;
+ auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp);
+
+ if (ret == ORBIS_OK) {
+ ret = thread->SetAffinity(thread->attr.cpuset);
+ }
+
+ thread->lock.unlock();
+ return ret;
}
-int PS4_SYSV_ABI scePthreadSetaffinity(PthreadT thread, const Cpuset mask) {
- int result = posix_pthread_setaffinity_np(thread, 0x10, &mask);
- if (result != 0) {
- return ErrnoToSceKernelError(result);
+int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) {
+ Cpuset cpuset;
+ const int ret = posix_pthread_getaffinity_np(thread, sizeof(Cpuset), &cpuset);
+ if (ret == 0) {
+ *mask = cpuset.bits;
}
- return 0;
+ return ret;
+}
+
+int PS4_SYSV_ABI scePthreadSetaffinity(PthreadT thread, const u64 mask) {
+ const Cpuset cpuset = {.bits = mask};
+ return posix_pthread_setaffinity_np(thread, sizeof(Cpuset), &cpuset);
}
void RegisterThread(Core::Loader::SymbolsResolver* sym) {
@@ -612,6 +652,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_once);
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create);
+ LIB_FUNCTION("Jb2uGFMr688", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getaffinity_np);
LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_setaffinity_np);
// Orbis
@@ -635,7 +676,8 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_setprio));
LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors);
LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield);
- LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity)
+ LIB_FUNCTION("rcrVFJsQWRY", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadGetaffinity));
+ LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadSetaffinity));
}
} // namespace Libraries::Kernel
diff --git a/src/core/libraries/kernel/threads/pthread.h b/src/core/libraries/kernel/threads/pthread.h
index 09eed11b8..ebcc4aed3 100644
--- a/src/core/libraries/kernel/threads/pthread.h
+++ b/src/core/libraries/kernel/threads/pthread.h
@@ -159,6 +159,7 @@ enum class SchedPolicy : u32 {
struct Cpuset {
u64 bits;
+ u64 _reserved;
};
struct PthreadAttr {
@@ -269,7 +270,7 @@ struct Pthread {
bool no_cancel;
bool cancel_async;
bool cancelling;
- Cpuset sigmask;
+ u64 sigmask;
bool unblock_sigcancel;
bool in_sigsuspend;
bool force_exit;
diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp
index a8e60ccf8..e098b00a4 100644
--- a/src/core/libraries/kernel/threads/pthread_attr.cpp
+++ b/src/core/libraries/kernel/threads/pthread_attr.cpp
@@ -243,7 +243,7 @@ int PS4_SYSV_ABI posix_pthread_attr_getaffinity_np(const PthreadAttrT* pattr, si
if (attr->cpuset != nullptr)
memcpy(cpusetp, attr->cpuset, std::min(cpusetsize, attr->cpusetsize));
else
- memset(cpusetp, -1, sizeof(Cpuset));
+ memset(cpusetp, -1, cpusetsize);
return 0;
}
@@ -259,30 +259,31 @@ int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t c
if (cpusetsize == 0 || cpusetp == nullptr) {
if (attr->cpuset != nullptr) {
free(attr->cpuset);
- attr->cpuset = NULL;
+ attr->cpuset = nullptr;
attr->cpusetsize = 0;
}
return 0;
}
if (attr->cpuset == nullptr) {
- attr->cpuset = (Cpuset*)calloc(1, sizeof(Cpuset));
+ attr->cpuset = static_cast(calloc(1, sizeof(Cpuset)));
attr->cpusetsize = sizeof(Cpuset);
}
- memcpy(attr->cpuset, cpusetp, sizeof(Cpuset));
+ memcpy(attr->cpuset, cpusetp, std::min(cpusetsize, sizeof(Cpuset)));
return 0;
}
-int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* param_1, Cpuset* mask) {
+int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* attr, u64* mask) {
Cpuset cpuset;
- const int ret = posix_pthread_attr_getaffinity_np(param_1, 0x10, &cpuset);
+ const int ret = posix_pthread_attr_getaffinity_np(attr, sizeof(Cpuset), &cpuset);
if (ret == 0) {
- *mask = cpuset;
+ *mask = cpuset.bits;
}
return ret;
}
-int PS4_SYSV_ABI scePthreadAttrSetaffinity(PthreadAttrT* attr, const Cpuset mask) {
- return posix_pthread_attr_setaffinity_np(attr, 0x10, &mask);
+int PS4_SYSV_ABI scePthreadAttrSetaffinity(PthreadAttrT* attr, const u64 mask) {
+ const Cpuset cpuset = {.bits = mask};
+ return posix_pthread_attr_setaffinity_np(attr, sizeof(Cpuset), &cpuset);
}
void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
@@ -305,6 +306,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
posix_pthread_attr_getdetachstate);
LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_setguardsize);
+ LIB_FUNCTION("qlk9pSLsUmM", "libScePosix", 1, "libkernel", 1, 1,
+ posix_pthread_attr_getschedparam);
// Orbis
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1,
diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp
index b7e4c1756..2fe74d0a3 100644
--- a/src/core/libraries/kernel/time.cpp
+++ b/src/core/libraries/kernel/time.cpp
@@ -5,24 +5,23 @@
#include "common/assert.h"
#include "common/native_clock.h"
+#include "common/thread.h"
#include "core/libraries/kernel/kernel.h"
#include "core/libraries/kernel/orbis_error.h"
+#include "core/libraries/kernel/posix_error.h"
#include "core/libraries/kernel/time.h"
#include "core/libraries/libs.h"
#ifdef _WIN64
-#include
#include
-
#include "common/ntapi.h"
-
#else
#if __APPLE__
#include
#endif
+#include
#include
#include
-#include
#include
#endif
@@ -52,88 +51,116 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() {
return clock->GetUptime();
}
-int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
-#ifdef _WIN64
- const auto start_time = std::chrono::high_resolution_clock::now();
- auto total_wait_time = std::chrono::microseconds(microseconds);
+static s32 posix_nanosleep_impl(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp,
+ const bool interruptible) {
+ if (!rqtp || rqtp->tv_sec < 0 || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1'000'000'000) {
+ SetPosixErrno(EINVAL);
+ return -1;
+ }
+ const auto duration = std::chrono::nanoseconds(rqtp->tv_sec * 1'000'000'000 + rqtp->tv_nsec);
+ std::chrono::nanoseconds remain;
+ const auto uninterrupted = Common::AccurateSleep(duration, &remain, interruptible);
+ if (rmtp) {
+ rmtp->tv_sec = remain.count() / 1'000'000'000;
+ rmtp->tv_nsec = remain.count() % 1'000'000'000;
+ }
+ if (!uninterrupted) {
+ SetPosixErrno(EINTR);
+ return -1;
+ }
+ return 0;
+}
- while (total_wait_time.count() > 0) {
- auto wait_time = std::chrono::ceil(total_wait_time).count();
- u64 res = SleepEx(static_cast(wait_time), true);
- if (res == WAIT_IO_COMPLETION) {
- auto elapsedTime = std::chrono::high_resolution_clock::now() - start_time;
- auto elapsedMicroseconds =
- std::chrono::duration_cast(elapsedTime).count();
- total_wait_time = std::chrono::microseconds(microseconds - elapsedMicroseconds);
- } else {
- break;
- }
+s32 PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
+ return posix_nanosleep_impl(rqtp, rmtp, true);
+}
+
+s32 PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
+ if (const auto ret = posix_nanosleep_impl(rqtp, rmtp, false); ret < 0) {
+ return ErrnoToSceKernelError(*__Error());
+ }
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI posix_usleep(u32 microseconds) {
+ const OrbisKernelTimespec ts = {
+ .tv_sec = microseconds / 1'000'000,
+ .tv_nsec = (microseconds % 1'000'000) * 1'000,
+ };
+ return posix_nanosleep(&ts, nullptr);
+}
+
+s32 PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
+ const OrbisKernelTimespec ts = {
+ .tv_sec = microseconds / 1'000'000,
+ .tv_nsec = (microseconds % 1'000'000) * 1'000,
+ };
+ return sceKernelNanosleep(&ts, nullptr);
+}
+
+u32 PS4_SYSV_ABI posix_sleep(u32 seconds) {
+ const OrbisKernelTimespec ts = {
+ .tv_sec = seconds,
+ .tv_nsec = 0,
+ };
+ OrbisKernelTimespec rm;
+ if (const auto ret = posix_nanosleep(&ts, &rm); ret < 0) {
+ return *__Error() == POSIX_EINTR ? rm.tv_sec + (rm.tv_nsec == 0 ? 0 : 1) : seconds;
+ }
+ return 0;
+}
+
+s32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) {
+ return sceKernelUsleep(seconds * 1'000'000);
+}
+
+s32 PS4_SYSV_ABI posix_clock_gettime(u32 clock_id, OrbisKernelTimespec* ts) {
+ if (ts == nullptr) {
+ SetPosixErrno(EFAULT);
+ return -1;
}
- return 0;
-#else
- timespec start;
- timespec remain;
- start.tv_sec = microseconds / 1000000;
- start.tv_nsec = (microseconds % 1000000) * 1000;
- timespec* requested = &start;
- int ret = 0;
- do {
- ret = nanosleep(requested, &remain);
- requested = &remain;
- } while (ret != 0);
- return ret;
-#endif
-}
+ if (clock_id == ORBIS_CLOCK_PROCTIME) {
+ const auto us = sceKernelGetProcessTime();
+ ts->tv_sec = static_cast(us / 1'000'000);
+ ts->tv_nsec = static_cast((us % 1'000'000) * 1000);
+ return 0;
+ }
+ if (clock_id == ORBIS_CLOCK_EXT_NETWORK || clock_id == ORBIS_CLOCK_EXT_DEBUG_NETWORK ||
+ clock_id == ORBIS_CLOCK_EXT_AD_NETWORK || clock_id == ORBIS_CLOCK_EXT_RAW_NETWORK) {
+ LOG_ERROR(Lib_Kernel, "Unsupported clock type {}, using CLOCK_MONOTONIC", clock_id);
+ clock_id = ORBIS_CLOCK_MONOTONIC;
+ }
-int PS4_SYSV_ABI posix_usleep(u32 microseconds) {
- return sceKernelUsleep(microseconds);
-}
-
-u32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) {
- std::this_thread::sleep_for(std::chrono::seconds(seconds));
- return 0;
-}
-
-#ifdef _WIN64
-#ifndef CLOCK_REALTIME
-#define CLOCK_REALTIME 0
-#endif
-#ifndef CLOCK_MONOTONIC
-#define CLOCK_MONOTONIC 1
-#endif
-#ifndef CLOCK_PROCESS_CPUTIME_ID
-#define CLOCK_PROCESS_CPUTIME_ID 2
-#endif
-#ifndef CLOCK_THREAD_CPUTIME_ID
-#define CLOCK_THREAD_CPUTIME_ID 3
-#endif
-#ifndef CLOCK_REALTIME_COARSE
-#define CLOCK_REALTIME_COARSE 5
-#endif
-#ifndef CLOCK_MONOTONIC_COARSE
-#define CLOCK_MONOTONIC_COARSE 6
-#endif
-
-#define DELTA_EPOCH_IN_100NS 116444736000000000ULL
-
-static u64 FileTimeTo100Ns(FILETIME& ft) {
- return *reinterpret_cast(&ft);
-}
-
-static s32 clock_gettime(u32 clock_id, struct timespec* ts) {
+#ifdef _WIN32
+ static const auto FileTimeTo100Ns = [](FILETIME& ft) { return *reinterpret_cast(&ft); };
switch (clock_id) {
- case CLOCK_REALTIME:
- case CLOCK_REALTIME_COARSE: {
+ case ORBIS_CLOCK_REALTIME:
+ case ORBIS_CLOCK_REALTIME_PRECISE: {
FILETIME ft;
- GetSystemTimeAsFileTime(&ft);
- const u64 ns = FileTimeTo100Ns(ft) - DELTA_EPOCH_IN_100NS;
+ GetSystemTimePreciseAsFileTime(&ft);
+ static constexpr u64 DeltaEpochIn100ns = 116444736000000000ULL;
+ const u64 ns = FileTimeTo100Ns(ft) - DeltaEpochIn100ns;
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
return 0;
}
- case CLOCK_MONOTONIC:
- case CLOCK_MONOTONIC_COARSE: {
+ case ORBIS_CLOCK_SECOND:
+ case ORBIS_CLOCK_REALTIME_FAST: {
+ FILETIME ft;
+ GetSystemTimeAsFileTime(&ft);
+ static constexpr u64 DeltaEpochIn100ns = 116444736000000000ULL;
+ const u64 ns = FileTimeTo100Ns(ft) - DeltaEpochIn100ns;
+ ts->tv_sec = ns / 10'000'000;
+ ts->tv_nsec = (ns % 10'000'000) * 100;
+ return 0;
+ }
+ case ORBIS_CLOCK_UPTIME:
+ case ORBIS_CLOCK_UPTIME_PRECISE:
+ case ORBIS_CLOCK_MONOTONIC:
+ case ORBIS_CLOCK_MONOTONIC_PRECISE:
+ case ORBIS_CLOCK_UPTIME_FAST:
+ case ORBIS_CLOCK_MONOTONIC_FAST: {
static LARGE_INTEGER pf = [] {
LARGE_INTEGER res{};
QueryPerformanceFrequency(&pf);
@@ -141,43 +168,53 @@ static s32 clock_gettime(u32 clock_id, struct timespec* ts) {
}();
LARGE_INTEGER pc{};
- QueryPerformanceCounter(&pc);
+ if (!QueryPerformanceCounter(&pc)) {
+ SetPosixErrno(EFAULT);
+ return -1;
+ }
ts->tv_sec = pc.QuadPart / pf.QuadPart;
ts->tv_nsec = ((pc.QuadPart % pf.QuadPart) * 1000'000'000) / pf.QuadPart;
return 0;
}
- case CLOCK_PROCESS_CPUTIME_ID: {
+ case ORBIS_CLOCK_THREAD_CPUTIME_ID: {
FILETIME ct, et, kt, ut;
- if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
- return EFAULT;
+ if (!GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut)) {
+ SetPosixErrno(EFAULT);
+ return -1;
}
const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt);
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
return 0;
}
- case CLOCK_THREAD_CPUTIME_ID: {
+ case ORBIS_CLOCK_VIRTUAL: {
FILETIME ct, et, kt, ut;
- if (!GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut)) {
- return EFAULT;
+ if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
+ SetPosixErrno(EFAULT);
+ return -1;
}
- const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt);
+ const u64 ns = FileTimeTo100Ns(ut);
+ ts->tv_sec = ns / 10'000'000;
+ ts->tv_nsec = (ns % 10'000'000) * 100;
+ return 0;
+ }
+ case ORBIS_CLOCK_PROF: {
+ FILETIME ct, et, kt, ut;
+ if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
+ SetPosixErrno(EFAULT);
+ return -1;
+ }
+ const u64 ns = FileTimeTo100Ns(kt);
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
return 0;
}
default:
- return EINVAL;
+ SetPosixErrno(EFAULT);
+ return -1;
}
-}
-#endif
-
-int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* ts) {
- if (ts == nullptr) {
- return ORBIS_KERNEL_ERROR_EFAULT;
- }
-
- clockid_t pclock_id = CLOCK_MONOTONIC;
+#else
+ clockid_t pclock_id;
switch (clock_id) {
case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE:
@@ -185,7 +222,7 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
break;
case ORBIS_CLOCK_SECOND:
case ORBIS_CLOCK_REALTIME_FAST:
-#ifndef __APPLE__
+#ifdef CLOCK_REALTIME_COARSE
pclock_id = CLOCK_REALTIME_COARSE;
#else
pclock_id = CLOCK_REALTIME;
@@ -199,7 +236,7 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
break;
case ORBIS_CLOCK_UPTIME_FAST:
case ORBIS_CLOCK_MONOTONIC_FAST:
-#ifndef __APPLE__
+#ifdef CLOCK_MONOTONIC_COARSE
pclock_id = CLOCK_MONOTONIC_COARSE;
#else
pclock_id = CLOCK_MONOTONIC;
@@ -208,196 +245,226 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
case ORBIS_CLOCK_THREAD_CPUTIME_ID:
pclock_id = CLOCK_THREAD_CPUTIME_ID;
break;
- case ORBIS_CLOCK_PROCTIME: {
- const auto us = sceKernelGetProcessTime();
- ts->tv_sec = us / 1'000'000;
- ts->tv_nsec = (us % 1'000'000) * 1000;
- return 0;
- }
case ORBIS_CLOCK_VIRTUAL: {
-#ifdef _WIN64
- FILETIME ct, et, kt, ut;
- if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
- return EFAULT;
- }
- const u64 ns = FileTimeTo100Ns(ut);
- ts->tv_sec = ns / 10'000'000;
- ts->tv_nsec = (ns % 10'000'000) * 100;
-#else
- struct rusage ru;
+ rusage ru;
const auto res = getrusage(RUSAGE_SELF, &ru);
if (res < 0) {
- return res;
+ SetPosixErrno(EFAULT);
+ return -1;
}
ts->tv_sec = ru.ru_utime.tv_sec;
ts->tv_nsec = ru.ru_utime.tv_usec * 1000;
-#endif
return 0;
}
case ORBIS_CLOCK_PROF: {
-#ifdef _WIN64
- FILETIME ct, et, kt, ut;
- if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
- return EFAULT;
- }
- const u64 ns = FileTimeTo100Ns(kt);
- ts->tv_sec = ns / 10'000'000;
- ts->tv_nsec = (ns % 10'000'000) * 100;
-#else
- struct rusage ru;
+ rusage ru;
const auto res = getrusage(RUSAGE_SELF, &ru);
if (res < 0) {
- return res;
+ SetPosixErrno(EFAULT);
+ return -1;
}
ts->tv_sec = ru.ru_stime.tv_sec;
ts->tv_nsec = ru.ru_stime.tv_usec * 1000;
-#endif
return 0;
}
- case ORBIS_CLOCK_EXT_NETWORK:
- case ORBIS_CLOCK_EXT_DEBUG_NETWORK:
- case ORBIS_CLOCK_EXT_AD_NETWORK:
- case ORBIS_CLOCK_EXT_RAW_NETWORK:
- pclock_id = CLOCK_MONOTONIC;
- LOG_ERROR(Lib_Kernel, "unsupported = {} using CLOCK_MONOTONIC", clock_id);
- break;
default:
- return EINVAL;
+ SetPosixErrno(EFAULT);
+ return -1;
}
timespec t{};
- int result = clock_gettime(pclock_id, &t);
+ const auto result = clock_gettime(pclock_id, &t);
ts->tv_sec = t.tv_sec;
ts->tv_nsec = t.tv_nsec;
- return result;
-}
-
-int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) {
- const auto res = orbis_clock_gettime(clock_id, tp);
- if (res < 0) {
- return ErrnoToSceKernelError(res);
+ if (result < 0) {
+ SetPosixErrno(errno);
+ return -1;
}
- return ORBIS_OK;
-}
-
-int PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
- const auto* request = reinterpret_cast(rqtp);
- auto* remain = reinterpret_cast(rmtp);
- return nanosleep(request, remain);
-}
-
-int PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
- if (!rqtp || !rmtp) {
- return ORBIS_KERNEL_ERROR_EFAULT;
- }
-
- if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0) {
- return ORBIS_KERNEL_ERROR_EINVAL;
- }
-
- return posix_nanosleep(rqtp, rmtp);
-}
-
-int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
- if (!tp) {
- return ORBIS_KERNEL_ERROR_EFAULT;
- }
-
-#ifdef _WIN64
- FILETIME filetime;
- GetSystemTimePreciseAsFileTime(&filetime);
-
- constexpr u64 UNIX_TIME_START = 0x295E9648864000;
- constexpr u64 TICKS_PER_SECOND = 1000000;
-
- u64 ticks = filetime.dwHighDateTime;
- ticks <<= 32;
- ticks |= filetime.dwLowDateTime;
- ticks /= 10;
- ticks -= UNIX_TIME_START;
-
- tp->tv_sec = ticks / TICKS_PER_SECOND;
- tp->tv_usec = ticks % TICKS_PER_SECOND;
-#else
- timeval tv;
- gettimeofday(&tv, nullptr);
- tp->tv_sec = tv.tv_sec;
- tp->tv_usec = tv.tv_usec;
+ return 0;
#endif
+}
+
+s32 PS4_SYSV_ABI sceKernelClockGettime(const u32 clock_id, OrbisKernelTimespec* ts) {
+ if (const auto ret = posix_clock_gettime(clock_id, ts); ret < 0) {
+ return ErrnoToSceKernelError(*__Error());
+ }
return ORBIS_OK;
}
-int PS4_SYSV_ABI gettimeofday(OrbisKernelTimeval* tp, OrbisKernelTimezone* tz) {
- // FreeBSD docs mention that the kernel generally does not track these values
- // and they are usually returned as zero.
- if (tz) {
- tz->tz_minuteswest = 0;
- tz->tz_dsttime = 0;
- }
- return sceKernelGettimeofday(tp);
-}
-
-s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
-#ifdef _WIN64
- ASSERT(tz);
- static int tzflag = 0;
- if (!tzflag) {
- _tzset();
- tzflag++;
- }
- tz->tz_minuteswest = _timezone / 60;
- tz->tz_dsttime = _daylight;
-#else
- struct timezone tzz;
- struct timeval tv;
- gettimeofday(&tv, &tzz);
- tz->tz_dsttime = tzz.tz_dsttime;
- tz->tz_minuteswest = tzz.tz_minuteswest;
-#endif
- return ORBIS_OK;
-}
-
-int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
+s32 PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
if (res == nullptr) {
- return ORBIS_KERNEL_ERROR_EFAULT;
+ SetPosixErrno(EFAULT);
+ return -1;
}
- clockid_t pclock_id = CLOCK_REALTIME;
+
+ if (clock_id == ORBIS_CLOCK_EXT_NETWORK || clock_id == ORBIS_CLOCK_EXT_DEBUG_NETWORK ||
+ clock_id == ORBIS_CLOCK_EXT_AD_NETWORK || clock_id == ORBIS_CLOCK_EXT_RAW_NETWORK) {
+ LOG_ERROR(Lib_Kernel, "Unsupported clock type {}, using CLOCK_MONOTONIC", clock_id);
+ clock_id = ORBIS_CLOCK_MONOTONIC;
+ }
+
+#ifdef _WIN32
+ switch (clock_id) {
+ case ORBIS_CLOCK_SECOND:
+ case ORBIS_CLOCK_REALTIME_FAST: {
+ DWORD timeAdjustment;
+ DWORD timeIncrement;
+ BOOL isTimeAdjustmentDisabled;
+ if (!GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled)) {
+ SetPosixErrno(EFAULT);
+ return -1;
+ }
+ res->tv_sec = 0;
+ res->tv_nsec = timeIncrement * 100;
+ return 0;
+ }
+ case ORBIS_CLOCK_REALTIME:
+ case ORBIS_CLOCK_REALTIME_PRECISE:
+ case ORBIS_CLOCK_UPTIME:
+ case ORBIS_CLOCK_UPTIME_PRECISE:
+ case ORBIS_CLOCK_MONOTONIC:
+ case ORBIS_CLOCK_MONOTONIC_PRECISE:
+ case ORBIS_CLOCK_UPTIME_FAST:
+ case ORBIS_CLOCK_MONOTONIC_FAST: {
+ LARGE_INTEGER pf;
+ if (!QueryPerformanceFrequency(&pf)) {
+ SetPosixErrno(EFAULT);
+ return -1;
+ }
+ res->tv_sec = 0;
+ res->tv_nsec =
+ std::max(static_cast((1000000000 + (pf.QuadPart >> 1)) / pf.QuadPart), 1);
+ return 0;
+ }
+ default:
+ UNREACHABLE();
+ }
+#else
+ clockid_t pclock_id;
switch (clock_id) {
case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE:
- case ORBIS_CLOCK_REALTIME_FAST:
pclock_id = CLOCK_REALTIME;
break;
case ORBIS_CLOCK_SECOND:
+ case ORBIS_CLOCK_REALTIME_FAST:
+#ifdef CLOCK_REALTIME_COARSE
+ pclock_id = CLOCK_REALTIME_COARSE;
+#else
+ pclock_id = CLOCK_REALTIME;
+#endif
+ break;
+ case ORBIS_CLOCK_UPTIME:
+ case ORBIS_CLOCK_UPTIME_PRECISE:
case ORBIS_CLOCK_MONOTONIC:
case ORBIS_CLOCK_MONOTONIC_PRECISE:
- case ORBIS_CLOCK_MONOTONIC_FAST:
pclock_id = CLOCK_MONOTONIC;
break;
+ case ORBIS_CLOCK_UPTIME_FAST:
+ case ORBIS_CLOCK_MONOTONIC_FAST:
+#ifdef CLOCK_MONOTONIC_COARSE
+ pclock_id = CLOCK_MONOTONIC_COARSE;
+#else
+ pclock_id = CLOCK_MONOTONIC;
+#endif
+ break;
default:
UNREACHABLE();
}
timespec t{};
- int result = clock_getres(pclock_id, &t);
+ const auto result = clock_getres(pclock_id, &t);
res->tv_sec = t.tv_sec;
res->tv_nsec = t.tv_nsec;
- if (result == 0) {
- return ORBIS_OK;
+ if (result < 0) {
+ SetPosixErrno(errno);
+ return -1;
}
- return ORBIS_KERNEL_ERROR_EINVAL;
+ return 0;
+#endif
}
-int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
- OrbisKernelTimezone* timezone, int* dst_seconds) {
+s32 PS4_SYSV_ABI sceKernelClockGetres(const u32 clock_id, OrbisKernelTimespec* res) {
+ if (const auto ret = posix_clock_getres(clock_id, res); ret < 0) {
+ return ErrnoToSceKernelError(*__Error());
+ }
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI posix_gettimeofday(OrbisKernelTimeval* tp, OrbisKernelTimezone* tz) {
+#ifdef _WIN64
+ if (tp) {
+ FILETIME filetime;
+ GetSystemTimePreciseAsFileTime(&filetime);
+
+ constexpr u64 UNIX_TIME_START = 0x295E9648864000;
+ constexpr u64 TICKS_PER_SECOND = 1000000;
+
+ u64 ticks = filetime.dwHighDateTime;
+ ticks <<= 32;
+ ticks |= filetime.dwLowDateTime;
+ ticks /= 10;
+ ticks -= UNIX_TIME_START;
+
+ tp->tv_sec = ticks / TICKS_PER_SECOND;
+ tp->tv_usec = ticks % TICKS_PER_SECOND;
+ }
+ if (tz) {
+ static int tzflag = 0;
+ if (!tzflag) {
+ _tzset();
+ tzflag++;
+ }
+ tz->tz_minuteswest = _timezone / 60;
+ tz->tz_dsttime = _daylight;
+ }
+ return 0;
+#else
+ struct timezone tzz;
+ timeval tv;
+ const auto ret = gettimeofday(&tv, &tzz);
+ if (tp) {
+ tp->tv_sec = tv.tv_sec;
+ tp->tv_usec = tv.tv_usec;
+ }
+ if (tz) {
+ tz->tz_dsttime = tzz.tz_dsttime;
+ tz->tz_minuteswest = tzz.tz_minuteswest;
+ }
+ if (ret < 0) {
+ SetPosixErrno(errno);
+ return -1;
+ }
+ return 0;
+#endif
+}
+
+s32 PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
+ if (const auto ret = posix_gettimeofday(tp, nullptr); ret < 0) {
+ return ErrnoToSceKernelError(*__Error());
+ }
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
+ if (const auto ret = posix_gettimeofday(nullptr, tz); ret < 0) {
+ return ErrnoToSceKernelError(*__Error());
+ }
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
+ OrbisKernelTimezone* timezone, s32* dst_seconds) {
LOG_INFO(Kernel, "called");
if (timezone) {
sceKernelGettimezone(timezone);
param_1 -= (timezone->tz_minuteswest + timezone->tz_dsttime) * 60;
- if (seconds)
+ if (seconds) {
*seconds = param_1;
- if (dst_seconds)
+ }
+ if (dst_seconds) {
*dst_seconds = timezone->tz_dsttime * 60;
+ }
} else {
return ORBIS_KERNEL_ERROR_EINVAL;
}
@@ -415,7 +482,7 @@ Common::NativeClock* GetClock() {
} // namespace Dev
-int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
+s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
struct OrbisTimesec* st, u64* dst_sec) {
LOG_TRACE(Kernel, "Called");
#ifdef __APPLE__
@@ -444,28 +511,35 @@ int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
void RegisterTime(Core::Loader::SymbolsResolver* sym) {
clock = std::make_unique();
initial_ptc = clock->GetUptime();
+
+ // POSIX
+ LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);
+ LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep);
+ LIB_FUNCTION("QcteRwbsnV0", "libkernel", 1, "libkernel", 1, 1, posix_usleep);
+ LIB_FUNCTION("QcteRwbsnV0", "libScePosix", 1, "libkernel", 1, 1, posix_usleep);
+ LIB_FUNCTION("0wu33hunNdE", "libkernel", 1, "libkernel", 1, 1, posix_sleep);
+ LIB_FUNCTION("0wu33hunNdE", "libScePosix", 1, "libkernel", 1, 1, posix_sleep);
+ LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, posix_clock_gettime);
+ LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime);
+ LIB_FUNCTION("smIj7eqzZE8", "libkernel", 1, "libkernel", 1, 1, posix_clock_getres);
+ LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
+ LIB_FUNCTION("n88vx3C5nW8", "libkernel", 1, "libkernel", 1, 1, posix_gettimeofday);
+ LIB_FUNCTION("n88vx3C5nW8", "libScePosix", 1, "libkernel", 1, 1, posix_gettimeofday);
+
+ // Orbis
LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime);
LIB_FUNCTION("fgxnMeTNUtY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTimeCounter);
LIB_FUNCTION("BNowx2l588E", "libkernel", 1, "libkernel", 1, 1,
sceKernelGetProcessTimeCounterFrequency);
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
LIB_FUNCTION("1j3S3n-tTW4", "libkernel", 1, "libkernel", 1, 1, sceKernelGetTscFrequency);
- LIB_FUNCTION("ejekcaNQNq0", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimeofday);
- LIB_FUNCTION("n88vx3C5nW8", "libkernel", 1, "libkernel", 1, 1, gettimeofday);
- LIB_FUNCTION("n88vx3C5nW8", "libScePosix", 1, "libkernel", 1, 1, gettimeofday);
LIB_FUNCTION("QvsZxomvUHs", "libkernel", 1, "libkernel", 1, 1, sceKernelNanosleep);
LIB_FUNCTION("1jfXLRVzisc", "libkernel", 1, "libkernel", 1, 1, sceKernelUsleep);
- LIB_FUNCTION("QcteRwbsnV0", "libkernel", 1, "libkernel", 1, 1, posix_usleep);
- LIB_FUNCTION("QcteRwbsnV0", "libScePosix", 1, "libkernel", 1, 1, posix_usleep);
LIB_FUNCTION("-ZR+hG7aDHw", "libkernel", 1, "libkernel", 1, 1, sceKernelSleep);
- LIB_FUNCTION("0wu33hunNdE", "libScePosix", 1, "libkernel", 1, 1, sceKernelSleep);
- LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);
- LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep);
LIB_FUNCTION("QBi7HCK03hw", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGettime);
+ LIB_FUNCTION("wRYVA5Zolso", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGetres);
+ LIB_FUNCTION("ejekcaNQNq0", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimeofday);
LIB_FUNCTION("kOcnerypnQA", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimezone);
- LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, orbis_clock_gettime);
- LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, orbis_clock_gettime);
- LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc);
LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime);
}
diff --git a/src/core/libraries/kernel/time.h b/src/core/libraries/kernel/time.h
index 407b6f9ed..c80de7bc4 100644
--- a/src/core/libraries/kernel/time.h
+++ b/src/core/libraries/kernel/time.h
@@ -75,14 +75,14 @@ u64 PS4_SYSV_ABI sceKernelGetProcessTime();
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency();
u64 PS4_SYSV_ABI sceKernelReadTsc();
-int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp);
+s32 PS4_SYSV_ABI sceKernelClockGettime(u32 clock_id, OrbisKernelTimespec* tp);
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz);
-int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
- OrbisKernelTimezone* timezone, int* dst_seconds);
+s32 PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
+ OrbisKernelTimezone* timezone, s32* dst_seconds);
-int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st,
+s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st,
u64* dst_sec);
-int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds);
+s32 PS4_SYSV_ABI sceKernelUsleep(u32 microseconds);
void RegisterTime(Core::Loader::SymbolsResolver* sym);
diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp
index 3826ff793..762c1e762 100644
--- a/src/core/libraries/libs.cpp
+++ b/src/core/libraries/libs.cpp
@@ -8,6 +8,9 @@
#include "core/libraries/audio/audioout.h"
#include "core/libraries/audio3d/audio3d.h"
#include "core/libraries/avplayer/avplayer.h"
+#include "core/libraries/camera/camera.h"
+#include "core/libraries/companion/companion_httpd.h"
+#include "core/libraries/companion/companion_util.h"
#include "core/libraries/disc_map/disc_map.h"
#include "core/libraries/game_live_streaming/gamelivestreaming.h"
#include "core/libraries/gnmdriver/gnmdriver.h"
@@ -57,6 +60,7 @@
#include "core/libraries/videodec/videodec.h"
#include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videoout/video_out.h"
+#include "core/libraries/voice/voice.h"
#include "core/libraries/web_browser_dialog/webbrowserdialog.h"
#include "core/libraries/zlib/zlib_sce.h"
#include "fiber/fiber.h"
@@ -122,6 +126,10 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::DiscMap::RegisterlibSceDiscMap(sym);
Libraries::Ulobjmgr::RegisterlibSceUlobjmgr(sym);
Libraries::SigninDialog::RegisterlibSceSigninDialog(sym);
+ Libraries::Camera::RegisterlibSceCamera(sym);
+ Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym);
+ Libraries::CompanionUtil::RegisterlibSceCompanionUtil(sym);
+ Libraries::Voice::RegisterlibSceVoice(sym);
}
} // namespace Libraries
diff --git a/src/core/libraries/libs.h b/src/core/libraries/libs.h
index aa5ba4a97..d9c8216a5 100644
--- a/src/core/libraries/libs.h
+++ b/src/core/libraries/libs.h
@@ -3,13 +3,9 @@
#pragma once
-#include
-
-#include "common/logging/log.h"
#include "core/loader/elf.h"
#include "core/loader/symbols_resolver.h"
-
-#define W(foo) foo
+#include "core/tls.h"
#define LIB_FUNCTION(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \
{ \
@@ -21,11 +17,11 @@
sr.module_version_major = moduleVersionMajor; \
sr.module_version_minor = moduleVersionMinor; \
sr.type = Core::Loader::SymbolType::Function; \
- auto func = reinterpret_cast(function); \
+ auto func = reinterpret_cast(HOST_CALL(function)); \
sym->AddSymbol(sr, func); \
}
-#define LIB_OBJ(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \
+#define LIB_OBJ(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, obj) \
{ \
Core::Loader::SymbolResolver sr{}; \
sr.name = nid; \
@@ -35,8 +31,7 @@
sr.module_version_major = moduleVersionMajor; \
sr.module_version_minor = moduleVersionMinor; \
sr.type = Core::Loader::SymbolType::Object; \
- auto func = reinterpret_cast(function); \
- sym->AddSymbol(sr, func); \
+ sym->AddSymbol(sr, reinterpret_cast(obj)); \
}
namespace Libraries {
diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp
index 1f024277f..9607f0d78 100644
--- a/src/core/libraries/network/net.cpp
+++ b/src/core/libraries/network/net.cpp
@@ -886,6 +886,7 @@ int PS4_SYSV_ABI sceNetGetsockname(OrbisNetId s, OrbisNetSockaddr* addr, u32* pa
}
int PS4_SYSV_ABI sceNetGetsockopt(OrbisNetId s, int level, int optname, void* optval, u32* optlen) {
+ LOG_INFO(Lib_Net, "s={} level={} optname={}", s, level, optname);
if (!g_isNetInitialized) {
return ORBIS_NET_ERROR_ENOTINIT;
}
@@ -954,16 +955,148 @@ u16 PS4_SYSV_ABI sceNetHtons(u16 host16) {
return htons(host16);
}
-const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size) {
#ifdef WIN32
- const char* res = InetNtopA(af, src, dst, size);
-#else
- const char* res = inet_ntop(af, src, dst, size);
-#endif
- if (res == nullptr) {
- UNREACHABLE();
+// there isn't a strlcpy function in windows so implement one
+u64 strlcpy(char* dst, const char* src, u64 size) {
+ u64 src_len = strlen(src);
+
+ if (size > 0) {
+ u64 copy_len = (src_len >= size) ? (size - 1) : src_len;
+ memcpy(dst, src, copy_len);
+ dst[copy_len] = '\0';
}
- return dst;
+
+ return src_len;
+}
+
+#endif
+
+const char* freebsd_inet_ntop4(const char* src, char* dst, u64 size) {
+ static const char fmt[] = "%u.%u.%u.%u";
+ char tmp[sizeof "255.255.255.255"];
+ int l;
+
+ l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
+ if (l <= 0 || (socklen_t)l >= size) {
+ return nullptr;
+ }
+ strlcpy(dst, tmp, size);
+ return (dst);
+}
+
+const char* freebsd_inet_ntop6(const char* src, char* dst, u64 size) {
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
+ struct {
+ int base, len;
+ } best, cur;
+#define NS_IN6ADDRSZ 16
+#define NS_INT16SZ 2
+ u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
+ int i;
+
+ /*
+ * Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof words);
+ for (i = 0; i < NS_IN6ADDRSZ; i++)
+ words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+ best.base = -1;
+ best.len = 0;
+ cur.base = -1;
+ cur.len = 0;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ if (words[i] == 0) {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ } else {
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ }
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ }
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /*
+ * Format the result.
+ */
+ tp = tmp;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ /* Are we inside the best run of 0x00's? */
+ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) {
+ if (i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+ /* Are we following an initial run of 0x00s or any real hex? */
+ if (i != 0)
+ *tp++ = ':';
+ /* Is this address an encapsulated IPv4? */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 7 && words[7] != 0x0001) ||
+ (best.len == 5 && words[5] == 0xffff))) {
+ if (!freebsd_inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp)))
+ return nullptr;
+ tp += strlen(tp);
+ break;
+ }
+ tp += sprintf(tp, "%x", words[i]);
+ }
+ /* Was it a trailing run of 0x00's? */
+ if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ))
+ *tp++ = ':';
+ *tp++ = '\0';
+
+ /*
+ * Check for overflow, copy, and we're done.
+ */
+ if ((u64)(tp - tmp) > size) {
+ return nullptr;
+ }
+ strcpy(dst, tmp);
+ return (dst);
+}
+const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size) {
+ if (!(src && dst)) {
+ *sceNetErrnoLoc() = ORBIS_NET_ENOSPC;
+ LOG_ERROR(Lib_Net, "returned ORBIS_NET_ENOSPC");
+ return nullptr;
+ }
+ const char* returnvalue = nullptr;
+ switch (af) {
+ case ORBIS_NET_AF_INET:
+ returnvalue = freebsd_inet_ntop4((const char*)src, dst, size);
+ break;
+ case ORBIS_NET_AF_INET6:
+ returnvalue = freebsd_inet_ntop6((const char*)src, dst, size);
+ break;
+ default:
+ *sceNetErrnoLoc() = ORBIS_NET_EAFNOSUPPORT;
+ LOG_ERROR(Lib_Net, "returned ORBIS_NET_EAFNOSUPPORT");
+ return nullptr;
+ }
+ if (returnvalue == nullptr) {
+ *sceNetErrnoLoc() = ORBIS_NET_ENOSPC;
+ LOG_ERROR(Lib_Net, "returned ORBIS_NET_ENOSPC");
+ }
+ return returnvalue;
}
int PS4_SYSV_ABI sceNetInetNtopWithScopeId() {
@@ -1449,6 +1582,7 @@ int PS4_SYSV_ABI sceNetSetDnsInfoToKernel() {
int PS4_SYSV_ABI sceNetSetsockopt(OrbisNetId s, int level, int optname, const void* optval,
u32 optlen) {
+ LOG_INFO(Lib_Net, "s={} level={} optname={} optlen={}", s, level, optname, optlen);
if (!g_isNetInitialized) {
return ORBIS_NET_ERROR_ENOTINIT;
}
diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h
index 812ee6bd7..1393ecb1d 100644
--- a/src/core/libraries/network/net.h
+++ b/src/core/libraries/network/net.h
@@ -20,6 +20,10 @@ class SymbolsResolver;
namespace Libraries::Net {
+enum OrbisNetFamily : u32 {
+ ORBIS_NET_AF_INET = 2,
+ ORBIS_NET_AF_INET6 = 28,
+};
enum OrbisNetSocketType : u32 {
ORBIS_NET_SOCK_STREAM = 1,
ORBIS_NET_SOCK_DGRAM = 2,
diff --git a/src/core/libraries/network/p2p_sockets.cpp b/src/core/libraries/network/p2p_sockets.cpp
index e9b710bb3..4f678dace 100644
--- a/src/core/libraries/network/p2p_sockets.cpp
+++ b/src/core/libraries/network/p2p_sockets.cpp
@@ -10,25 +10,25 @@ namespace Libraries::Net {
int P2PSocket::Close() {
LOG_ERROR(Lib_Net, "(STUBBED) called");
- return -1;
+ return 0;
}
int P2PSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
- return -1;
+ return 0;
}
int P2PSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
- return -1;
+ return 0;
}
int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
- return -1;
+ return 0;
}
int P2PSocket::Listen(int backlog) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
- return -1;
+ return 0;
}
int P2PSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
@@ -49,12 +49,12 @@ SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
- return -1;
+ return 0;
}
int P2PSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
LOG_ERROR(Lib_Net, "(STUBBED) called");
- return -1;
+ return 0;
}
} // namespace Libraries::Net
\ No newline at end of file
diff --git a/src/core/libraries/network/posix_sockets.cpp b/src/core/libraries/network/posix_sockets.cpp
index 140e4fd22..2df375262 100644
--- a/src/core/libraries/network/posix_sockets.cpp
+++ b/src/core/libraries/network/posix_sockets.cpp
@@ -143,6 +143,7 @@ static void convertPosixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) {
}
int PosixSocket::Close() {
+ std::scoped_lock lock{m_mutex};
#ifdef _WIN32
auto out = closesocket(sock);
#else
@@ -152,17 +153,20 @@ int PosixSocket::Close() {
}
int PosixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) {
+ std::scoped_lock lock{m_mutex};
sockaddr addr2;
convertOrbisNetSockaddrToPosix(addr, &addr2);
return ConvertReturnErrorCode(::bind(sock, &addr2, sizeof(sockaddr_in)));
}
int PosixSocket::Listen(int backlog) {
+ std::scoped_lock lock{m_mutex};
return ConvertReturnErrorCode(::listen(sock, backlog));
}
int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to,
u32 tolen) {
+ std::scoped_lock lock{m_mutex};
if (to != nullptr) {
sockaddr addr;
convertOrbisNetSockaddrToPosix(to, &addr);
@@ -175,6 +179,7 @@ int PosixSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetS
int PosixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from,
u32* fromlen) {
+ std::scoped_lock lock{m_mutex};
if (from != nullptr) {
sockaddr addr;
int res = recvfrom(sock, (char*)buf, len, flags, &addr, (socklen_t*)fromlen);
@@ -187,6 +192,7 @@ int PosixSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr*
}
SocketPtr PosixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
+ std::scoped_lock lock{m_mutex};
sockaddr addr2;
net_socket new_socket = ::accept(sock, &addr2, (socklen_t*)addrlen);
#ifdef _WIN32
@@ -202,12 +208,14 @@ SocketPtr PosixSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) {
}
int PosixSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) {
+ std::scoped_lock lock{m_mutex};
sockaddr addr2;
convertOrbisNetSockaddrToPosix(addr, &addr2);
return ::connect(sock, &addr2, sizeof(sockaddr_in));
}
int PosixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
+ std::scoped_lock lock{m_mutex};
sockaddr addr;
convertOrbisNetSockaddrToPosix(name, &addr);
if (name != nullptr) {
@@ -234,13 +242,15 @@ int PosixSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) {
return 0
int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) {
+ std::scoped_lock lock{m_mutex};
level = ConvertLevels(level);
+ ::linger native_linger;
if (level == SOL_SOCKET) {
switch (optname) {
CASE_SETSOCKOPT(SO_REUSEADDR);
CASE_SETSOCKOPT(SO_KEEPALIVE);
CASE_SETSOCKOPT(SO_BROADCAST);
- CASE_SETSOCKOPT(SO_LINGER);
+ // CASE_SETSOCKOPT(SO_LINGER);
CASE_SETSOCKOPT(SO_SNDBUF);
CASE_SETSOCKOPT(SO_RCVBUF);
CASE_SETSOCKOPT(SO_SNDTIMEO);
@@ -251,6 +261,24 @@ int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u3
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_ONESBCAST, &sockopt_so_onesbcast);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USECRYPTO, &sockopt_so_usecrypto);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_SO_USESIGNATURE, &sockopt_so_usesignature);
+ case ORBIS_NET_SO_LINGER: {
+ if (socket_type != ORBIS_NET_SOCK_STREAM) {
+ return ORBIS_NET_EPROCUNAVAIL;
+ }
+ if (optlen < sizeof(OrbisNetLinger)) {
+ LOG_ERROR(Lib_Net, "size missmatched! optlen = {} OrbisNetLinger={}", optlen,
+ sizeof(OrbisNetLinger));
+ return ORBIS_NET_ERROR_EINVAL;
+ }
+
+ const void* native_val = &native_linger;
+ u32 native_len = sizeof(native_linger);
+ native_linger.l_onoff = reinterpret_cast(optval)->l_onoff;
+ native_linger.l_linger = reinterpret_cast(optval)->l_linger;
+ return ConvertReturnErrorCode(
+ setsockopt(sock, level, SO_LINGER, (const char*)native_val, native_len));
+ }
+
case ORBIS_NET_SO_NAME:
return ORBIS_NET_ERROR_EINVAL; // don't support set for name
case ORBIS_NET_SO_NBIO: {
@@ -269,7 +297,7 @@ int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u3
}
} else if (level == IPPROTO_IP) {
switch (optname) {
- CASE_SETSOCKOPT(IP_HDRINCL);
+ // CASE_SETSOCKOPT(IP_HDRINCL);
CASE_SETSOCKOPT(IP_TOS);
CASE_SETSOCKOPT(IP_TTL);
CASE_SETSOCKOPT(IP_MULTICAST_IF);
@@ -279,6 +307,13 @@ int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u3
CASE_SETSOCKOPT(IP_DROP_MEMBERSHIP);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_TTLCHK, &sockopt_ip_ttlchk);
CASE_SETSOCKOPT_VALUE(ORBIS_NET_IP_MAXTTL, &sockopt_ip_maxttl);
+ case ORBIS_NET_IP_HDRINCL: {
+ if (socket_type != ORBIS_NET_SOCK_RAW) {
+ return ORBIS_NET_EPROCUNAVAIL;
+ }
+ return ConvertReturnErrorCode(
+ setsockopt(sock, level, optname, (const char*)optval, optlen));
+ }
}
} else if (level == IPPROTO_TCP) {
switch (optname) {
@@ -311,6 +346,7 @@ int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u3
return 0;
int PosixSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) {
+ std::scoped_lock lock{m_mutex};
level = ConvertLevels(level);
if (level == SOL_SOCKET) {
switch (optname) {
diff --git a/src/core/libraries/network/sockets.h b/src/core/libraries/network/sockets.h
index e41671d88..c54e11e66 100644
--- a/src/core/libraries/network/sockets.h
+++ b/src/core/libraries/network/sockets.h
@@ -32,6 +32,10 @@ struct Socket;
typedef std::shared_ptr SocketPtr;
+struct OrbisNetLinger {
+ s32 l_onoff;
+ s32 l_linger;
+};
struct Socket {
explicit Socket(int domain, int type, int protocol) {}
virtual ~Socket() = default;
@@ -47,6 +51,7 @@ struct Socket {
u32* fromlen) = 0;
virtual int Connect(const OrbisNetSockaddr* addr, u32 namelen) = 0;
virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0;
+ std::mutex m_mutex;
};
struct PosixSocket : public Socket {
@@ -59,8 +64,11 @@ struct PosixSocket : public Socket {
int sockopt_ip_ttlchk = 0;
int sockopt_ip_maxttl = 0;
int sockopt_tcp_mss_to_advertise = 0;
+ int socket_type;
explicit PosixSocket(int domain, int type, int protocol)
- : Socket(domain, type, protocol), sock(socket(domain, type, protocol)) {}
+ : Socket(domain, type, protocol), sock(socket(domain, type, protocol)) {
+ socket_type = type;
+ }
explicit PosixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {}
int Close() override;
int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override;
diff --git a/src/core/libraries/network/sys_net.cpp b/src/core/libraries/network/sys_net.cpp
index fbf2a2456..087632159 100644
--- a/src/core/libraries/network/sys_net.cpp
+++ b/src/core/libraries/network/sys_net.cpp
@@ -1,4 +1,3 @@
-#include "sys_net.h"
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
diff --git a/src/core/libraries/ngs2/ngs2.cpp b/src/core/libraries/ngs2/ngs2.cpp
index 743be5fd6..9bb73536c 100644
--- a/src/core/libraries/ngs2/ngs2.cpp
+++ b/src/core/libraries/ngs2/ngs2.cpp
@@ -380,8 +380,7 @@ s32 PS4_SYSV_ABI sceNgs2GeomApply(const OrbisNgs2GeomListenerWork* listener,
s32 PS4_SYSV_ABI sceNgs2PanInit(OrbisNgs2PanWork* work, const float* aSpeakerAngle, float unitAngle,
u32 numSpeakers) {
- LOG_ERROR(Lib_Ngs2, "aSpeakerAngle = {}, unitAngle = {}, numSpeakers = {}", *aSpeakerAngle,
- unitAngle, numSpeakers);
+ LOG_ERROR(Lib_Ngs2, "unitAngle = {}, numSpeakers = {}", unitAngle, numSpeakers);
return ORBIS_OK;
}
diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp
index a60dcd86f..bc920b5a9 100644
--- a/src/core/libraries/np_manager/np_manager.cpp
+++ b/src/core/libraries/np_manager/np_manager.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/config.h"
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
@@ -10,6 +11,8 @@
namespace Libraries::NpManager {
+#define SIGNEDIN_STATUS (Config::getPSNSignedIn() ? ORBIS_OK : ORBIS_NP_ERROR_SIGNED_OUT)
+
int PS4_SYSV_ABI Func_EF4378573542A508() {
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
return ORBIS_OK;
@@ -921,9 +924,16 @@ int PS4_SYSV_ABI sceNpGetAccountCountry() {
return ORBIS_OK;
}
-int PS4_SYSV_ABI sceNpGetAccountCountryA() {
- LOG_ERROR(Lib_NpManager, "(STUBBED) called");
- return ORBIS_OK;
+int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id,
+ OrbisNpCountryCode* country_code) {
+ LOG_INFO(Lib_NpManager, "(STUBBED) called, user_id = {}", user_id);
+ if (country_code == nullptr) {
+ return ORBIS_NP_ERROR_INVALID_ARGUMENT;
+ }
+ ::memset(country_code, 0, sizeof(OrbisNpCountryCode));
+ // TODO: get NP country code from config
+ ::memcpy(country_code->country_code, "us", 2);
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetAccountDateOfBirth() {
@@ -941,8 +951,8 @@ int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id)
if (online_id == nullptr || account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- *account_id = 0;
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ *account_id = 0xFEEDFACE;
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) {
@@ -950,8 +960,8 @@ int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account
if (account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- *account_id = 0;
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ *account_id = 0xFEEDFACE;
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetAccountLanguage() {
@@ -984,7 +994,9 @@ int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id)
if (np_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ memset(np_id, 0, sizeof(OrbisNpId));
+ strncpy(np_id->handle.data, Config::getUserName().c_str(), sizeof(np_id->handle.data));
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
@@ -997,7 +1009,9 @@ int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineI
if (online_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- return ORBIS_NP_ERROR_SIGNED_OUT;
+ memset(online_id, 0, sizeof(OrbisNpOnlineId));
+ strncpy(online_id->data, Config::getUserName().c_str(), sizeof(online_id->data));
+ return SIGNEDIN_STATUS;
}
int PS4_SYSV_ABI sceNpGetParentalControlInfo() {
@@ -1014,8 +1028,8 @@ int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* sta
if (state == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
- *state = OrbisNpState::SignedOut;
- LOG_DEBUG(Lib_NpManager, "Signed out");
+ *state = Config::getPSNSignedIn() ? OrbisNpState::SignedIn : OrbisNpState::SignedOut;
+ LOG_DEBUG(Lib_NpManager, "Signed {}", Config::getPSNSignedIn() ? "in" : "out");
return ORBIS_OK;
}
diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h
index 02a1a32f6..1078a9f3e 100644
--- a/src/core/libraries/np_manager/np_manager.h
+++ b/src/core/libraries/np_manager/np_manager.h
@@ -32,6 +32,12 @@ struct OrbisNpId {
u8 reserved[8];
};
+struct OrbisNpCountryCode {
+ char country_code[2];
+ char end;
+ char pad;
+};
+
int PS4_SYSV_ABI Func_EF4378573542A508();
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromKernel();
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromPool();
@@ -215,7 +221,8 @@ int PS4_SYSV_ABI sceNpCreateRequest();
int PS4_SYSV_ABI sceNpDeleteRequest(int reqId);
int PS4_SYSV_ABI sceNpGetAccountAge();
int PS4_SYSV_ABI sceNpGetAccountCountry();
-int PS4_SYSV_ABI sceNpGetAccountCountryA();
+int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id,
+ OrbisNpCountryCode* country_code);
int PS4_SYSV_ABI sceNpGetAccountDateOfBirth();
int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA();
int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id);
diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp
index a951d5655..e3c5ce35e 100644
--- a/src/core/libraries/np_trophy/np_trophy.cpp
+++ b/src/core/libraries/np_trophy/np_trophy.cpp
@@ -164,10 +164,12 @@ s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, int32_t
}
const auto ctx_id = trophy_contexts.insert(user_id, service_label);
- contexts_internal[key].context_id = ctx_id.index;
- LOG_INFO(Lib_NpTrophy, "New context = {}, user_id = {} service label = {}", ctx_id.index,
- user_id, service_label);
- *context = ctx_id.index;
+
+ *context = ctx_id.index + 1;
+ contexts_internal[key].context_id = *context;
+ LOG_INFO(Lib_NpTrophy, "New context = {}, user_id = {} service label = {}", *context, user_id,
+ service_label);
+
return ORBIS_OK;
}
@@ -179,21 +181,23 @@ s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(OrbisNpTrophyHandle* handle) {
if (trophy_handles.size() >= MaxTrophyHandles) {
return ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX;
}
- const auto handle_id = trophy_handles.insert();
- LOG_INFO(Lib_NpTrophy, "New handle = {}", handle_id.index);
- *handle = handle_id.index;
+ const auto handle_id = trophy_handles.insert();
+
+ *handle = handle_id.index + 1;
+ LOG_INFO(Lib_NpTrophy, "New handle = {}", *handle);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyDestroyContext(OrbisNpTrophyContext context) {
LOG_INFO(Lib_NpTrophy, "Destroyed Context {}", context);
- if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
+ if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT) {
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
+ }
Common::SlotId contextId;
- contextId.index = context;
+ contextId.index = context - 1;
ContextKey contextkey = trophy_contexts[contextId];
trophy_contexts.erase(contextId);
@@ -206,11 +210,17 @@ s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(OrbisNpTrophyHandle handle) {
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
- if (!trophy_handles.is_allocated({static_cast(handle)})) {
+ s32 handle_index = handle - 1;
+ if (handle_index >= trophy_handles.size()) {
+ LOG_ERROR(Lib_NpTrophy, "Invalid handle {}", handle);
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
}
- trophy_handles.erase({static_cast(handle)});
+ if (!trophy_handles.is_allocated({static_cast(handle_index)})) {
+ return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
+ }
+
+ trophy_handles.erase({static_cast(handle_index)});
LOG_INFO(Lib_NpTrophy, "Handle {} destroyed", handle);
return ORBIS_OK;
}
diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp
index 5dfc68e90..42582783b 100644
--- a/src/core/libraries/pad/pad.cpp
+++ b/src/core/libraries/pad/pad.cpp
@@ -316,22 +316,79 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
pData[i].angularVelocity.y = states[i].angularVelocity.y;
pData[i].angularVelocity.z = states[i].angularVelocity.z;
pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
- if (engine) {
+ pData[i].acceleration.x = states[i].acceleration.x * 0.098;
+ pData[i].acceleration.y = states[i].acceleration.y * 0.098;
+ pData[i].acceleration.z = states[i].acceleration.z * 0.098;
+ pData[i].angularVelocity.x = states[i].angularVelocity.x;
+ pData[i].angularVelocity.y = states[i].angularVelocity.y;
+ pData[i].angularVelocity.z = states[i].angularVelocity.z;
+
+ if (engine && handle == 1) {
const auto gyro_poll_rate = engine->GetAccelPollRate();
if (gyro_poll_rate != 0.0f) {
- GameController::CalculateOrientation(pData[i].acceleration,
- pData[i].angularVelocity,
- 1.0f / gyro_poll_rate, pData[i].orientation);
+ auto now = std::chrono::steady_clock::now();
+ float deltaTime = std::chrono::duration_cast(
+ now - controller->GetLastUpdate())
+ .count() /
+ 1000000.0f;
+ controller->SetLastUpdate(now);
+ Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
+ Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
+ GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
+ deltaTime, lastOrientation, outputOrientation);
+ pData[i].orientation = outputOrientation;
+ controller->SetLastOrientation(outputOrientation);
}
}
+
pData[i].touchData.touchNum =
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0);
+
+ if (handle == 1) {
+ if (controller->GetTouchCount() >= 127) {
+ controller->SetTouchCount(0);
+ }
+
+ if (controller->GetSecondaryTouchCount() >= 127) {
+ controller->SetSecondaryTouchCount(0);
+ }
+
+ if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) {
+ controller->SetTouchCount(controller->GetTouchCount() + 1);
+ controller->SetSecondaryTouchCount(controller->GetTouchCount());
+ } else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) {
+ controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1);
+ } else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) {
+ if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ } else {
+ if (controller->WasSecondaryTouchReset()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ controller->UnsetSecondaryTouchResetBool();
+ }
+ }
+ }
+
+ controller->SetPreviousTouchNum(pData->touchData.touchNum);
+
+ if (pData->touchData.touchNum == 1) {
+ states[i].touchpad[0].ID = controller->GetTouchCount();
+ states[i].touchpad[1].ID = 0;
+ } else if (pData->touchData.touchNum == 2) {
+ states[i].touchpad[0].ID = controller->GetTouchCount();
+ states[i].touchpad[1].ID = controller->GetSecondaryTouchCount();
+ }
+ } else {
+ states[i].touchpad[0].ID = 1;
+ states[i].touchpad[1].ID = 2;
+ }
+
pData[i].touchData.touch[0].x = states[i].touchpad[0].x;
pData[i].touchData.touch[0].y = states[i].touchpad[0].y;
- pData[i].touchData.touch[0].id = 1;
+ pData[i].touchData.touch[0].id = states[i].touchpad[0].ID;
pData[i].touchData.touch[1].x = states[i].touchpad[1].x;
pData[i].touchData.touch[1].y = states[i].touchpad[1].y;
- pData[i].touchData.touch[1].id = 2;
+ pData[i].touchData.touch[1].id = states[i].touchpad[1].ID;
pData[i].connected = connected;
pData[i].timestamp = states[i].time;
pData[i].connectedCount = connected_count;
@@ -376,31 +433,85 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
pData->leftStick.x = state.axes[static_cast(Input::Axis::LeftX)];
pData->leftStick.y = state.axes[static_cast(Input::Axis::LeftY)];
pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)];
+ pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)];
pData->rightStick.y = state.axes[static_cast(Input::Axis::RightY)];
pData->analogButtons.l2 = state.axes[static_cast(Input::Axis::TriggerLeft)];
pData->analogButtons.r2 = state.axes[static_cast(Input::Axis::TriggerRight)];
- pData->acceleration.x = state.acceleration.x;
- pData->acceleration.y = state.acceleration.y;
- pData->acceleration.z = state.acceleration.z;
+ pData->acceleration.x = state.acceleration.x * 0.098;
+ pData->acceleration.y = state.acceleration.y * 0.098;
+ pData->acceleration.z = state.acceleration.z * 0.098;
pData->angularVelocity.x = state.angularVelocity.x;
pData->angularVelocity.y = state.angularVelocity.y;
pData->angularVelocity.z = state.angularVelocity.z;
pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f};
- if (engine) {
+
+ // Only do this on handle 1 for now
+ if (engine && handle == 1) {
const auto gyro_poll_rate = engine->GetAccelPollRate();
if (gyro_poll_rate != 0.0f) {
+ auto now = std::chrono::steady_clock::now();
+ float deltaTime = std::chrono::duration_cast(
+ now - controller->GetLastUpdate())
+ .count() /
+ 1000000.0f;
+ controller->SetLastUpdate(now);
+ Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
+ Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
- 1.0f / gyro_poll_rate, pData->orientation);
+ deltaTime, lastOrientation, outputOrientation);
+ pData->orientation = outputOrientation;
+ controller->SetLastOrientation(outputOrientation);
}
}
pData->touchData.touchNum =
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
+
+ // Only do this on handle 1 for now
+ if (handle == 1) {
+ if (controller->GetTouchCount() >= 127) {
+ controller->SetTouchCount(0);
+ }
+
+ if (controller->GetSecondaryTouchCount() >= 127) {
+ controller->SetSecondaryTouchCount(0);
+ }
+
+ if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) {
+ controller->SetTouchCount(controller->GetTouchCount() + 1);
+ controller->SetSecondaryTouchCount(controller->GetTouchCount());
+ } else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) {
+ controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1);
+ } else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) {
+ if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ } else {
+ if (controller->WasSecondaryTouchReset()) {
+ controller->SetTouchCount(controller->GetSecondaryTouchCount());
+ controller->UnsetSecondaryTouchResetBool();
+ }
+ }
+ }
+
+ controller->SetPreviousTouchNum(pData->touchData.touchNum);
+
+ if (pData->touchData.touchNum == 1) {
+ state.touchpad[0].ID = controller->GetTouchCount();
+ state.touchpad[1].ID = 0;
+ } else if (pData->touchData.touchNum == 2) {
+ state.touchpad[0].ID = controller->GetTouchCount();
+ state.touchpad[1].ID = controller->GetSecondaryTouchCount();
+ }
+ } else {
+ state.touchpad[0].ID = 1;
+ state.touchpad[1].ID = 2;
+ }
+
pData->touchData.touch[0].x = state.touchpad[0].x;
pData->touchData.touch[0].y = state.touchpad[0].y;
- pData->touchData.touch[0].id = 1;
+ pData->touchData.touch[0].id = state.touchpad[0].ID;
pData->touchData.touch[1].x = state.touchpad[1].x;
pData->touchData.touch[1].y = state.touchpad[1].y;
- pData->touchData.touch[1].id = 2;
+ pData->touchData.touch[1].id = state.touchpad[1].ID;
pData->timestamp = state.time;
pData->connected = true; // isConnected; //TODO fix me proper
pData->connectedCount = 1; // connectedCount;
diff --git a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp
index edb5caa07..05df67eeb 100644
--- a/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp
+++ b/src/core/libraries/save_data/dialog/savedatadialog_ui.cpp
@@ -155,7 +155,7 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) {
if (item->focusPos != FocusPos::DIRNAME) {
this->focus_pos = item->focusPos;
- } else {
+ } else if (item->focusPosDirName != nullptr) {
this->focus_pos = item->focusPosDirName->data.to_string();
}
this->style = item->itemStyle;
diff --git a/src/core/libraries/save_data/save_memory.cpp b/src/core/libraries/save_data/save_memory.cpp
index 13e122c60..ab3ce2d4f 100644
--- a/src/core/libraries/save_data/save_memory.cpp
+++ b/src/core/libraries/save_data/save_memory.cpp
@@ -10,15 +10,14 @@
#include
#include
-#include
-
-#include "common/assert.h"
+#include "boost/icl/concept/interval.hpp"
#include "common/elf_info.h"
#include "common/logging/log.h"
#include "common/path_util.h"
#include "common/singleton.h"
#include "common/thread.h"
#include "core/file_sys/fs.h"
+#include "core/libraries/system/msgdialog_ui.h"
#include "save_instance.h"
using Common::FS::IOFile;
@@ -35,11 +34,12 @@ namespace Libraries::SaveData::SaveMemory {
static Core::FileSys::MntPoints* g_mnt = Common::Singleton::Instance();
struct SlotData {
- OrbisUserServiceUserId user_id;
+ OrbisUserServiceUserId user_id{};
std::string game_serial;
std::filesystem::path folder_path;
PSF sfo;
std::vector memory_cache;
+ size_t memory_cache_size{};
};
static std::mutex g_slot_mtx;
@@ -97,7 +97,8 @@ std::filesystem::path GetSavePath(OrbisUserServiceUserId user_id, u32 slot_id,
return SaveInstance::MakeDirSavePath(user_id, Common::ElfInfo::Instance().GameSerial(), dir);
}
-size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial) {
+size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial,
+ size_t memory_size) {
std::lock_guard lck{g_slot_mtx};
const auto save_dir = GetSavePath(user_id, slot_id, game_serial);
@@ -109,6 +110,7 @@ size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_
.folder_path = save_dir,
.sfo = {},
.memory_cache = {},
+ .memory_cache_size = memory_size,
};
SaveInstance::SetupDefaultParamSFO(data.sfo, GetSaveDir(slot_id), std::string{game_serial});
@@ -196,9 +198,9 @@ void ReadMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset) {
auto& data = g_attached_slots[slot_id];
auto& memory = data.memory_cache;
if (memory.empty()) { // Load file
+ memory.resize(data.memory_cache_size);
IOFile f{data.folder_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Read};
if (f.IsOpen()) {
- memory.resize(f.GetSize());
f.Seek(0);
f.ReadSpan(std::span{memory});
}
@@ -222,5 +224,4 @@ void WriteMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset) {
Backup::NewRequest(data.user_id, data.game_serial, GetSaveDir(slot_id),
Backup::OrbisSaveDataEventType::__DO_NOT_SAVE);
}
-
} // namespace Libraries::SaveData::SaveMemory
\ No newline at end of file
diff --git a/src/core/libraries/save_data/save_memory.h b/src/core/libraries/save_data/save_memory.h
index 681865634..7765b04cd 100644
--- a/src/core/libraries/save_data/save_memory.h
+++ b/src/core/libraries/save_data/save_memory.h
@@ -4,13 +4,13 @@
#pragma once
#include
-#include "save_backup.h"
+#include "core/libraries/save_data/save_backup.h"
class PSF;
namespace Libraries::SaveData {
using OrbisUserServiceUserId = s32;
-}
+} // namespace Libraries::SaveData
namespace Libraries::SaveData::SaveMemory {
@@ -22,7 +22,8 @@ void PersistMemory(u32 slot_id, bool lock = true);
std::string_view game_serial);
// returns the size of the save memory if exists
-size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial);
+size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial,
+ size_t memory_size);
// Write the icon. Set buf to null to read the standard icon.
void SetIcon(u32 slot_id, void* buf = nullptr, size_t buf_size = 0);
diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp
index e9ad77d69..b25ebde6c 100644
--- a/src/core/libraries/save_data/savedata.cpp
+++ b/src/core/libraries/save_data/savedata.cpp
@@ -5,10 +5,10 @@
#include
#include
-#include
#include
#include "common/assert.h"
+#include "common/config.h"
#include "common/cstring.h"
#include "common/elf_info.h"
#include "common/enum.h"
@@ -20,7 +20,9 @@
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/save_data/savedata.h"
+#include "core/libraries/save_data/savedata_error.h"
#include "core/libraries/system/msgdialog.h"
+#include "core/libraries/system/msgdialog_ui.h"
#include "save_backup.h"
#include "save_instance.h"
#include "save_memory.h"
@@ -33,27 +35,6 @@ using Common::ElfInfo;
namespace Libraries::SaveData {
-enum class Error : u32 {
- OK = 0,
- USER_SERVICE_NOT_INITIALIZED = 0x80960002,
- PARAMETER = 0x809F0000,
- NOT_INITIALIZED = 0x809F0001,
- OUT_OF_MEMORY = 0x809F0002,
- BUSY = 0x809F0003,
- NOT_MOUNTED = 0x809F0004,
- EXISTS = 0x809F0007,
- NOT_FOUND = 0x809F0008,
- NO_SPACE_FS = 0x809F000A,
- INTERNAL = 0x809F000B,
- MOUNT_FULL = 0x809F000C,
- BAD_MOUNTED = 0x809F000D,
- BROKEN = 0x809F000F,
- INVALID_LOGIN_USER = 0x809F0011,
- MEMORY_NOT_READY = 0x809F0012,
- BACKUP_BUSY = 0x809F0013,
- BUSY_FOR_SAVING = 0x809F0016,
-};
-
enum class OrbisSaveDataSaveDataMemoryOption : u32 {
NONE = 0,
SET_PARAM = 1 << 0,
@@ -336,7 +317,9 @@ static std::array, 16> g_mount_slots;
static void initialize() {
g_initialized = true;
- g_game_serial = ElfInfo::Instance().GameSerial();
+ g_game_serial = Common::Singleton::Instance()
+ ->GetString("INSTALL_DIR_SAVEDATA")
+ .value_or(ElfInfo::Instance().GameSerial());
g_fw_ver = ElfInfo::Instance().FirmwareVer();
Backup::StartThread();
}
@@ -456,7 +439,7 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
LOG_INFO(Lib_SaveData, "called with invalid block size");
}
- const auto root_save = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir);
+ const auto root_save = Config::GetSaveDataPath();
fs::create_directories(root_save);
const auto available = fs::space(root_save).available;
@@ -1593,8 +1576,8 @@ Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetu
}
try {
- size_t existed_size =
- SaveMemory::SetupSaveMemory(setupParam->userId, slot_id, g_game_serial);
+ size_t existed_size = SaveMemory::SetupSaveMemory(setupParam->userId, slot_id,
+ g_game_serial, setupParam->memorySize);
if (existed_size == 0) { // Just created
if (g_fw_ver >= ElfInfo::FW_45 && setupParam->initParam != nullptr) {
auto& sfo = SaveMemory::GetParamSFO(slot_id);
diff --git a/src/core/libraries/save_data/savedata_error.h b/src/core/libraries/save_data/savedata_error.h
new file mode 100644
index 000000000..ef347e855
--- /dev/null
+++ b/src/core/libraries/save_data/savedata_error.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+namespace Libraries::SaveData {
+enum class Error : u32 {
+ OK = 0,
+ USER_SERVICE_NOT_INITIALIZED = 0x80960002,
+ PARAMETER = 0x809F0000,
+ NOT_INITIALIZED = 0x809F0001,
+ OUT_OF_MEMORY = 0x809F0002,
+ BUSY = 0x809F0003,
+ NOT_MOUNTED = 0x809F0004,
+ EXISTS = 0x809F0007,
+ NOT_FOUND = 0x809F0008,
+ NO_SPACE_FS = 0x809F000A,
+ INTERNAL = 0x809F000B,
+ MOUNT_FULL = 0x809F000C,
+ BAD_MOUNTED = 0x809F000D,
+ BROKEN = 0x809F000F,
+ INVALID_LOGIN_USER = 0x809F0011,
+ MEMORY_NOT_READY = 0x809F0012,
+ BACKUP_BUSY = 0x809F0013,
+ BUSY_FOR_SAVING = 0x809F0016,
+};
+} // namespace Libraries::SaveData
diff --git a/src/core/libraries/videodec/videodec.cpp b/src/core/libraries/videodec/videodec.cpp
index 02ea61509..ae7d17560 100644
--- a/src/core/libraries/videodec/videodec.cpp
+++ b/src/core/libraries/videodec/videodec.cpp
@@ -17,10 +17,12 @@ int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInf
LOG_INFO(Lib_Videodec, "called");
if (!pCfgInfoIn || !pRsrcInfoIn || !pCtrlOut) {
+ LOG_ERROR(Lib_Videodec, "Invalid arguments");
return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER;
}
if (pCfgInfoIn->thisSize != sizeof(OrbisVideodecConfigInfo) ||
pRsrcInfoIn->thisSize != sizeof(OrbisVideodecResourceInfo)) {
+ LOG_ERROR(Lib_Videodec, "Invalid struct size");
return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE;
}
@@ -37,15 +39,18 @@ int PS4_SYSV_ABI sceVideodecDecode(OrbisVideodecCtrl* pCtrlIn,
OrbisVideodecPictureInfo* pPictureInfoOut) {
LOG_TRACE(Lib_Videodec, "called");
if (!pCtrlIn || !pInputDataIn || !pPictureInfoOut) {
+ LOG_ERROR(Lib_Videodec, "Invalid arguments");
return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER;
}
if (pCtrlIn->thisSize != sizeof(OrbisVideodecCtrl) ||
pFrameBufferInOut->thisSize != sizeof(OrbisVideodecFrameBuffer)) {
+ LOG_ERROR(Lib_Videodec, "Invalid struct size");
return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE;
}
VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle;
if (!decoder) {
+ LOG_ERROR(Lib_Videodec, "Invalid decoder handle");
return ORBIS_VIDEODEC_ERROR_HANDLE;
}
return decoder->Decode(*pInputDataIn, *pFrameBufferInOut, *pPictureInfoOut);
@@ -56,6 +61,7 @@ int PS4_SYSV_ABI sceVideodecDeleteDecoder(OrbisVideodecCtrl* pCtrlIn) {
VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle;
if (!decoder) {
+ LOG_ERROR(Lib_Videodec, "Invalid decoder handle");
return ORBIS_VIDEODEC_ERROR_HANDLE;
}
delete decoder;
@@ -68,15 +74,18 @@ int PS4_SYSV_ABI sceVideodecFlush(OrbisVideodecCtrl* pCtrlIn,
LOG_INFO(Lib_Videodec, "called");
if (!pFrameBufferInOut || !pPictureInfoOut) {
+ LOG_ERROR(Lib_Videodec, "Invalid arguments");
return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER;
}
if (pFrameBufferInOut->thisSize != sizeof(OrbisVideodecFrameBuffer) ||
pPictureInfoOut->thisSize != sizeof(OrbisVideodecPictureInfo)) {
+ LOG_ERROR(Lib_Videodec, "Invalid struct size");
return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE;
}
VdecDecoder* decoder = (VdecDecoder*)pCtrlIn->handle;
if (!decoder) {
+ LOG_ERROR(Lib_Videodec, "Invalid decoder handle");
return ORBIS_VIDEODEC_ERROR_HANDLE;
}
return decoder->Flush(*pFrameBufferInOut, *pPictureInfoOut);
@@ -92,10 +101,12 @@ int PS4_SYSV_ABI sceVideodecQueryResourceInfo(const OrbisVideodecConfigInfo* pCf
LOG_INFO(Lib_Videodec, "called");
if (!pCfgInfoIn || !pRsrcInfoOut) {
+ LOG_ERROR(Lib_Videodec, "Invalid arguments");
return ORBIS_VIDEODEC_ERROR_ARGUMENT_POINTER;
}
if (pCfgInfoIn->thisSize != sizeof(OrbisVideodecConfigInfo) ||
pRsrcInfoOut->thisSize != sizeof(OrbisVideodecResourceInfo)) {
+ LOG_ERROR(Lib_Videodec, "Invalid struct size");
return ORBIS_VIDEODEC_ERROR_STRUCT_SIZE;
}
diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp
index a7e520b41..1c6044fe2 100644
--- a/src/core/libraries/videodec/videodec2.cpp
+++ b/src/core/libraries/videodec/videodec2.cpp
@@ -17,9 +17,11 @@ sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemIn
LOG_INFO(Lib_Vdec2, "called");
if (!computeMemInfo) {
+ LOG_ERROR(Lib_Vdec2, "Invalid arguments");
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) {
+ LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
@@ -47,10 +49,12 @@ sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decode
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo) {
+ LOG_ERROR(Lib_Vdec2, "Invalid arguments");
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
+ LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
@@ -74,10 +78,12 @@ s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo || !decoder) {
+ LOG_ERROR(Lib_Vdec2, "Invalid arguments");
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
+ LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
@@ -89,6 +95,7 @@ s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
+ LOG_ERROR(Lib_Vdec2, "Invalid arguments");
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
@@ -103,13 +110,16 @@ s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
LOG_TRACE(Lib_Vdec2, "called");
if (!decoder) {
+ LOG_ERROR(Lib_Vdec2, "Invalid decoder instance");
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!inputData || !frameBuffer || !outputInfo) {
+ LOG_ERROR(Lib_Vdec2, "Invalid arguments");
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (inputData->thisSize != sizeof(OrbisVideodec2InputData) ||
frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) {
+ LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
@@ -122,13 +132,16 @@ s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
+ LOG_ERROR(Lib_Vdec2, "Invalid decoder instance");
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!frameBuffer || !outputInfo) {
+ LOG_ERROR(Lib_Vdec2, "Invalid arguments");
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) ||
- outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
+ (outputInfo->thisSize | 8) != sizeof(OrbisVideodec2OutputInfo)) {
+ LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
@@ -139,6 +152,7 @@ s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
+ LOG_ERROR(Lib_Vdec2, "Invalid decoder instance");
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
@@ -150,19 +164,23 @@ s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outp
LOG_TRACE(Lib_Vdec2, "called");
if (!outputInfo) {
+ LOG_ERROR(Lib_Vdec2, "Invalid arguments");
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
- if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
+ if ((outputInfo->thisSize | 8) != sizeof(OrbisVideodec2OutputInfo)) {
+ LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) {
+ LOG_ERROR(Lib_Vdec2, "No picture info available");
return ORBIS_OK;
}
if (p1stPictureInfoOut) {
OrbisVideodec2AvcPictureInfo* picInfo =
static_cast(p1stPictureInfoOut);
- if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) {
+ if ((picInfo->thisSize | 16) != sizeof(OrbisVideodec2AvcPictureInfo)) {
+ LOG_ERROR(Lib_Vdec2, "Invalid struct size");
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*picInfo = gPictureInfos.back();
diff --git a/src/core/libraries/videodec/videodec2.h b/src/core/libraries/videodec/videodec2.h
index abc8f8ab5..410ee8ea6 100644
--- a/src/core/libraries/videodec/videodec2.h
+++ b/src/core/libraries/videodec/videodec2.h
@@ -73,8 +73,10 @@ struct OrbisVideodec2OutputInfo {
u32 frameHeight;
void* frameBuffer;
u64 frameBufferSize;
+ u32 frameFormat;
+ u32 framePitchInBytes;
};
-static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30);
+static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x38);
struct OrbisVideodec2FrameBuffer {
u64 thisSize;
diff --git a/src/core/libraries/videodec/videodec2_avc.h b/src/core/libraries/videodec/videodec2_avc.h
index 22293ee93..1975209cb 100644
--- a/src/core/libraries/videodec/videodec2_avc.h
+++ b/src/core/libraries/videodec/videodec2_avc.h
@@ -55,6 +55,23 @@ struct OrbisVideodec2AvcPictureInfo {
u8 pic_struct;
u8 field_pic_flag;
u8 bottom_field_flag;
+
+ u8 sequenceParameterSetPresentFlag;
+ u8 pictureParameterSetPresentFlag;
+ u8 auDelimiterPresentFlag;
+ u8 endOfSequencePresentFlag;
+ u8 endOfStreamPresentFlag;
+ u8 fillerDataPresentFlag;
+ u8 pictureTimingSeiPresentFlag;
+ u8 bufferingPeriodSeiPresentFlag;
+
+ u8 constraint_set0_flag;
+ u8 constraint_set1_flag;
+ u8 constraint_set2_flag;
+ u8 constraint_set3_flag;
+ u8 constraint_set4_flag;
+ u8 constraint_set5_flag;
};
+static_assert(sizeof(OrbisVideodec2AvcPictureInfo) == 0x78);
} // namespace Libraries::Vdec2
\ No newline at end of file
diff --git a/src/core/libraries/videodec/videodec2_impl.cpp b/src/core/libraries/videodec/videodec2_impl.cpp
index 22b17c86c..373809c14 100644
--- a/src/core/libraries/videodec/videodec2_impl.cpp
+++ b/src/core/libraries/videodec/videodec2_impl.cpp
@@ -44,11 +44,15 @@ s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
- outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
+ // Only set frameFormat if the game uses the newer struct version.
+ if (outputInfo.thisSize == sizeof(OrbisVideodec2OutputInfo)) {
+ outputInfo.frameFormat = 0;
+ }
+
if (!inputData.auData) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER;
}
@@ -113,6 +117,11 @@ s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
+ // Only set framePitchInBytes if the game uses the newer struct version.
+ if (outputInfo.thisSize == sizeof(OrbisVideodec2OutputInfo)) {
+ outputInfo.framePitchInBytes = frame->linesize[0];
+ }
+
if (outputInfo.isValid) {
OrbisVideodec2AvcPictureInfo pictureInfo = {};
@@ -140,11 +149,15 @@ s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
- outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
+ // Only set frameFormat if the game uses the newer struct version.
+ if (outputInfo.thisSize == sizeof(OrbisVideodec2OutputInfo)) {
+ outputInfo.frameFormat = 0;
+ }
+
AVFrame* frame = av_frame_alloc();
if (!frame) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
@@ -182,6 +195,11 @@ s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer,
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
+ // Only set framePitchInBytes if the game uses the newer struct version.
+ if (outputInfo.thisSize == sizeof(OrbisVideodec2OutputInfo)) {
+ outputInfo.framePitchInBytes = frame->linesize[0];
+ }
+
// FIXME: Should we add picture info here too?
}
diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp
index c5208b6dd..da715b3bf 100644
--- a/src/core/libraries/videoout/video_out.cpp
+++ b/src/core/libraries/videoout/video_out.cpp
@@ -282,7 +282,12 @@ s32 PS4_SYSV_ABI sceVideoOutGetVblankStatus(int handle, SceVideoOutVblankStatus*
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status) {
LOG_INFO(Lib_VideoOut, "called");
- *status = driver->GetPort(handle)->resolution;
+ auto* port = driver->GetPort(handle);
+ if (!port || !port->is_open) {
+ return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
+ }
+
+ *status = port->resolution;
return ORBIS_OK;
}
diff --git a/src/core/libraries/voice/voice.cpp b/src/core/libraries/voice/voice.cpp
new file mode 100644
index 000000000..caa16431a
--- /dev/null
+++ b/src/core/libraries/voice/voice.cpp
@@ -0,0 +1,203 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "core/libraries/error_codes.h"
+#include "core/libraries/libs.h"
+#include "core/libraries/voice/voice.h"
+
+namespace Libraries::Voice {
+
+s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceCreatePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceDeletePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceEnd() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate) {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ *bitrate = 48000;
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetMuteFlag() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetPortAttr() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info) {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ info->port_type = 0;
+ info->state = 3;
+ info->byte_count = 0;
+ info->frame_size = 1;
+ info->edge_count = 0;
+ info->reserved = 0;
+
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetResourceInfo() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceGetVolume() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceInit() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceInitHQ() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoicePausePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoicePausePortAll() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceReadFromOPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceResetPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceResumePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceResumePortAll() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetBitRate() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlag() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetThreadsParams() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceSetVolume() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceStart() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceStop() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceUpdatePort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceVADAdjustment() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceVADSetVersion() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+s32 PS4_SYSV_ABI sceVoiceWriteToIPort() {
+ LOG_ERROR(Lib_Voice, "(STUBBED) called");
+ return ORBIS_OK;
+}
+
+void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym) {
+ LIB_FUNCTION("oV9GAdJ23Gw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceConnectIPortToOPort);
+ LIB_FUNCTION("nXpje5yNpaE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceCreatePort);
+ LIB_FUNCTION("b7kJI+nx2hg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceDeletePort);
+ LIB_FUNCTION("ajVj3QG2um4", "libSceVoice", 1, "libSceVoice", 0, 0,
+ sceVoiceDisconnectIPortFromOPort);
+ LIB_FUNCTION("Oo0S5PH7FIQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceEnd);
+ LIB_FUNCTION("cJLufzou6bc", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetBitRate);
+ LIB_FUNCTION("Pc4z1QjForU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetMuteFlag);
+ LIB_FUNCTION("elcxZTEfHZM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortAttr);
+ LIB_FUNCTION("CrLqDwWLoXM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortInfo);
+ LIB_FUNCTION("Z6QV6j7igvE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetResourceInfo);
+ LIB_FUNCTION("jjkCjneOYSs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetVolume);
+ LIB_FUNCTION("9TrhuGzberQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInit);
+ LIB_FUNCTION("IPHvnM5+g04", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInitHQ);
+ LIB_FUNCTION("x0slGBQW+wY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePort);
+ LIB_FUNCTION("Dinob0yMRl8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePortAll);
+ LIB_FUNCTION("cQ6DGsQEjV4", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceReadFromOPort);
+ LIB_FUNCTION("udAxvCePkUs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResetPort);
+ LIB_FUNCTION("gAgN+HkiEzY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePort);
+ LIB_FUNCTION("jbkJFmOZ9U0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePortAll);
+ LIB_FUNCTION("TexwmOHQsDg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetBitRate);
+ LIB_FUNCTION("gwUynkEgNFY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlag);
+ LIB_FUNCTION("oUha0S-Ij9Q", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlagAll);
+ LIB_FUNCTION("clyKUyi3RYU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetThreadsParams);
+ LIB_FUNCTION("QBFoAIjJoXQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetVolume);
+ LIB_FUNCTION("54phPH2LZls", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStart);
+ LIB_FUNCTION("Ao2YNSA7-Qo", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStop);
+ LIB_FUNCTION("jSZNP7xJrcw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceUpdatePort);
+ LIB_FUNCTION("hg9T73LlRiU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADAdjustment);
+ LIB_FUNCTION("wFeAxEeEi-8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADSetVersion);
+ LIB_FUNCTION("YeJl6yDlhW0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceWriteToIPort);
+};
+
+} // namespace Libraries::Voice
\ No newline at end of file
diff --git a/src/core/libraries/voice/voice.h b/src/core/libraries/voice/voice.h
new file mode 100644
index 000000000..8f008f2cc
--- /dev/null
+++ b/src/core/libraries/voice/voice.h
@@ -0,0 +1,56 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/types.h"
+
+namespace Core::Loader {
+class SymbolsResolver;
+}
+
+namespace Libraries::Voice {
+
+struct OrbisVoicePortInfo {
+ s32 port_type;
+ s32 state;
+ u32* edge;
+ u32 byte_count;
+ u32 frame_size;
+ u16 edge_count;
+ u16 reserved;
+};
+
+s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort();
+s32 PS4_SYSV_ABI sceVoiceCreatePort();
+s32 PS4_SYSV_ABI sceVoiceDeletePort();
+s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort();
+s32 PS4_SYSV_ABI sceVoiceEnd();
+s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate);
+s32 PS4_SYSV_ABI sceVoiceGetMuteFlag();
+s32 PS4_SYSV_ABI sceVoiceGetPortAttr();
+s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info);
+s32 PS4_SYSV_ABI sceVoiceGetResourceInfo();
+s32 PS4_SYSV_ABI sceVoiceGetVolume();
+s32 PS4_SYSV_ABI sceVoiceInit();
+s32 PS4_SYSV_ABI sceVoiceInitHQ();
+s32 PS4_SYSV_ABI sceVoicePausePort();
+s32 PS4_SYSV_ABI sceVoicePausePortAll();
+s32 PS4_SYSV_ABI sceVoiceReadFromOPort();
+s32 PS4_SYSV_ABI sceVoiceResetPort();
+s32 PS4_SYSV_ABI sceVoiceResumePort();
+s32 PS4_SYSV_ABI sceVoiceResumePortAll();
+s32 PS4_SYSV_ABI sceVoiceSetBitRate();
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlag();
+s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll();
+s32 PS4_SYSV_ABI sceVoiceSetThreadsParams();
+s32 PS4_SYSV_ABI sceVoiceSetVolume();
+s32 PS4_SYSV_ABI sceVoiceStart();
+s32 PS4_SYSV_ABI sceVoiceStop();
+s32 PS4_SYSV_ABI sceVoiceUpdatePort();
+s32 PS4_SYSV_ABI sceVoiceVADAdjustment();
+s32 PS4_SYSV_ABI sceVoiceVADSetVersion();
+s32 PS4_SYSV_ABI sceVoiceWriteToIPort();
+
+void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym);
+} // namespace Libraries::Voice
\ No newline at end of file
diff --git a/src/core/libraries/zlib/zlib.cpp b/src/core/libraries/zlib/zlib.cpp
index 899cb5bf6..b304992ad 100644
--- a/src/core/libraries/zlib/zlib.cpp
+++ b/src/core/libraries/zlib/zlib.cpp
@@ -51,7 +51,7 @@ void ZlibTaskThread(const std::stop_token& stop) {
if (!task_queue_cv.wait(lock, stop, [&] { return !task_queue.empty(); })) {
break;
}
- task = task_queue.back();
+ task = task_queue.front();
task_queue.pop();
}
@@ -136,7 +136,7 @@ s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout) {
} else {
done_queue_cv.wait(lock, pred);
}
- *request_id = done_queue.back();
+ *request_id = done_queue.front();
done_queue.pop();
}
return ORBIS_OK;
diff --git a/src/core/linker.cpp b/src/core/linker.cpp
index 0f86376af..1f45caf12 100644
--- a/src/core/linker.cpp
+++ b/src/core/linker.cpp
@@ -12,6 +12,7 @@
#include "common/thread.h"
#include "core/aerolib/aerolib.h"
#include "core/aerolib/stubs.h"
+#include "core/devtools/widget/module_list.h"
#include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/threads.h"
#include "core/linker.h"
@@ -116,6 +117,18 @@ void Linker::Execute(const std::vector args) {
Common::SetCurrentThreadName("GAME_MainThread");
LoadSharedLibraries();
+ // Simulate libSceGnmDriver initialization, which maps a chunk of direct memory.
+ // Some games fail without accurately emulating this behavior.
+ s64 phys_addr{};
+ s32 result = Libraries::Kernel::sceKernelAllocateDirectMemory(
+ 0, Libraries::Kernel::sceKernelGetDirectMemorySize(), 0x10000, 0x10000, 3, &phys_addr);
+ if (result == 0) {
+ void* addr{reinterpret_cast(0xfe0000000)};
+ result = Libraries::Kernel::sceKernelMapNamedDirectMemory(
+ &addr, 0x10000, 0x13, 0, phys_addr, 0x10000, "SceGnmDriver");
+ }
+ ASSERT_MSG(result == 0, "Unable to emulate libSceGnmDriver initialization");
+
// Start main module.
EntryParams params{};
params.argc = 1;
@@ -127,7 +140,7 @@ void Linker::Execute(const std::vector args) {
}
}
params.entry_addr = module->GetEntryAddress();
- RunMainEntry(¶ms);
+ ExecuteGuest(RunMainEntry, ¶ms);
});
}
@@ -147,6 +160,9 @@ s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) {
num_static_modules += !is_dynamic;
m_modules.emplace_back(std::move(module));
+
+ Core::Devtools::Widget::ModuleList::AddModule(elf_name.filename().string(), elf_name);
+
return m_modules.size() - 1;
}
@@ -316,18 +332,22 @@ bool Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul
sr.type = sym_type;
const auto* record = m_hle_symbols.FindSymbol(sr);
- if (!record) {
- // Check if it an export function
- const auto* p = FindExportedModule(*module, *library);
- if (p && p->export_sym.GetSize() > 0) {
- record = p->export_sym.FindSymbol(sr);
- }
- }
if (record) {
*return_info = *record;
+ Core::Devtools::Widget::ModuleList::AddModule(sr.library);
return true;
}
+ // Check if it an export function
+ const auto* p = FindExportedModule(*module, *library);
+ if (p && p->export_sym.GetSize() > 0) {
+ record = p->export_sym.FindSymbol(sr);
+ if (record) {
+ *return_info = *record;
+ return true;
+ }
+ }
+
const auto aeronid = AeroLib::FindByNid(sr.name.c_str());
if (aeronid) {
return_info->name = aeronid->name;
@@ -366,7 +386,8 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
if (!addr) {
// Module was just loaded by above code. Allocate TLS block for it.
const u32 init_image_size = module->tls.init_image_size;
- u8* dest = reinterpret_cast(heap_api->heap_malloc(module->tls.image_size));
+ u8* dest = reinterpret_cast(
+ Core::ExecuteGuest(heap_api->heap_malloc, module->tls.image_size));
const u8* src = reinterpret_cast(module->tls.image_virtual_addr);
std::memcpy(dest, src, init_image_size);
std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 9861e813a..dad42347a 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -5,6 +5,7 @@
#include "common/assert.h"
#include "common/config.h"
#include "common/debug.h"
+#include "core/file_sys/fs.h"
#include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/kernel/process.h"
@@ -67,7 +68,7 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1
}
u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
- static constexpr u64 MinSizeToClamp = 512_MB;
+ static constexpr u64 MinSizeToClamp = 2_MB;
// Dont bother with clamping if the size is small so we dont pay a map lookup on every buffer.
if (size < MinSizeToClamp) {
return size;
@@ -94,6 +95,46 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
return clamped_size;
}
+void MemoryManager::SetPrtArea(u32 id, VAddr address, u64 size) {
+ PrtArea& area = prt_areas[id];
+ if (area.mapped) {
+ rasterizer->UnmapMemory(area.start, area.end - area.start);
+ }
+
+ area.start = address;
+ area.end = address + size;
+ area.mapped = true;
+
+ // Pretend the entire PRT area is mapped to avoid GPU tracking errors.
+ // The caches will use CopySparseMemory to fetch data which avoids unmapped areas.
+ rasterizer->MapMemory(address, size);
+}
+
+void MemoryManager::CopySparseMemory(VAddr virtual_addr, u8* dest, u64 size) {
+ const bool is_sparse = std::ranges::any_of(
+ prt_areas, [&](const PrtArea& area) { return area.Overlaps(virtual_addr, size); });
+ if (!is_sparse) {
+ std::memcpy(dest, std::bit_cast(virtual_addr), size);
+ return;
+ }
+
+ auto vma = FindVMA(virtual_addr);
+ ASSERT_MSG(vma->second.Contains(virtual_addr, 0),
+ "Attempted to access invalid GPU address {:#x}", virtual_addr);
+ while (size) {
+ u64 copy_size = std::min(vma->second.size - (virtual_addr - vma->first), size);
+ if (vma->second.IsFree()) {
+ std::memset(dest, 0, copy_size);
+ } else {
+ std::memcpy(dest, std::bit_cast(virtual_addr), copy_size);
+ }
+ size -= copy_size;
+ virtual_addr += copy_size;
+ dest += copy_size;
+ ++vma;
+ }
+}
+
bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_bytes) {
const VAddr virtual_addr = std::bit_cast(address);
const auto& vma = FindVMA(virtual_addr)->second;
@@ -109,31 +150,42 @@ bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_byt
PAddr MemoryManager::PoolExpand(PAddr search_start, PAddr search_end, size_t size, u64 alignment) {
std::scoped_lock lk{mutex};
+ alignment = alignment > 0 ? alignment : 64_KB;
auto dmem_area = FindDmemArea(search_start);
+ auto mapping_start = search_start > dmem_area->second.base
+ ? Common::AlignUp(search_start, alignment)
+ : Common::AlignUp(dmem_area->second.base, alignment);
+ auto mapping_end = mapping_start + size;
- const auto is_suitable = [&] {
- const auto aligned_base = alignment > 0 ? Common::AlignUp(dmem_area->second.base, alignment)
- : dmem_area->second.base;
- 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;
- return dmem_area->second.is_free && remaining_size >= size;
- };
- while (!is_suitable() && dmem_area->second.GetEnd() <= search_end) {
+ // Find the first free, large enough dmem area in the range.
+ while (!dmem_area->second.is_free || dmem_area->second.GetEnd() < mapping_end) {
+ // The current dmem_area isn't suitable, move to the next one.
dmem_area++;
- }
- ASSERT_MSG(is_suitable(), "Unable to find free direct memory area: size = {:#x}", size);
+ if (dmem_area == dmem_map.end()) {
+ break;
+ }
- // Align free position
- PAddr free_addr = dmem_area->second.base;
- free_addr = alignment > 0 ? Common::AlignUp(free_addr, alignment) : free_addr;
+ // Update local variables based on the new dmem_area
+ mapping_start = Common::AlignUp(dmem_area->second.base, alignment);
+ mapping_end = mapping_start + size;
+ }
+
+ if (dmem_area == dmem_map.end()) {
+ // There are no suitable mappings in this range
+ LOG_ERROR(Kernel_Vmm, "Unable to find free direct memory area: size = {:#x}", size);
+ return -1;
+ }
// Add the allocated region to the list and commit its pages.
- auto& area = CarveDmemArea(free_addr, size)->second;
+ auto& area = CarveDmemArea(mapping_start, size)->second;
area.is_free = false;
area.is_pooled = true;
- return free_addr;
+
+ // Track how much dmem was allocated for pools.
+ pool_budget += size;
+
+ return mapping_start;
}
PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment,
@@ -170,6 +222,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
auto& area = CarveDmemArea(mapping_start, size)->second;
area.memory_type = memory_type;
area.is_free = false;
+ MergeAdjacent(dmem_map, dmem_area);
return mapping_start;
}
@@ -203,123 +256,52 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) {
MergeAdjacent(dmem_map, dmem_area);
}
-int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size,
- MemoryMapFlags flags, u64 alignment) {
- std::scoped_lock lk{mutex};
-
- virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
- alignment = alignment > 0 ? alignment : 2_MB;
- VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
-
- // Fixed mapping means the virtual address must exactly match the provided one.
- if (True(flags & MemoryMapFlags::Fixed)) {
- auto& vma = FindVMA(mapped_addr)->second;
- // If the VMA is mapped, unmap the region first.
- if (vma.IsMapped()) {
- UnmapMemoryImpl(mapped_addr, size);
- vma = FindVMA(mapped_addr)->second;
- }
- const size_t remaining_size = vma.base + vma.size - mapped_addr;
- ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size,
- "Memory region {:#x} to {:#x} is not large enough to reserve {:#x} to {:#x}",
- vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
- }
-
- // Find the first free area starting with provided virtual address.
- if (False(flags & MemoryMapFlags::Fixed)) {
- mapped_addr = SearchFree(mapped_addr, size, alignment);
- if (mapped_addr == -1) {
- // No suitable memory areas to map to
- return ORBIS_KERNEL_ERROR_ENOMEM;
- }
- }
-
- // Add virtual memory area
- const auto new_vma_handle = CarveVMA(mapped_addr, size);
- auto& new_vma = new_vma_handle->second;
- new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
- new_vma.prot = MemoryProt::NoAccess;
- new_vma.name = "anon";
- new_vma.type = VMAType::PoolReserved;
- MergeAdjacent(vma_map, new_vma_handle);
-
- *out_addr = std::bit_cast(mapped_addr);
- return ORBIS_OK;
-}
-
-int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags,
- u64 alignment) {
- std::scoped_lock lk{mutex};
-
- virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
- alignment = alignment > 0 ? alignment : 16_KB;
- VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
-
- // Fixed mapping means the virtual address must exactly match the provided one.
- if (True(flags & MemoryMapFlags::Fixed)) {
- auto vma = FindVMA(mapped_addr)->second;
- // If the VMA is mapped, unmap the region first.
- if (vma.IsMapped()) {
- UnmapMemoryImpl(mapped_addr, size);
- vma = FindVMA(mapped_addr)->second;
- }
- const size_t remaining_size = vma.base + vma.size - mapped_addr;
- ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size,
- "Memory region {:#x} to {:#x} is not large enough to reserve {:#x} to {:#x}",
- vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
- }
-
- // Find the first free area starting with provided virtual address.
- if (False(flags & MemoryMapFlags::Fixed)) {
- mapped_addr = SearchFree(mapped_addr, size, alignment);
- if (mapped_addr == -1) {
- // No suitable memory areas to map to
- return ORBIS_KERNEL_ERROR_ENOMEM;
- }
- }
-
- // Add virtual memory area
- const auto new_vma_handle = CarveVMA(mapped_addr, size);
- auto& new_vma = new_vma_handle->second;
- new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
- new_vma.prot = MemoryProt::NoAccess;
- new_vma.name = "anon";
- new_vma.type = VMAType::Reserved;
- MergeAdjacent(vma_map, new_vma_handle);
-
- *out_addr = std::bit_cast(mapped_addr);
- return ORBIS_OK;
-}
-
int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex};
const u64 alignment = 64_KB;
- // When virtual addr is zero, force it to virtual_base. The guest cannot pass Fixed
- // flag so we will take the branch that searches for free (or reserved) mappings.
- virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
+ // Input addresses to PoolCommit are treated as fixed.
VAddr mapped_addr = Common::AlignUp(virtual_addr, alignment);
- // This should return SCE_KERNEL_ERROR_ENOMEM but shouldn't normally happen.
- const auto& vma = FindVMA(mapped_addr)->second;
- const size_t remaining_size = vma.base + vma.size - mapped_addr;
- ASSERT_MSG(!vma.IsMapped() && remaining_size >= size,
- "Memory region {:#x} to {:#x} isn't free enough to map region {:#x} to {:#x}",
- vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
+ auto& vma = FindVMA(mapped_addr)->second;
+ if (vma.type != VMAType::PoolReserved) {
+ // If we're attempting to commit non-pooled memory, return EINVAL
+ LOG_ERROR(Kernel_Vmm, "Attempting to commit non-pooled memory at {:#x}", mapped_addr);
+ return ORBIS_KERNEL_ERROR_EINVAL;
+ }
- // Perform the mapping.
- void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false);
- TRACK_ALLOC(out_addr, size, "VMEM");
+ if (!vma.Contains(mapped_addr, size)) {
+ // If there's not enough space to commit, return EINVAL
+ LOG_ERROR(Kernel_Vmm,
+ "Pooled region {:#x} to {:#x} is not large enough to commit from {:#x} to {:#x}",
+ vma.base, vma.base + vma.size, mapped_addr, mapped_addr + size);
+ return ORBIS_KERNEL_ERROR_EINVAL;
+ }
- auto& new_vma = CarveVMA(mapped_addr, size)->second;
+ if (pool_budget <= size) {
+ // If there isn't enough pooled memory to perform the mapping, return ENOMEM
+ LOG_ERROR(Kernel_Vmm, "Not enough pooled memory to perform mapping");
+ return ORBIS_KERNEL_ERROR_ENOMEM;
+ } else {
+ // Track how much pooled memory this commit will take
+ pool_budget -= size;
+ }
+
+ // Carve out the new VMA representing this mapping
+ const auto new_vma_handle = CarveVMA(mapped_addr, size);
+ auto& new_vma = new_vma_handle->second;
new_vma.disallow_merge = false;
new_vma.prot = prot;
- new_vma.name = "";
+ new_vma.name = "anon";
new_vma.type = Core::VMAType::Pooled;
new_vma.is_exec = false;
new_vma.phys_base = 0;
+ // Perform the mapping
+ void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false);
+ TRACK_ALLOC(out_addr, size, "VMEM");
+
if (IsValidGpuMapping(mapped_addr, size)) {
rasterizer->MapMemory(mapped_addr, size);
}
@@ -327,7 +309,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot)
return ORBIS_OK;
}
-int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
+s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
MemoryMapFlags flags, VMAType type, std::string_view name,
bool is_exec, PAddr phys_addr, u64 alignment) {
std::scoped_lock lk{mutex};
@@ -338,37 +320,39 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
return ORBIS_KERNEL_ERROR_ENOMEM;
}
- // When virtual addr is zero, force it to virtual_base. The guest cannot pass Fixed
- // flag so we will take the branch that searches for free (or reserved) mappings.
- virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
- alignment = alignment > 0 ? alignment : 16_KB;
- VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
+ // Limit the minumum address to SystemManagedVirtualBase to prevent hardware-specific issues.
+ VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
// Fixed mapping means the virtual address must exactly match the provided one.
- if (True(flags & MemoryMapFlags::Fixed)) {
+ // On a PS4, the Fixed flag is ignored if address 0 is provided.
+ if (True(flags & MemoryMapFlags::Fixed) && virtual_addr != 0) {
auto vma = FindVMA(mapped_addr)->second;
- size_t remaining_size = vma.base + vma.size - mapped_addr;
// There's a possible edge case where we're mapping to a partially reserved range.
// To account for this, unmap any reserved areas within this mapping range first.
auto unmap_addr = mapped_addr;
auto unmap_size = size;
- while (!vma.IsMapped() && unmap_addr < mapped_addr + size && remaining_size < size) {
+
+ // If flag NoOverwrite is provided, don't overwrite mapped VMAs.
+ // When it isn't provided, VMAs can be overwritten regardless of if they're mapped.
+ while ((False(flags & MemoryMapFlags::NoOverwrite) || !vma.IsMapped()) &&
+ unmap_addr < mapped_addr + size) {
auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size);
unmap_addr += unmapped;
unmap_size -= unmapped;
vma = FindVMA(unmap_addr)->second;
}
- // This should return SCE_KERNEL_ERROR_ENOMEM but rarely happens.
vma = FindVMA(mapped_addr)->second;
- remaining_size = vma.base + vma.size - mapped_addr;
- ASSERT_MSG(!vma.IsMapped() && remaining_size >= size,
- "Memory region {:#x} to {:#x} isn't free enough to map region {:#x} to {:#x}",
- vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
- }
-
- // Find the first free area starting with provided virtual address.
- if (False(flags & MemoryMapFlags::Fixed)) {
+ auto remaining_size = vma.base + vma.size - mapped_addr;
+ if (vma.IsMapped() || remaining_size < size) {
+ LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr);
+ return ORBIS_KERNEL_ERROR_ENOMEM;
+ }
+ } else {
+ // When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0,
+ // search from address 0x200000000 instead.
+ alignment = alignment > 0 ? alignment : 16_KB;
+ mapped_addr = virtual_addr == 0 ? 0x200000000 : mapped_addr;
mapped_addr = SearchFree(mapped_addr, size, alignment);
if (mapped_addr == -1) {
// No suitable memory areas to map to
@@ -376,33 +360,48 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
}
}
- // Perform the mapping.
- *out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
- TRACK_ALLOC(*out_addr, size, "VMEM");
+ // Create a memory area representing this mapping.
+ const auto new_vma_handle = CarveVMA(mapped_addr, size);
+ auto& new_vma = new_vma_handle->second;
- auto& new_vma = CarveVMA(mapped_addr, size)->second;
- new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
- new_vma.prot = prot;
- new_vma.name = name;
- new_vma.type = type;
- new_vma.is_exec = is_exec;
-
- if (type == VMAType::Direct) {
- new_vma.phys_base = phys_addr;
- }
+ // If type is Flexible, we need to track how much flexible memory is used here.
if (type == VMAType::Flexible) {
flexible_usage += size;
}
- if (IsValidGpuMapping(mapped_addr, size)) {
- rasterizer->MapMemory(mapped_addr, size);
+ new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
+ new_vma.prot = prot;
+ new_vma.name = name;
+ new_vma.type = type;
+ new_vma.phys_base = phys_addr == -1 ? 0 : phys_addr;
+ new_vma.is_exec = is_exec;
+
+ if (type == VMAType::Reserved) {
+ // Technically this should be done for direct and flexible mappings too,
+ // But some Windows-specific limitations make that hard to accomplish.
+ MergeAdjacent(vma_map, new_vma_handle);
}
+ if (type == VMAType::Reserved || type == VMAType::PoolReserved) {
+ // For Reserved/PoolReserved mappings, we don't perform any address space allocations.
+ // Just set out_addr to mapped_addr instead.
+ *out_addr = std::bit_cast(mapped_addr);
+ } else {
+ // If this is not a reservation, then map to GPU and address space
+ if (IsValidGpuMapping(mapped_addr, size)) {
+ rasterizer->MapMemory(mapped_addr, size);
+ }
+ *out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
+ }
+
+ TRACK_ALLOC(*out_addr, size, "VMEM");
return ORBIS_OK;
}
-int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
- MemoryMapFlags flags, uintptr_t fd, size_t offset) {
+s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
+ MemoryMapFlags flags, s32 fd, s64 phys_addr) {
+ auto* h = Common::Singleton::Instance();
+
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
const size_t size_aligned = Common::AlignUp(size, 16_KB);
@@ -423,8 +422,19 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
}
- // Map the file.
- impl.MapFile(mapped_addr, size_aligned, offset, std::bit_cast(prot), fd);
+ // Get the file to map
+ auto file = h->GetFile(fd);
+ if (file == nullptr) {
+ return ORBIS_KERNEL_ERROR_EBADF;
+ }
+
+ const auto handle = file->f.GetFileMapping();
+
+ impl.MapFile(mapped_addr, size_aligned, phys_addr, std::bit_cast(prot), handle);
+
+ if (prot >= MemoryProt::GpuRead) {
+ ASSERT_MSG(false, "Files cannot be mapped to GPU memory");
+ }
// Add virtual memory area
auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second;
@@ -438,7 +448,7 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
return ORBIS_OK;
}
-void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
+s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
std::scoped_lock lk{mutex};
const auto it = FindVMA(virtual_addr);
@@ -453,8 +463,19 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
const auto start_in_vma = virtual_addr - vma_base_addr;
const auto type = vma_base.type;
- if (IsValidGpuMapping(virtual_addr, size)) {
- rasterizer->UnmapMemory(virtual_addr, size);
+ if (type != VMAType::PoolReserved && type != VMAType::Pooled) {
+ LOG_ERROR(Kernel_Vmm, "Attempting to decommit non-pooled memory!");
+ return ORBIS_KERNEL_ERROR_EINVAL;
+ }
+
+ if (type == VMAType::Pooled) {
+ // We always map PoolCommitted memory to GPU, so unmap when decomitting.
+ if (IsValidGpuMapping(virtual_addr, size)) {
+ rasterizer->UnmapMemory(virtual_addr, size);
+ }
+
+ // Track how much pooled memory is decommitted
+ pool_budget += size;
}
// Mark region as free and attempt to coalesce it with neighbours.
@@ -464,13 +485,17 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
vma.prot = MemoryProt::NoAccess;
vma.phys_base = 0;
vma.disallow_merge = false;
- vma.name = "";
+ vma.name = "anon";
MergeAdjacent(vma_map, new_it);
- // Unmap the memory region.
- impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec,
- false, false);
- TRACK_FREE(virtual_addr, "VMEM");
+ if (type != VMAType::PoolReserved) {
+ // Unmap the memory region.
+ impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base,
+ is_exec, false, false);
+ TRACK_FREE(virtual_addr, "VMEM");
+ }
+
+ return ORBIS_OK;
}
s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
@@ -488,18 +513,16 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
const auto adjusted_size =
vma_base_size - start_in_vma < size ? vma_base_size - start_in_vma : size;
const bool has_backing = type == VMAType::Direct || type == VMAType::File;
+ const auto prot = vma_base.prot;
if (type == VMAType::Free) {
return adjusted_size;
}
+
if (type == VMAType::Flexible) {
flexible_usage -= adjusted_size;
}
- if (IsValidGpuMapping(virtual_addr, adjusted_size)) {
- rasterizer->UnmapMemory(virtual_addr, adjusted_size);
- }
-
// Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, adjusted_size);
auto& vma = new_it->second;
@@ -512,6 +535,11 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
auto& post_merge_vma = post_merge_it->second;
bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File;
if (type != VMAType::Reserved && type != VMAType::PoolReserved) {
+ // If this mapping has GPU access, unmap from GPU.
+ if (IsValidGpuMapping(virtual_addr, size)) {
+ rasterizer->UnmapMemory(virtual_addr, size);
+ }
+
// Unmap the memory region.
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size,
phys_base, is_exec, has_backing, readonly_file);
@@ -565,20 +593,8 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
vma_base.size - start_in_vma < size ? vma_base.size - start_in_vma : size;
if (vma_base.type == VMAType::Free) {
- LOG_ERROR(Kernel_Vmm, "Cannot change protection on free memory region");
- return ORBIS_KERNEL_ERROR_EINVAL;
- }
-
- // Validate protection flags
- constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
- MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
- MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
-
- MemoryProt invalid_flags = prot & ~valid_flags;
- if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) {
- LOG_ERROR(Kernel_Vmm, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}",
- u32(prot), u32(invalid_flags));
- return ORBIS_KERNEL_ERROR_EINVAL;
+ // On PS4, protecting freed memory does nothing.
+ return adjusted_size;
}
// Change protection
@@ -610,20 +626,38 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex};
+
+ // Validate protection flags
+ constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
+ MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
+ MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
+
+ MemoryProt invalid_flags = prot & ~valid_flags;
+ if (invalid_flags != MemoryProt::NoAccess) {
+ LOG_ERROR(Kernel_Vmm, "Invalid protection flags");
+ return ORBIS_KERNEL_ERROR_EINVAL;
+ }
+
+ // Align addr and size to the nearest page boundary.
+ auto aligned_addr = Common::AlignDown(addr, 16_KB);
+ auto aligned_size = Common::AlignUp(size + addr - aligned_addr, 16_KB);
+
+ // Protect all VMAs between aligned_addr and aligned_addr + aligned_size.
s64 protected_bytes = 0;
- do {
- auto it = FindVMA(addr + protected_bytes);
+ while (protected_bytes < aligned_size) {
+ auto it = FindVMA(aligned_addr + protected_bytes);
auto& vma_base = it->second;
ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds",
addr + protected_bytes);
auto result = 0;
- result = ProtectBytes(addr + protected_bytes, vma_base, size - protected_bytes, prot);
+ result = ProtectBytes(aligned_addr + protected_bytes, vma_base,
+ aligned_size - protected_bytes, prot);
if (result < 0) {
// ProtectBytes returned an error, return it
return result;
}
protected_bytes += result;
- } while (protected_bytes < size);
+ }
return ORBIS_OK;
}
@@ -741,12 +775,44 @@ int MemoryManager::DirectQueryAvailable(PAddr search_start, PAddr search_end, si
return ORBIS_OK;
}
-void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) {
- auto it = FindVMA(virtual_addr);
+s32 MemoryManager::SetDirectMemoryType(s64 phys_addr, s32 memory_type) {
+ std::scoped_lock lk{mutex};
- ASSERT_MSG(it->second.Contains(virtual_addr, size),
- "Range provided is not fully contained in vma");
- it->second.name = name;
+ auto& dmem_area = FindDmemArea(phys_addr)->second;
+
+ ASSERT_MSG(phys_addr <= dmem_area.GetEnd() && !dmem_area.is_free,
+ "Direct memory area is not mapped");
+
+ dmem_area.memory_type = memory_type;
+
+ return ORBIS_OK;
+}
+
+void MemoryManager::NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name) {
+ // Sizes are aligned up to the nearest 16_KB
+ auto aligned_size = Common::AlignUp(size, 16_KB);
+ // Addresses are aligned down to the nearest 16_KB
+ auto aligned_addr = Common::AlignDown(virtual_addr, 16_KB);
+
+ auto it = FindVMA(aligned_addr);
+ s64 remaining_size = aligned_size;
+ auto current_addr = aligned_addr;
+ while (remaining_size > 0) {
+ // Nothing needs to be done to free VMAs
+ if (!it->second.IsFree()) {
+ if (remaining_size < it->second.size) {
+ // We should split VMAs here, but this could cause trouble for Windows.
+ // Instead log a warning and name the whole VMA.
+ // it = CarveVMA(current_addr, remaining_size);
+ LOG_WARNING(Kernel_Vmm, "Trying to partially name a range");
+ }
+ auto& vma = it->second;
+ vma.name = name;
+ }
+ remaining_size -= it->second.size;
+ current_addr += it->second.size;
+ it = FindVMA(current_addr);
+ }
}
void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const {
@@ -767,6 +833,8 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment)
ASSERT_MSG(virtual_addr <= max_search_address, "Input address {:#x} is out of bounds",
virtual_addr);
+ // Align up the virtual_addr first.
+ virtual_addr = Common::AlignUp(virtual_addr, alignment);
auto it = FindVMA(virtual_addr);
// If the VMA is free and contains the requested mapping we are done.
@@ -907,4 +975,33 @@ int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut,
return ORBIS_OK;
}
+int MemoryManager::IsStack(VAddr addr, void** start, void** end) {
+ auto vma_handle = FindVMA(addr);
+ if (vma_handle == vma_map.end()) {
+ return ORBIS_KERNEL_ERROR_EINVAL;
+ }
+
+ const VirtualMemoryArea& vma = vma_handle->second;
+ if (!vma.Contains(addr, 0) || vma.IsFree()) {
+ return ORBIS_KERNEL_ERROR_EACCES;
+ }
+
+ auto stack_start = 0ul;
+ auto stack_end = 0ul;
+ if (vma.type == VMAType::Stack) {
+ stack_start = vma.base;
+ stack_end = vma.base + vma.size;
+ }
+
+ if (start != nullptr) {
+ *start = reinterpret_cast(stack_start);
+ }
+
+ if (end != nullptr) {
+ *end = reinterpret_cast(stack_end);
+ }
+
+ return ORBIS_OK;
+}
+
} // namespace Core
diff --git a/src/core/memory.h b/src/core/memory.h
index 3a204eb96..6a9b29382 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -75,6 +75,9 @@ struct DirectMemoryArea {
if (base + size != next.base) {
return false;
}
+ if (memory_type != next.memory_type) {
+ return false;
+ }
if (is_free != next.is_free) {
return false;
}
@@ -172,6 +175,10 @@ public:
u64 ClampRangeSize(VAddr virtual_addr, u64 size);
+ void SetPrtArea(u32 id, VAddr address, u64 size);
+
+ void CopySparseMemory(VAddr source, u8* dest, u64 size);
+
bool TryWriteBacking(void* address, const void* data, u32 num_bytes);
void SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1, bool use_extended_mem2);
@@ -183,22 +190,16 @@ public:
void Free(PAddr phys_addr, size_t size);
- int PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags,
- u64 alignment = 0);
-
- int Reserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags,
- u64 alignment = 0);
-
int PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot);
- int MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
+ s32 MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
MemoryMapFlags flags, VMAType type, std::string_view name = "anon",
bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0);
- int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
- MemoryMapFlags flags, uintptr_t fd, size_t offset);
+ s32 MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
+ MemoryMapFlags flags, s32 fd, s64 phys_addr);
- void PoolDecommit(VAddr virtual_addr, size_t size);
+ s32 PoolDecommit(VAddr virtual_addr, size_t size);
s32 UnmapMemory(VAddr virtual_addr, size_t size);
@@ -219,10 +220,14 @@ public:
int GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut, void** directMemoryStartOut,
void** directMemoryEndOut);
- void NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name);
+ s32 SetDirectMemoryType(s64 phys_addr, s32 memory_type);
+
+ void NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name);
void InvalidateMemory(VAddr addr, u64 size) const;
+ int IsStack(VAddr addr, void** start, void** end);
+
private:
VMAHandle FindVMA(VAddr target) {
return std::prev(vma_map.upper_bound(target));
@@ -274,8 +279,21 @@ private:
size_t total_direct_size{};
size_t total_flexible_size{};
size_t flexible_usage{};
+ size_t pool_budget{};
Vulkan::Rasterizer* rasterizer{};
+ struct PrtArea {
+ VAddr start;
+ VAddr end;
+ bool mapped;
+
+ bool Overlaps(VAddr test_address, u64 test_size) const {
+ const VAddr overlap_end = test_address + test_size;
+ return start < overlap_end && test_address < end;
+ }
+ };
+ std::array prt_areas{};
+
friend class ::Core::Devtools::Widget::MemoryMapViewer;
};
diff --git a/src/core/signals.cpp b/src/core/signals.cpp
index e47a78cd2..4099ac237 100644
--- a/src/core/signals.cpp
+++ b/src/core/signals.cpp
@@ -11,6 +11,7 @@
#include
#else
#include
+#include
#ifdef ARCH_X86_64
#include
#endif
diff --git a/src/core/signals.h b/src/core/signals.h
index 6ee525e10..0409b73ae 100644
--- a/src/core/signals.h
+++ b/src/core/signals.h
@@ -5,6 +5,7 @@
#include
#include "common/singleton.h"
+#include "common/types.h"
namespace Core {
diff --git a/src/core/tls.cpp b/src/core/tls.cpp
index e13c683e1..0d1d514cf 100644
--- a/src/core/tls.cpp
+++ b/src/core/tls.cpp
@@ -51,7 +51,7 @@ Tcb* GetTcbBase() {
// Apple x86_64
// Reserve space in the 32-bit address range for allocating TCB pages.
-asm(".zerofill TCB_SPACE,TCB_SPACE,__guest_system,0x3FC000");
+asm(".zerofill TCB_SPACE,TCB_SPACE,__tcb_space,0x3FC000");
struct LdtPage {
void* tcb;
diff --git a/src/core/tls.h b/src/core/tls.h
index 46ca8153b..470553d85 100644
--- a/src/core/tls.h
+++ b/src/core/tls.h
@@ -3,10 +3,9 @@
#pragma once
+#include
#include "common/types.h"
-void* memset(void* ptr, int value, size_t num);
-
namespace Xbyak {
class CodeGenerator;
}
@@ -54,8 +53,20 @@ template
ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) {
EnsureThreadInitialized();
// clear stack to avoid trash from EnsureThreadInitialized
- ClearStack<13_KB>();
+ ClearStack<12_KB>();
return func(std::forward(args)...);
}
+template
+struct HostCallWrapperImpl;
+
+template
+struct HostCallWrapperImpl {
+ static ReturnType PS4_SYSV_ABI wrap(Args... args) {
+ return func(args...);
+ }
+};
+
+#define HOST_CALL(func) (Core::HostCallWrapperImpl::wrap)
+
} // namespace Core
diff --git a/src/emulator.cpp b/src/emulator.cpp
index 318de929c..bb0318070 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
#include
#include
@@ -25,6 +26,7 @@
#include "common/polyfill_thread.h"
#include "common/scm_rev.h"
#include "common/singleton.h"
+#include "core/devtools/widget/module_list.h"
#include "core/file_format/psf.h"
#include "core/file_format/trp.h"
#include "core/file_sys/fs.h"
@@ -61,14 +63,19 @@ Emulator::~Emulator() {
Config::saveMainWindow(config_dir / "config.toml");
}
-void Emulator::Run(const std::filesystem::path& file, const std::vector args) {
+void Emulator::Run(std::filesystem::path file, const std::vector args) {
+ if (std::filesystem::is_directory(file)) {
+ file /= "eboot.bin";
+ }
+
const auto eboot_name = file.filename().string();
+
auto game_folder = file.parent_path();
if (const auto game_folder_name = game_folder.filename().string();
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) {
// If an executable was launched from a separate update directory,
// use the base game directory as the game folder.
- const auto base_name = game_folder_name.substr(0, game_folder_name.size() - 7);
+ const std::string base_name = game_folder_name.substr(0, game_folder_name.rfind('-'));
const auto base_path = game_folder.parent_path() / base_name;
if (std::filesystem::is_directory(base_path)) {
game_folder = base_path;
@@ -113,6 +120,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector", id, title, app_version);
std::string window_title = "";
+ std::string remote_url(Common::g_scm_remote_url);
+ std::string remote_host = Common::GetRemoteNameFromLink();
if (Common::g_is_release) {
- window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title);
- } else {
- std::string remote_url(Common::g_scm_remote_url);
- std::string remote_host;
- try {
- remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
- } catch (...) {
- remote_host = "unknown";
+ if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
+ window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title);
+ } else {
+ window_title =
+ fmt::format("shadPS4 {}/v{} | {}", remote_host, Common::g_version, game_title);
}
+ } else {
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
window_title = fmt::format("shadPS4 v{} {} {} | {}", Common::g_version,
Common::g_scm_branch, Common::g_scm_desc, game_title);
@@ -252,7 +266,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/" + eboot_name);
- linker->LoadModule(eboot_path);
+ if (linker->LoadModule(eboot_path) == -1) {
+ LOG_CRITICAL(Loader, "Failed to load game's eboot.bin: {}",
+ std::filesystem::absolute(eboot_path).string());
+ std::quick_exit(0);
+ }
// check if we have system modules to load
LoadSystemModules(game_info.game_serial);
diff --git a/src/emulator.h b/src/emulator.h
index 08c2807a1..257ccd694 100644
--- a/src/emulator.h
+++ b/src/emulator.h
@@ -25,7 +25,7 @@ public:
Emulator();
~Emulator();
- void Run(const std::filesystem::path& file, const std::vector args = {});
+ void Run(std::filesystem::path file, const std::vector args = {});
void UpdatePlayTime(const std::string& serial);
private:
diff --git a/src/images/KBM.png b/src/images/KBM.png
index 37f52d549..feab9fa0f 100644
Binary files a/src/images/KBM.png and b/src/images/KBM.png differ
diff --git a/src/input/controller.cpp b/src/input/controller.cpp
index bb8db9a7c..42cabb837 100644
--- a/src/input/controller.cpp
+++ b/src/input/controller.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include
@@ -165,69 +165,37 @@ void GameController::Acceleration(int id, const float acceleration[3]) {
AddState(state);
}
-// Stolen from
-// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
-float eInt[3] = {0.0f, 0.0f, 0.0f}; // Integral error terms
-const float Kp = 50.0f; // Proportional gain
-const float Ki = 1.0f; // Integral gain
-Libraries::Pad::OrbisFQuaternion o = {1, 0, 0, 0};
void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
+ Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation) {
- float ax = acceleration.x, ay = acceleration.y, az = acceleration.z;
- float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z;
+ Libraries::Pad::OrbisFQuaternion q = lastOrientation;
+ Libraries::Pad::OrbisFQuaternion ω = {angularVelocity.x, angularVelocity.y, angularVelocity.z,
+ 0.0f};
- float q1 = o.w, q2 = o.x, q3 = o.y, q4 = o.z;
+ Libraries::Pad::OrbisFQuaternion qω = {q.w * ω.x + q.x * ω.w + q.y * ω.z - q.z * ω.y,
+ q.w * ω.y + q.y * ω.w + q.z * ω.x - q.x * ω.z,
+ q.w * ω.z + q.z * ω.w + q.x * ω.y - q.y * ω.x,
+ q.w * ω.w - q.x * ω.x - q.y * ω.y - q.z * ω.z};
- // Normalize accelerometer measurement
- float norm = std::sqrt(ax * ax + ay * ay + az * az);
- if (norm == 0.0f || deltaTime == 0.0f)
- return; // Handle NaN
- norm = 1.0f / norm;
- ax *= norm;
- ay *= norm;
- az *= norm;
+ Libraries::Pad::OrbisFQuaternion qDot = {0.5f * qω.x, 0.5f * qω.y, 0.5f * qω.z, 0.5f * qω.w};
- // Estimated direction of gravity
- float vx = 2.0f * (q2 * q4 - q1 * q3);
- float vy = 2.0f * (q1 * q2 + q3 * q4);
- float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
+ q.x += qDot.x * deltaTime;
+ q.y += qDot.y * deltaTime;
+ q.z += qDot.z * deltaTime;
+ q.w += qDot.w * deltaTime;
- // Error is cross product between estimated direction and measured direction of gravity
- float ex = (ay * vz - az * vy);
- float ey = (az * vx - ax * vz);
- float ez = (ax * vy - ay * vx);
- if (Ki > 0.0f) {
- eInt[0] += ex * deltaTime; // Accumulate integral error
- eInt[1] += ey * deltaTime;
- eInt[2] += ez * deltaTime;
- } else {
- eInt[0] = eInt[1] = eInt[2] = 0.0f; // Prevent integral wind-up
- }
+ float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
+ q.x /= norm;
+ q.y /= norm;
+ q.z /= norm;
+ q.w /= norm;
- // Apply feedback terms
- gx += Kp * ex + Ki * eInt[0];
- gy += Kp * ey + Ki * eInt[1];
- gz += Kp * ez + Ki * eInt[2];
-
- //// Integrate rate of change of quaternion
- q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime);
- q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime);
- q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime);
- q4 += (q1 * gz + q2 * gy - q3 * gx) * (0.5f * deltaTime);
-
- // Normalize quaternion
- norm = std::sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
- norm = 1.0f / norm;
- orientation.w = q1 * norm;
- orientation.x = q2 * norm;
- orientation.y = q3 * norm;
- orientation.z = q4 * norm;
- o.w = q1 * norm;
- o.x = q2 * norm;
- o.y = q3 * norm;
- o.z = q4 * norm;
+ orientation.x = q.x;
+ orientation.y = q.y;
+ orientation.z = q.z;
+ orientation.w = q.w;
LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x,
orientation.y, orientation.z, orientation.w);
}
@@ -260,6 +228,69 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f
}
}
+u8 GameController::GetTouchCount() {
+ std::scoped_lock lock{m_mutex};
+ return m_touch_count;
+}
+
+void GameController::SetTouchCount(u8 touchCount) {
+ std::scoped_lock lock{m_mutex};
+ m_touch_count = touchCount;
+}
+
+u8 GameController::GetSecondaryTouchCount() {
+ std::scoped_lock lock{m_mutex};
+ return m_secondary_touch_count;
+}
+
+void GameController::SetSecondaryTouchCount(u8 touchCount) {
+ std::scoped_lock lock{m_mutex};
+ m_secondary_touch_count = touchCount;
+ if (touchCount == 0) {
+ m_was_secondary_reset = true;
+ }
+}
+
+u8 GameController::GetPreviousTouchNum() {
+ std::scoped_lock lock{m_mutex};
+ return m_previous_touchnum;
+}
+
+void GameController::SetPreviousTouchNum(u8 touchNum) {
+ std::scoped_lock lock{m_mutex};
+ m_previous_touchnum = touchNum;
+}
+
+bool GameController::WasSecondaryTouchReset() {
+ std::scoped_lock lock{m_mutex};
+ return m_was_secondary_reset;
+}
+
+void GameController::UnsetSecondaryTouchResetBool() {
+ std::scoped_lock lock{m_mutex};
+ m_was_secondary_reset = false;
+}
+
+void GameController::SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation) {
+ std::scoped_lock lock{m_mutex};
+ m_orientation = orientation;
+}
+
+Libraries::Pad::OrbisFQuaternion GameController::GetLastOrientation() {
+ std::scoped_lock lock{m_mutex};
+ return m_orientation;
+}
+
+std::chrono::steady_clock::time_point GameController::GetLastUpdate() {
+ std::scoped_lock lock{m_mutex};
+ return m_last_update;
+}
+
+void GameController::SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate) {
+ std::scoped_lock lock{m_mutex};
+ m_last_update = lastUpdate;
+}
+
void GameController::SetEngine(std::unique_ptr engine) {
std::scoped_lock _{m_mutex};
m_engine = std::move(engine);
diff --git a/src/input/controller.h b/src/input/controller.h
index bbaed75ea..f427a55ec 100644
--- a/src/input/controller.h
+++ b/src/input/controller.h
@@ -23,6 +23,7 @@ enum class Axis {
};
struct TouchpadEntry {
+ u8 ID = 0;
bool state{};
u16 x{};
u16 y{};
@@ -82,9 +83,23 @@ public:
Engine* GetEngine();
u32 Poll();
+ u8 GetTouchCount();
+ void SetTouchCount(u8 touchCount);
+ u8 GetSecondaryTouchCount();
+ void SetSecondaryTouchCount(u8 touchCount);
+ u8 GetPreviousTouchNum();
+ void SetPreviousTouchNum(u8 touchNum);
+ bool WasSecondaryTouchReset();
+ void UnsetSecondaryTouchResetBool();
+
+ void SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation);
+ Libraries::Pad::OrbisFQuaternion GetLastOrientation();
+ std::chrono::steady_clock::time_point GetLastUpdate();
+ void SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate);
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
+ Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation);
private:
@@ -98,8 +113,15 @@ private:
int m_connected_count = 0;
u32 m_states_num = 0;
u32 m_first_state = 0;
+ u8 m_touch_count = 0;
+ u8 m_secondary_touch_count = 0;
+ u8 m_previous_touch_count = 0;
+ u8 m_previous_touchnum = 0;
+ bool m_was_secondary_reset = false;
std::array m_states;
std::array m_private;
+ std::chrono::steady_clock::time_point m_last_update = {};
+ Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f};
std::unique_ptr m_engine = nullptr;
};
diff --git a/src/input/input_mouse.cpp b/src/input/input_mouse.cpp
index c84d14b3f..5eb0aab3e 100644
--- a/src/input/input_mouse.cpp
+++ b/src/input/input_mouse.cpp
@@ -60,7 +60,7 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) {
float angle = atan2(d_y, d_x);
float a_x = cos(angle) * output_speed, a_y = sin(angle) * output_speed;
- if (d_x != 0 && d_y != 0) {
+ if (d_x != 0 || d_y != 0) {
controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, a_x));
controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, a_y));
} else {
diff --git a/src/main.cpp b/src/main.cpp
index 85581774b..fe245d104 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -35,17 +35,20 @@ int main(int argc, char* argv[]) {
std::unordered_map> arg_map = {
{"-h",
[&](int&) {
- std::cout << "Usage: shadps4 [options] \n"
- "Options:\n"
- " -g, --game