diff --git a/.github/ISSUE_TEMPLATE/game-bug-report.yaml b/.github/ISSUE_TEMPLATE/game-bug-report.yaml
index a9c669ff9..d9ebd8347 100644
--- a/.github/ISSUE_TEMPLATE/game-bug-report.yaml
+++ b/.github/ISSUE_TEMPLATE/game-bug-report.yaml
@@ -35,7 +35,7 @@ body:
required: true
- label: I have disabled all patches and cheats and the issue is still present.
required: true
- - label: I have all the required [system modules](https://github.com/shadps4-emu/shadps4-game-compatibility?tab=readme-ov-file#informations) installed.
+ - label: I have all the required [system modules](https://github.com/shadps4-emu/shadPS4/wiki/I.-Quick-start-%5BUsers%5D#4-adding-modules) installed.
required: true
- type: textarea
id: desc
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ceb915f6a..b098896f5 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,13 +218,16 @@ 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
archives: qtbase qttools
modules: qtmultimedia
-
+
+ - name: Workaround Qt <=6.9.1 issue
+ run: sed -i '' '/target_link_libraries(WrapOpenGL::WrapOpenGL INTERFACE ${__opengl_agl_fw_path})/d' ${{env.QT_ROOT_DIR}}/lib/cmake/Qt6/FindWrapOpenGL.cmake
+
- name: Cache CMake Configuration
uses: actions/cache@v4
env:
@@ -247,7 +240,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 +294,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 +355,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 +402,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 +438,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 +487,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 +523,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/CMakeLinuxPresets.json b/CMakeLinuxPresets.json
new file mode 100644
index 000000000..5c820774c
--- /dev/null
+++ b/CMakeLinuxPresets.json
@@ -0,0 +1,21 @@
+{
+ "version": 9,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 30,
+ "patch": 0
+ },
+ "configurePresets": [
+ {
+ "name": "x64-Clang-Base",
+ "hidden": true,
+ "generator": "Ninja",
+ "binaryDir": "${sourceDir}/Build/${presetName}",
+ "cacheVariables": {
+ "CMAKE_C_COMPILER": "clang",
+ "CMAKE_CXX_COMPILER": "clang++",
+ "CMAKE_INSTALL_PREFIX": "${sourceDir}/Build/${presetName}"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f989c4dab..91829261d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -203,7 +203,7 @@ execute_process(
# Set Version
set(EMULATOR_VERSION_MAJOR "0")
-set(EMULATOR_VERSION_MINOR "9")
+set(EMULATOR_VERSION_MINOR "10")
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}")
@@ -214,6 +214,10 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_
message("end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}")
+if(WIN32 AND ENABLE_QT_GUI AND NOT CMAKE_PREFIX_PATH)
+ include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/DetectQtInstallation.cmake")
+endif ()
+
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
find_package(Boost 1.84.0 CONFIG)
find_package(FFmpeg 5.1.2 MODULE)
@@ -296,6 +300,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
@@ -607,6 +613,8 @@ set(CAMERA_LIBS src/core/libraries/camera/camera.cpp
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
@@ -653,6 +661,7 @@ set(COMMON src/common/logging/backend.cpp
src/common/arch.h
src/common/assert.cpp
src/common/assert.h
+ src/common/bit_array.h
src/common/bit_field.h
src/common/bounded_threadsafe_queue.h
src/common/concepts.h
@@ -678,11 +687,13 @@ set(COMMON src/common/logging/backend.cpp
src/common/path_util.h
src/common/object_pool.h
src/common/polyfill_thread.h
+ src/common/range_lock.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/shared_first_mutex.h
src/common/signal_context.h
src/common/signal_context.cpp
src/common/singleton.h
@@ -870,6 +881,7 @@ 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
@@ -912,9 +924,10 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/buffer_cache/buffer.h
src/video_core/buffer_cache/buffer_cache.cpp
src/video_core/buffer_cache/buffer_cache.h
- src/video_core/buffer_cache/memory_tracker_base.h
+ src/video_core/buffer_cache/memory_tracker.h
src/video_core/buffer_cache/range_set.h
- src/video_core/buffer_cache/word_manager.h
+ src/video_core/buffer_cache/region_definitions.h
+ src/video_core/buffer_cache/region_manager.h
src/video_core/renderer_vulkan/liverpool_to_vk.cpp
src/video_core/renderer_vulkan/liverpool_to_vk.h
src/video_core/renderer_vulkan/vk_common.cpp
@@ -951,6 +964,10 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/renderer_vulkan/host_passes/fsr_pass.h
src/video_core/renderer_vulkan/host_passes/pp_pass.cpp
src/video_core/renderer_vulkan/host_passes/pp_pass.h
+ src/video_core/texture_cache/blit_helper.cpp
+ src/video_core/texture_cache/blit_helper.h
+ src/video_core/texture_cache/host_compatibility.cpp
+ src/video_core/texture_cache/host_compatibility.h
src/video_core/texture_cache/image.cpp
src/video_core/texture_cache/image.h
src/video_core/texture_cache/image_info.cpp
@@ -964,7 +981,6 @@ 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.h
src/video_core/page_manager.cpp
src/video_core/page_manager.h
src/video_core/multi_level_page_table.h
@@ -1053,6 +1069,12 @@ 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
+ src/qt_gui/sdl_event_wrapper.cpp
+ src/qt_gui/sdl_event_wrapper.h
${EMULATOR}
${RESOURCE_FILES}
${TRANSLATIONS}
@@ -1118,6 +1140,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})
@@ -1128,9 +1154,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}
@@ -1145,17 +1168,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)
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 000000000..bd1aba36e
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,62 @@
+{
+ "version": 9,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 30,
+ "patch": 0
+ },
+ "include": ["CMake${hostSystemName}Presets.json"],
+ "configurePresets": [
+ {
+ "name": "x64-Clang-Debug",
+ "displayName": "Clang x64 Debug",
+ "inherits": ["x64-Clang-Base"],
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug"
+ }
+ },
+ {
+ "name": "x64-Clang-Debug-Qt",
+ "displayName": "Clang x64 Debug with Qt",
+ "inherits": ["x64-Clang-Base"],
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug",
+ "ENABLE_QT_GUI": "ON"
+ }
+ },
+ {
+ "name": "x64-Clang-Release",
+ "displayName": "Clang x64 Release",
+ "inherits": ["x64-Clang-Base"],
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release"
+ }
+ },
+ {
+ "name": "x64-Clang-Release-Qt",
+ "displayName": "Clang x64 Release with Qt",
+ "inherits": ["x64-Clang-Base"],
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release",
+ "ENABLE_QT_GUI": "ON"
+ }
+ },
+ {
+ "name": "x64-Clang-RelWithDebInfo",
+ "displayName": "Clang x64 RelWithDebInfo",
+ "inherits": ["x64-Clang-Base"],
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "RelWithDebInfo"
+ }
+ },
+ {
+ "name": "x64-Clang-RelWithDebInfo-Qt",
+ "displayName": "Clang x64 RelWithDebInfo with Qt",
+ "inherits": ["x64-Clang-Base"],
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "RelWithDebInfo",
+ "ENABLE_QT_GUI": "ON"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/CMakeSettings.json b/CMakeSettings.json
index bb522fcfc..e1ed36887 100644
--- a/CMakeSettings.json
+++ b/CMakeSettings.json
@@ -12,6 +12,18 @@
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
},
+ {
+ "name": "x64-Clang-Release-Qt",
+ "generator": "Ninja",
+ "configurationType": "Release",
+ "buildRoot": "${projectDir}\\Build\\${name}",
+ "installRoot": "${projectDir}\\Install\\${name}",
+ "cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
+ "buildCommandArgs": "",
+ "ctestCommandArgs": "",
+ "inheritEnvironments": [ "clang_cl_x64_x64" ],
+ "intelliSenseMode": "windows-clang-x64"
+ },
{
"name": "x64-Clang-Debug",
"generator": "Ninja",
@@ -24,6 +36,18 @@
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
},
+ {
+ "name": "x64-Clang-Debug-Qt",
+ "generator": "Ninja",
+ "configurationType": "Debug",
+ "buildRoot": "${projectDir}\\Build\\${name}",
+ "installRoot": "${projectDir}\\Install\\${name}",
+ "cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
+ "buildCommandArgs": "",
+ "ctestCommandArgs": "",
+ "inheritEnvironments": [ "clang_cl_x64_x64" ],
+ "intelliSenseMode": "windows-clang-x64"
+ },
{
"name": "x64-Clang-RelWithDebInfo",
"generator": "Ninja",
@@ -35,6 +59,18 @@
"ctestCommandArgs": "",
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
+ },
+ {
+ "name": "x64-Clang-RelWithDebInfo-Qt",
+ "generator": "Ninja",
+ "configurationType": "RelWithDebInfo",
+ "buildRoot": "${projectDir}\\Build\\${name}",
+ "installRoot": "${projectDir}\\Install\\${name}",
+ "cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
+ "buildCommandArgs": "",
+ "ctestCommandArgs": "",
+ "inheritEnvironments": [ "clang_cl_x64_x64" ],
+ "intelliSenseMode": "windows-clang-x64"
}
]
}
\ No newline at end of file
diff --git a/CMakeWindowsPresets.json b/CMakeWindowsPresets.json
new file mode 100644
index 000000000..605fbfa94
--- /dev/null
+++ b/CMakeWindowsPresets.json
@@ -0,0 +1,26 @@
+{
+ "version": 9,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 30,
+ "patch": 0
+ },
+ "configurePresets": [
+ {
+ "name": "x64-Clang-Base",
+ "hidden": true,
+ "generator": "Ninja",
+ "binaryDir": "${sourceDir}/Build/${presetName}",
+ "cacheVariables": {
+ "CMAKE_C_COMPILER": "clang-cl",
+ "CMAKE_CXX_COMPILER": "clang-cl",
+ "CMAKE_INSTALL_PREFIX": "${sourceDir}/Build/${presetName}"
+ },
+ "vendor": {
+ "microsoft.com/VisualStudioSettings/CMake/1.0": {
+ "intelliSenseMode": "windows-clang-x64"
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
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/REUSE.toml b/REUSE.toml
index 662987611..4b1c94d21 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -5,10 +5,13 @@ path = [
"REUSE.toml",
"crowdin.yml",
"CMakeSettings.json",
+ "CMakeLinuxPresets.json",
+ "CMakeWindowsPresets.json",
+ "CMakePresets.json",
".github/FUNDING.yml",
".github/shadps4.png",
- ".github/workflows/scripts/update_translation.sh",
- ".github/workflows/update_translation.yml",
+ ".github/workflows/scripts/update_translation.sh",
+ ".github/workflows/update_translation.yml",
".gitmodules",
"dist/MacOSBundleInfo.plist.in",
"dist/net.shadps4.shadPS4.desktop",
@@ -29,6 +32,7 @@ path = [
"src/images/discord.png",
"src/images/dump_icon.png",
"src/images/exit_icon.png",
+ "src/images/favorite_icon.png",
"src/images/file_icon.png",
"src/images/trophy_icon.png",
"src/images/flag_china.png",
@@ -71,7 +75,7 @@ path = [
"src/images/youtube.svg",
"src/shadps4.qrc",
"src/shadps4.rc",
- "src/qt_gui/translations/update_translation.sh",
+ "src/qt_gui/translations/update_translation.sh",
]
precedence = "aggregate"
SPDX-FileCopyrightText = "shadPS4 Emulator Project"
diff --git a/cmake/DetectQtInstallation.cmake b/cmake/DetectQtInstallation.cmake
new file mode 100644
index 000000000..650cc9745
--- /dev/null
+++ b/cmake/DetectQtInstallation.cmake
@@ -0,0 +1,28 @@
+# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+set(highest_version "0")
+set(CANDIDATE_DRIVES A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
+
+foreach(drive ${CANDIDATE_DRIVES})
+ file(GLOB kits LIST_DIRECTORIES true CONFIGURE_DEPENDS "${drive}:/Qt/*/msvc*_64")
+ foreach(kit IN LISTS kits)
+ get_filename_component(version_dir "${kit}" DIRECTORY)
+ get_filename_component(kit_version "${version_dir}" NAME)
+
+ message(STATUS "DetectQtInstallation.cmake: Detected Qt: ${kit}")
+
+ if (kit_version VERSION_GREATER highest_version)
+ set(highest_version "${kit_version}")
+ set(QT_PREFIX "${kit}")
+
+ endif()
+ endforeach()
+endforeach()
+
+if(QT_PREFIX)
+ set(CMAKE_PREFIX_PATH "${QT_PREFIX}" CACHE PATH "Qt prefix auto‑detected" FORCE)
+ message(STATUS "DetectQtInstallation.cmake: Choose newest Qt: ${QT_PREFIX}")
+else()
+ message(STATUS "DetectQtInstallation.cmake: No Qt‑Directory found in :/Qt – please set CMAKE_PREFIX_PATH manually")
+endif()
diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml
index 493dc0df6..f9bd7c7c2 100644
--- a/dist/net.shadps4.shadPS4.metainfo.xml
+++ b/dist/net.shadps4.shadPS4.metainfo.xml
@@ -37,6 +37,9 @@
Game
+
+ https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.10.0
+
https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.9.0
diff --git a/documents/building-linux.md b/documents/building-linux.md
index cdc8ba12f..00d73280e 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
@@ -74,6 +74,7 @@ and install the dependencies on that container as cited above.
This option is **highly recommended** for distributions with immutable/atomic filesystems (example: Fedora Kinoite, SteamOS).
### Cloning
+The project uses submodules to manage dependencies, and they need to be initialized before you can build the project. To achieve this, make sure you've cloned the repository with the --recursive flag
```bash
git clone --recursive https://github.com/shadps4-emu/shadPS4.git
diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK
index 3a0b07a24..00abd384c 160000
--- a/externals/MoltenVK/MoltenVK
+++ b/externals/MoltenVK/MoltenVK
@@ -1 +1 @@
-Subproject commit 3a0b07a24a4a681ffe70b461b1f4333b2729e2ef
+Subproject commit 00abd384ce01cbd439045905d2fa6cf799dfa2f6
diff --git a/externals/MoltenVK/SPIRV-Cross b/externals/MoltenVK/SPIRV-Cross
index 969e75f7c..1a69a919f 160000
--- a/externals/MoltenVK/SPIRV-Cross
+++ b/externals/MoltenVK/SPIRV-Cross
@@ -1 +1 @@
-Subproject commit 969e75f7cc0718774231d029f9d52fa87d4ae1b2
+Subproject commit 1a69a919fa302e92b337594bd0a8aaea61037d91
diff --git a/externals/sirit b/externals/sirit
index 6b450704f..b4eccb336 160000
--- a/externals/sirit
+++ b/externals/sirit
@@ -1 +1 @@
-Subproject commit 6b450704f6fedb9413d0c89a9eb59d028eb1e6c0
+Subproject commit b4eccb336f1b1169af48dac1e04015985af86e3e
diff --git a/src/common/adaptive_mutex.h b/src/common/adaptive_mutex.h
index f174f5996..2ab385bdb 100644
--- a/src/common/adaptive_mutex.h
+++ b/src/common/adaptive_mutex.h
@@ -18,6 +18,9 @@ public:
void unlock() {
pthread_mutex_unlock(&mutex);
}
+ [[nodiscard]] bool try_lock() {
+ return pthread_mutex_trylock(&mutex) == 0;
+ }
private:
pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
diff --git a/src/common/bit_array.h b/src/common/bit_array.h
new file mode 100644
index 000000000..0ab464390
--- /dev/null
+++ b/src/common/bit_array.h
@@ -0,0 +1,406 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+#include
+#include "common/types.h"
+
+#ifdef __AVX2__
+#define BIT_ARRAY_USE_AVX
+#include
+#endif
+
+namespace Common {
+
+template
+class BitArray {
+ static_assert(N % 64 == 0, "BitArray size must be a multiple of 64 bits.");
+
+ static constexpr size_t BITS_PER_WORD = 64;
+ static constexpr size_t WORD_COUNT = N / BITS_PER_WORD;
+ static constexpr size_t WORDS_PER_AVX = 4;
+ static constexpr size_t AVX_WORD_COUNT = WORD_COUNT / WORDS_PER_AVX;
+
+public:
+ using Range = std::pair;
+
+ class Iterator {
+ public:
+ explicit Iterator(const BitArray& bit_array_, u64 start) : bit_array(bit_array_) {
+ range = bit_array.FirstRangeFrom(start);
+ }
+
+ Iterator& operator++() {
+ range = bit_array.FirstRangeFrom(range.second);
+ return *this;
+ }
+
+ bool operator==(const Iterator& other) const {
+ return range == other.range;
+ }
+
+ bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+
+ const Range& operator*() const {
+ return range;
+ }
+
+ const Range* operator->() const {
+ return ⦥
+ }
+
+ private:
+ const BitArray& bit_array;
+ Range range;
+ };
+
+ using const_iterator = Iterator;
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = Range;
+ using difference_type = std::ptrdiff_t;
+ using pointer = const Range*;
+ using reference = const Range&;
+
+ BitArray() = default;
+ BitArray(const BitArray& other) = default;
+ BitArray& operator=(const BitArray& other) = default;
+ BitArray(BitArray&& other) noexcept = default;
+ BitArray& operator=(BitArray&& other) noexcept = default;
+ ~BitArray() = default;
+
+ BitArray(const BitArray& other, size_t start, size_t end) {
+ if (start >= end || end > N) {
+ return;
+ }
+ const size_t first_word = start / BITS_PER_WORD;
+ const size_t last_word = (end - 1) / BITS_PER_WORD;
+ const size_t start_bit = start % BITS_PER_WORD;
+ const size_t end_bit = (end - 1) % BITS_PER_WORD;
+ const u64 start_mask = ~((1ULL << start_bit) - 1);
+ const u64 end_mask = end_bit == BITS_PER_WORD - 1 ? ~0ULL : (1ULL << (end_bit + 1)) - 1;
+ if (first_word == last_word) {
+ data[first_word] = other.data[first_word] & (start_mask & end_mask);
+ } else {
+ data[first_word] = other.data[first_word] & start_mask;
+ size_t i = first_word + 1;
+#ifdef BIT_ARRAY_USE_AVX
+ for (; i + WORDS_PER_AVX <= last_word; i += WORDS_PER_AVX) {
+ const __m256i current =
+ _mm256_loadu_si256(reinterpret_cast(&other.data[i]));
+ _mm256_storeu_si256(reinterpret_cast<__m256i*>(&data[i]), current);
+ }
+#endif
+ for (; i < last_word; ++i) {
+ data[i] = other.data[i];
+ }
+ data[last_word] = other.data[last_word] & end_mask;
+ }
+ }
+
+ BitArray(const BitArray& other, const Range& range)
+ : BitArray(other, range.first, range.second) {}
+
+ const_iterator begin() const {
+ return Iterator(*this, 0);
+ }
+ const_iterator end() const {
+ return Iterator(*this, N);
+ }
+
+ inline constexpr void Set(size_t idx) {
+ data[idx / BITS_PER_WORD] |= (1ULL << (idx % BITS_PER_WORD));
+ }
+
+ inline constexpr void Unset(size_t idx) {
+ data[idx / BITS_PER_WORD] &= ~(1ULL << (idx % BITS_PER_WORD));
+ }
+
+ inline constexpr bool Get(size_t idx) const {
+ return (data[idx / BITS_PER_WORD] & (1ULL << (idx % BITS_PER_WORD))) != 0;
+ }
+
+ inline void SetRange(size_t start, size_t end) {
+ if (start >= end || end > N) {
+ return;
+ }
+ const size_t first_word = start / BITS_PER_WORD;
+ const size_t last_word = (end - 1) / BITS_PER_WORD;
+ const size_t start_bit = start % BITS_PER_WORD;
+ const size_t end_bit = (end - 1) % BITS_PER_WORD;
+ const u64 start_mask = ~((1ULL << start_bit) - 1);
+ const u64 end_mask = end_bit == BITS_PER_WORD - 1 ? ~0ULL : (1ULL << (end_bit + 1)) - 1;
+ if (first_word == last_word) {
+ data[first_word] |= start_mask & end_mask;
+ } else {
+ data[first_word] |= start_mask;
+ size_t i = first_word + 1;
+#ifdef BIT_ARRAY_USE_AVX
+ const __m256i value = _mm256_set1_epi64x(-1);
+ for (; i + WORDS_PER_AVX <= last_word; i += WORDS_PER_AVX) {
+ _mm256_storeu_si256(reinterpret_cast<__m256i*>(&data[i]), value);
+ }
+#endif
+ for (; i < last_word; ++i) {
+ data[i] = ~0ULL;
+ }
+ data[last_word] |= end_mask;
+ }
+ }
+
+ inline void UnsetRange(size_t start, size_t end) {
+ if (start >= end || end > N) {
+ return;
+ }
+ size_t first_word = start / BITS_PER_WORD;
+ const size_t last_word = (end - 1) / BITS_PER_WORD;
+ const size_t start_bit = start % BITS_PER_WORD;
+ const size_t end_bit = (end - 1) % BITS_PER_WORD;
+ const u64 start_mask = (1ULL << start_bit) - 1;
+ const u64 end_mask = end_bit == BITS_PER_WORD - 1 ? 0ULL : ~((1ULL << (end_bit + 1)) - 1);
+ if (first_word == last_word) {
+ data[first_word] &= start_mask | end_mask;
+ } else {
+ data[first_word] &= start_mask;
+ size_t i = first_word + 1;
+#ifdef BIT_ARRAY_USE_AVX
+ const __m256i value = _mm256_setzero_si256();
+ for (; i + WORDS_PER_AVX <= last_word; i += WORDS_PER_AVX) {
+ _mm256_storeu_si256(reinterpret_cast<__m256i*>(&data[i]), value);
+ }
+#endif
+ for (; i < last_word; ++i) {
+ data[i] = 0ULL;
+ }
+ data[last_word] &= end_mask;
+ }
+ }
+
+ inline constexpr void SetRange(const Range& range) {
+ SetRange(range.first, range.second);
+ }
+
+ inline constexpr void UnsetRange(const Range& range) {
+ UnsetRange(range.first, range.second);
+ }
+
+ inline constexpr void Clear() {
+ data.fill(0);
+ }
+
+ inline constexpr void Fill() {
+ data.fill(~0ULL);
+ }
+
+ inline constexpr bool None() const {
+ u64 result = 0;
+ for (const auto& word : data) {
+ result |= word;
+ }
+ return result == 0;
+ }
+
+ inline constexpr bool Any() const {
+ return !None();
+ }
+
+ Range FirstRangeFrom(size_t start) const {
+ if (start >= N) {
+ return {N, N};
+ }
+ const auto find_end_bit = [&](size_t word) {
+#ifdef BIT_ARRAY_USE_AVX
+ const __m256i all_one = _mm256_set1_epi64x(-1);
+ for (; word + WORDS_PER_AVX <= WORD_COUNT; word += WORDS_PER_AVX) {
+ const __m256i current =
+ _mm256_loadu_si256(reinterpret_cast(&data[word]));
+ const __m256i cmp = _mm256_cmpeq_epi64(current, all_one);
+ if (_mm256_movemask_epi8(cmp) != 0xFFFFFFFF) {
+ break;
+ }
+ }
+#endif
+ for (; word < WORD_COUNT; ++word) {
+ if (data[word] != ~0ULL) {
+ return (word * BITS_PER_WORD) + std::countr_one(data[word]);
+ }
+ }
+ return N;
+ };
+
+ const auto word_bits = [&](size_t index, u64 word) {
+ const int empty_bits = std::countr_zero(word);
+ const int ones_count = std::countr_one(word >> empty_bits);
+ const size_t start_bit = index * BITS_PER_WORD + empty_bits;
+ if (ones_count + empty_bits < BITS_PER_WORD) {
+ return Range{start_bit, start_bit + ones_count};
+ }
+ return Range{start_bit, find_end_bit(index + 1)};
+ };
+
+ const size_t start_word = start / BITS_PER_WORD;
+ const size_t start_bit = start % BITS_PER_WORD;
+ const u64 masked_first = data[start_word] & (~((1ULL << start_bit) - 1));
+ if (masked_first) {
+ return word_bits(start_word, masked_first);
+ }
+
+ size_t word = start_word + 1;
+#ifdef BIT_ARRAY_USE_AVX
+ for (; word + WORDS_PER_AVX <= WORD_COUNT; word += WORDS_PER_AVX) {
+ const __m256i current =
+ _mm256_loadu_si256(reinterpret_cast(&data[word]));
+ if (!_mm256_testz_si256(current, current)) {
+ break;
+ }
+ }
+#endif
+ for (; word < WORD_COUNT; ++word) {
+ if (data[word] != 0) {
+ return word_bits(word, data[word]);
+ }
+ }
+ return {N, N};
+ }
+
+ inline constexpr Range FirstRange() const {
+ return FirstRangeFrom(0);
+ }
+
+ Range LastRangeFrom(size_t end) const {
+ if (end == 0) {
+ return {0, 0};
+ }
+ if (end > N) {
+ end = N;
+ }
+ const auto find_start_bit = [&](size_t word) {
+#ifdef BIT_ARRAY_USE_AVX
+ const __m256i all_zero = _mm256_setzero_si256();
+ for (; word >= WORDS_PER_AVX; word -= WORDS_PER_AVX) {
+ const __m256i current = _mm256_loadu_si256(
+ reinterpret_cast(&data[word - WORDS_PER_AVX]));
+ const __m256i cmp = _mm256_cmpeq_epi64(current, all_zero);
+ if (_mm256_movemask_epi8(cmp) != 0xFFFFFFFF) {
+ break;
+ }
+ }
+#endif
+ for (; word > 0; --word) {
+ if (data[word - 1] != ~0ULL) {
+ return word * BITS_PER_WORD - std::countl_one(data[word - 1]);
+ }
+ }
+ return size_t(0);
+ };
+ const auto word_bits = [&](size_t index, u64 word) {
+ const int empty_bits = std::countl_zero(word);
+ const int ones_count = std::countl_one(word << empty_bits);
+ const size_t end_bit = index * BITS_PER_WORD - empty_bits;
+ if (empty_bits + ones_count < BITS_PER_WORD) {
+ return Range{end_bit - ones_count, end_bit};
+ }
+ return Range{find_start_bit(index - 1), end_bit};
+ };
+ const size_t end_word = ((end - 1) / BITS_PER_WORD) + 1;
+ const size_t end_bit = (end - 1) % BITS_PER_WORD;
+ u64 masked_last = data[end_word - 1];
+ if (end_bit < BITS_PER_WORD - 1) {
+ masked_last &= (1ULL << (end_bit + 1)) - 1;
+ }
+ if (masked_last) {
+ return word_bits(end_word, masked_last);
+ }
+ size_t word = end_word - 1;
+#ifdef BIT_ARRAY_USE_AVX
+ for (; word >= WORDS_PER_AVX; word -= WORDS_PER_AVX) {
+ const __m256i current =
+ _mm256_loadu_si256(reinterpret_cast(&data[word - WORDS_PER_AVX]));
+ if (!_mm256_testz_si256(current, current)) {
+ break;
+ }
+ }
+#endif
+ for (; word > 0; --word) {
+ if (data[word - 1] != 0) {
+ return word_bits(word, data[word - 1]);
+ }
+ }
+ return {0, 0};
+ }
+
+ inline constexpr Range LastRange() const {
+ return LastRangeFrom(N);
+ }
+
+ inline constexpr size_t Size() const {
+ return N;
+ }
+
+ inline constexpr BitArray& operator|=(const BitArray& other) {
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ data[i] |= other.data[i];
+ }
+ return *this;
+ }
+
+ inline constexpr BitArray& operator&=(const BitArray& other) {
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ data[i] &= other.data[i];
+ }
+ return *this;
+ }
+
+ inline constexpr BitArray& operator^=(const BitArray& other) {
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ data[i] ^= other.data[i];
+ }
+ return *this;
+ }
+
+ inline constexpr BitArray operator|(const BitArray& other) const {
+ BitArray result = *this;
+ result |= other;
+ return result;
+ }
+
+ inline constexpr BitArray operator&(const BitArray& other) const {
+ BitArray result = *this;
+ result &= other;
+ return result;
+ }
+
+ inline constexpr BitArray operator^(const BitArray& other) const {
+ BitArray result = *this;
+ result ^= other;
+ return result;
+ }
+
+ inline constexpr BitArray operator~() const {
+ BitArray result = *this;
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ result.data[i] = ~result.data[i];
+ }
+ return result;
+ }
+
+ inline constexpr bool operator==(const BitArray& other) const {
+ u64 result = 0;
+ for (size_t i = 0; i < WORD_COUNT; ++i) {
+ result |= data[i] ^ other.data[i];
+ }
+ return result == 0;
+ }
+
+ inline constexpr bool operator!=(const BitArray& other) const {
+ return !(*this == other);
+ }
+
+private:
+ std::array data{};
+};
+
+} // namespace Common
diff --git a/src/common/config.cpp b/src/common/config.cpp
index 6bccd0f37..6f8563377 100644
--- a/src/common/config.cpp
+++ b/src/common/config.cpp
@@ -31,35 +31,53 @@ std::filesystem::path find_fs_path_or(const basic_value& v, const K& ky,
namespace Config {
+// General
static bool isNeo = false;
static bool isDevKit = false;
-static bool playBGM = false;
+static bool isPSNSignedIn = false;
static bool isTrophyPopupDisabled = false;
-static int BGMvolume = 50;
+static double trophyNotificationDuration = 6.0;
static bool enableDiscordRPC = false;
-static u32 screenWidth = 1280;
-static u32 screenHeight = 720;
-static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
-static std::string logFilter;
+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 std::string chooseHomeTab = "General";
+static bool isShowSplash = false;
+static std::string isSideTrophy = "right";
+static bool compatibilityData = false;
+static bool checkCompatibilityOnStartup = false;
+
+// Input
+static int cursorState = HideCursorState::Idle;
+static int cursorHideTimeout = 5; // 5 seconds (default)
static bool useSpecialPad = false;
static int specialPadClass = 1;
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 useUnifiedInputConfig = true;
+
+// These two entries aren't stored in the config
+static bool overrideControllerColor = false;
+static int controllerCustomColorRGB[3] = {0, 0, 255};
+
+// GPU
+static u32 windowWidth = 1280;
+static u32 windowHeight = 720;
+static u32 internalScreenWidth = 1280;
+static u32 internalScreenHeight = 720;
static bool isNullGpu = false;
static bool shouldCopyGPUBuffers = false;
+static bool readbacksEnabled = false;
+static bool readbackLinearImagesEnabled = false;
+static bool directMemoryAccessEnabled = false;
static bool shouldDumpShaders = false;
-static bool shouldPatchShaders = true;
+static bool shouldPatchShaders = false;
static u32 vblankDivider = 1;
+static bool isFullscreen = false;
+static std::string fullscreenMode = "Windowed";
+static bool isHDRAllowed = false;
+
+// Vulkan
+static s32 gpuId = -1;
static bool vkValidation = false;
static bool vkValidationSync = false;
static bool vkValidationGpu = false;
@@ -67,49 +85,29 @@ static bool vkCrashDiagnostic = false;
static bool vkHostMarkers = false;
static bool vkGuestMarkers = false;
static bool rdocEnable = false;
-static bool isFpsColor = true;
-static bool isSeparateLogFilesEnabled = false;
-static s16 cursorState = HideCursorState::Idle;
-static int cursorHideTimeout = 5; // 5 seconds (default)
-static double trophyNotificationDuration = 6.0;
-static bool useUnifiedInputConfig = true;
-static bool overrideControllerColor = false;
-static int controllerCustomColorRGB[3] = {0, 0, 255};
-static bool compatibilityData = false;
-static bool checkCompatibilityOnStartup = false;
-static std::string trophyKey;
-// Gui
+// Debug
+static bool isDebugDump = false;
+static bool isShaderDebug = false;
+static bool isSeparateLogFilesEnabled = false;
+static bool isFpsColor = true;
+
+// GUI
static bool load_game_size = true;
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
+// Settings
u32 m_language = 1; // english
+// Keys
+static std::string trophyKey = "";
+
+// Expected number of items in the config file
+static constexpr u64 total_entries = 54;
+
bool allowHDR() {
return isHDRAllowed;
}
@@ -175,14 +173,6 @@ bool getIsFullscreen() {
return isFullscreen;
}
-bool getShowLabelsUnderIcons() {
- return showLabelsUnderIcons;
-}
-
-bool setShowLabelsUnderIcons() {
- return false;
-}
-
std::string getFullscreenMode() {
return fullscreenMode;
}
@@ -191,14 +181,6 @@ bool getisTrophyPopupDisabled() {
return isTrophyPopupDisabled;
}
-bool getPlayBGM() {
- return playBGM;
-}
-
-int getBGMvolume() {
- return BGMvolume;
-}
-
bool getEnableDiscordRPC() {
return enableDiscordRPC;
}
@@ -215,12 +197,20 @@ double getTrophyNotificationDuration() {
return trophyNotificationDuration;
}
-u32 getScreenWidth() {
- return screenWidth;
+u32 getWindowWidth() {
+ return windowWidth;
}
-u32 getScreenHeight() {
- return screenHeight;
+u32 getWindowHeight() {
+ return windowHeight;
+}
+
+u32 getInternalScreenWidth() {
+ return internalScreenHeight;
+}
+
+u32 getInternalScreenHeight() {
+ return internalScreenHeight;
}
s32 getGpuId() {
@@ -239,18 +229,10 @@ std::string getUserName() {
return userName;
}
-std::string getUpdateChannel() {
- return updateChannel;
-}
-
std::string getChooseHomeTab() {
return chooseHomeTab;
}
-std::string getBackButtonBehavior() {
- return backButtonBehavior;
-}
-
bool getUseSpecialPad() {
return useSpecialPad;
}
@@ -275,14 +257,6 @@ bool showSplash() {
return isShowSplash;
}
-bool autoUpdate() {
- return isAutoUpdate;
-}
-
-bool alwaysShowChangelog() {
- return isAlwaysShowChangelog;
-}
-
std::string sideTrophy() {
return isSideTrophy;
}
@@ -295,6 +269,18 @@ bool copyGPUCmdBuffers() {
return shouldCopyGPUBuffers;
}
+bool readbacks() {
+ return readbacksEnabled;
+}
+
+bool readbackLinearImages() {
+ return readbackLinearImagesEnabled;
+}
+
+bool directMemoryAccess() {
+ return directMemoryAccessEnabled;
+}
+
bool dumpShaders() {
return shouldDumpShaders;
}
@@ -363,12 +349,20 @@ void setGpuId(s32 selectedGpuId) {
gpuId = selectedGpuId;
}
-void setScreenWidth(u32 width) {
- screenWidth = width;
+void setWindowWidth(u32 width) {
+ windowWidth = width;
}
-void setScreenHeight(u32 height) {
- screenHeight = height;
+void setWindowHeight(u32 height) {
+ windowHeight = height;
+}
+
+void setInternalScreenWidth(u32 width) {
+ internalScreenWidth = width;
+}
+
+void setInternalScreenHeight(u32 height) {
+ internalScreenHeight = height;
}
void setDebugDump(bool enable) {
@@ -383,14 +377,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;
}
@@ -407,6 +393,14 @@ void setCopyGPUCmdBuffers(bool enable) {
shouldCopyGPUBuffers = enable;
}
+void setReadbacks(bool enable) {
+ readbacksEnabled = enable;
+}
+
+void setDirectMemoryAccess(bool enable) {
+ directMemoryAccessEnabled = enable;
+}
+
void setDumpShaders(bool enable) {
shouldDumpShaders = enable;
}
@@ -430,9 +424,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 +433,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;
}
@@ -461,6 +444,7 @@ void setCursorState(s16 newCursorState) {
void setCursorHideTimeout(int newcursorHideTimeout) {
cursorHideTimeout = newcursorHideTimeout;
}
+
void setTrophyNotificationDuration(double newTrophyNotificationDuration) {
trophyNotificationDuration = newTrophyNotificationDuration;
}
@@ -489,17 +473,10 @@ void setUserName(const std::string& type) {
userName = type;
}
-void setUpdateChannel(const std::string& type) {
- updateChannel = type;
-}
void setChooseHomeTab(const std::string& type) {
chooseHomeTab = type;
}
-void setBackButtonBehavior(const std::string& type) {
- backButtonBehavior = type;
-}
-
void setUseSpecialPad(bool use) {
useSpecialPad = use;
}
@@ -520,13 +497,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) {
@@ -559,52 +529,6 @@ void setAddonInstallDir(const std::filesystem::path& dir) {
settings_addon_install_dir = dir;
}
-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;
-}
-
-void setRecentFiles(const std::vector& recentFiles) {
- m_recent_files.resize(recentFiles.size());
- m_recent_files = recentFiles;
-}
-
-void setEmulatorLanguage(std::string language) {
- emulator_language = language;
-}
-
void setGameInstallDirs(const std::vector& dirs_config) {
settings_install_dirs.clear();
for (const auto& dir : dirs_config) {
@@ -620,22 +544,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) {
@@ -662,50 +570,6 @@ std::filesystem::path getAddonInstallDir() {
return settings_addon_install_dir;
}
-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;
-}
-
-std::vector getRecentFiles() {
- return m_recent_files;
-}
-
-std::string getEmulatorLanguage() {
- return emulator_language;
-}
-
u32 GetLanguage() {
return m_language;
}
@@ -714,20 +578,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) {
@@ -749,95 +605,104 @@ void load(const std::filesystem::path& path) {
fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what());
return;
}
+
+ u64 entry_count = 0;
+
if (data.contains("General")) {
const toml::value& general = data.at("General");
- isNeo = toml::find_or(general, "isPS4Pro", false);
- isDevKit = toml::find_or(general, "isDevKit", false);
- playBGM = toml::find_or(general, "playBGM", 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 =
- toml::find_or(general, "checkCompatibilityOnStartup", false);
- chooseHomeTab = toml::find_or(general, "chooseHomeTab", "Release");
+ isNeo = toml::find_or(general, "isPS4Pro", isNeo);
+ isDevKit = toml::find_or(general, "isDevKit", isDevKit);
+ isPSNSignedIn = toml::find_or(general, "isPSNSignedIn", isPSNSignedIn);
+ isTrophyPopupDisabled =
+ toml::find_or(general, "isTrophyPopupDisabled", isTrophyPopupDisabled);
+ trophyNotificationDuration = toml::find_or(general, "trophyNotificationDuration",
+ trophyNotificationDuration);
+ enableDiscordRPC = toml::find_or(general, "enableDiscordRPC", enableDiscordRPC);
+ logFilter = toml::find_or(general, "logFilter", logFilter);
+ logType = toml::find_or(general, "logType", logType);
+ userName = toml::find_or(general, "userName", userName);
+ isShowSplash = toml::find_or(general, "showSplash", isShowSplash);
+ isSideTrophy = toml::find_or(general, "sideTrophy", isSideTrophy);
+ compatibilityData = toml::find_or(general, "compatibilityEnabled", compatibilityData);
+ checkCompatibilityOnStartup = toml::find_or(general, "checkCompatibilityOnStartup",
+ checkCompatibilityOnStartup);
+ chooseHomeTab = toml::find_or(general, "chooseHomeTab", chooseHomeTab);
+
+ entry_count += general.size();
}
if (data.contains("Input")) {
const toml::value& input = data.at("Input");
- cursorState = toml::find_or(input, "cursorState", HideCursorState::Idle);
- cursorHideTimeout = toml::find_or(input, "cursorHideTimeout", 5);
- backButtonBehavior = toml::find_or(input, "backButtonBehavior", "left");
- useSpecialPad = toml::find_or(input, "useSpecialPad", false);
- specialPadClass = toml::find_or(input, "specialPadClass", 1);
- isMotionControlsEnabled = toml::find_or(input, "isMotionControlsEnabled", true);
- useUnifiedInputConfig = toml::find_or(input, "useUnifiedInputConfig", true);
+ cursorState = toml::find_or(input, "cursorState", cursorState);
+ cursorHideTimeout = toml::find_or(input, "cursorHideTimeout", cursorHideTimeout);
+ useSpecialPad = toml::find_or(input, "useSpecialPad", useSpecialPad);
+ specialPadClass = toml::find_or(input, "specialPadClass", specialPadClass);
+ isMotionControlsEnabled =
+ toml::find_or(input, "isMotionControlsEnabled", isMotionControlsEnabled);
+ useUnifiedInputConfig =
+ toml::find_or(input, "useUnifiedInputConfig", useUnifiedInputConfig);
+
+ entry_count += input.size();
}
if (data.contains("GPU")) {
const toml::value& gpu = data.at("GPU");
- screenWidth = toml::find_or(gpu, "screenWidth", screenWidth);
- screenHeight = toml::find_or(gpu, "screenHeight", screenHeight);
- isNullGpu = toml::find_or(gpu, "nullGpu", false);
- shouldCopyGPUBuffers = toml::find_or(gpu, "copyGPUBuffers", false);
- shouldDumpShaders = toml::find_or(gpu, "dumpShaders", false);
- shouldPatchShaders = toml::find_or(gpu, "patchShaders", true);
- vblankDivider = toml::find_or(gpu, "vblankDivider", 1);
- isFullscreen = toml::find_or(gpu, "Fullscreen", false);
- fullscreenMode = toml::find_or(gpu, "FullscreenMode", "Windowed");
- isHDRAllowed = toml::find_or(gpu, "allowHDR", false);
+ windowWidth = toml::find_or(gpu, "screenWidth", windowWidth);
+ windowHeight = toml::find_or(gpu, "screenHeight", windowHeight);
+ internalScreenWidth = toml::find_or(gpu, "internalScreenWidth", internalScreenWidth);
+ internalScreenHeight =
+ toml::find_or(gpu, "internalScreenHeight", internalScreenHeight);
+ isNullGpu = toml::find_or(gpu, "nullGpu", isNullGpu);
+ shouldCopyGPUBuffers = toml::find_or(gpu, "copyGPUBuffers", shouldCopyGPUBuffers);
+ readbacksEnabled = toml::find_or(gpu, "readbacks", readbacksEnabled);
+ readbackLinearImagesEnabled =
+ toml::find_or(gpu, "readbackLinearImages", readbackLinearImagesEnabled);
+ directMemoryAccessEnabled =
+ toml::find_or(gpu, "directMemoryAccess", directMemoryAccessEnabled);
+ shouldDumpShaders = toml::find_or(gpu, "dumpShaders", shouldDumpShaders);
+ shouldPatchShaders = toml::find_or(gpu, "patchShaders", shouldPatchShaders);
+ vblankDivider = toml::find_or(gpu, "vblankDivider", vblankDivider);
+ isFullscreen = toml::find_or(gpu, "Fullscreen", isFullscreen);
+ fullscreenMode = toml::find_or(gpu, "FullscreenMode", fullscreenMode);
+ isHDRAllowed = toml::find_or(gpu, "allowHDR", isHDRAllowed);
+
+ entry_count += gpu.size();
}
if (data.contains("Vulkan")) {
const toml::value& vk = data.at("Vulkan");
- gpuId = toml::find_or(vk, "gpuId", -1);
- vkValidation = toml::find_or(vk, "validation", false);
- vkValidationSync = toml::find_or(vk, "validation_sync", false);
- vkValidationGpu = toml::find_or(vk, "validation_gpu", true);
- vkCrashDiagnostic = toml::find_or(vk, "crashDiagnostic", false);
- vkHostMarkers = toml::find_or(vk, "hostMarkers", false);
- vkGuestMarkers = toml::find_or(vk, "guestMarkers", false);
- rdocEnable = toml::find_or(vk, "rdocEnable", false);
+ gpuId = toml::find_or(vk, "gpuId", gpuId);
+ vkValidation = toml::find_or(vk, "validation", vkValidation);
+ vkValidationSync = toml::find_or(vk, "validation_sync", vkValidationSync);
+ vkValidationGpu = toml::find_or(vk, "validation_gpu", vkValidationGpu);
+ vkCrashDiagnostic = toml::find_or(vk, "crashDiagnostic", vkCrashDiagnostic);
+ vkHostMarkers = toml::find_or(vk, "hostMarkers", vkHostMarkers);
+ vkGuestMarkers = toml::find_or(vk, "guestMarkers", vkGuestMarkers);
+ rdocEnable = toml::find_or(vk, "rdocEnable", rdocEnable);
+
+ entry_count += vk.size();
}
if (data.contains("Debug")) {
const toml::value& debug = data.at("Debug");
- isDebugDump = toml::find_or(debug, "DebugDump", false);
- isSeparateLogFilesEnabled = toml::find_or(debug, "isSeparateLogFilesEnabled", false);
- isShaderDebug = toml::find_or(debug, "CollectShader", false);
- isFpsColor = toml::find_or(debug, "FPSColor", true);
+ isDebugDump = toml::find_or(debug, "DebugDump", isDebugDump);
+ isSeparateLogFilesEnabled =
+ toml::find_or(debug, "isSeparateLogFilesEnabled", isSeparateLogFilesEnabled);
+ isShaderDebug = toml::find_or(debug, "CollectShader", isShaderDebug);
+ isFpsColor = toml::find_or(debug, "FPSColor", isFpsColor);
+
+ entry_count += debug.size();
}
if (data.contains("GUI")) {
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);
+ load_game_size = toml::find_or(gui, "loadGameSizeEnabled", load_game_size);
const auto install_dir_array =
toml::find_or>(gui, "installDirs", {});
@@ -859,41 +724,31 @@ void load(const std::filesystem::path& path) {
{std::filesystem::path{install_dir_array[i]}, install_dirs_enabled[i]});
}
- save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {});
+ save_data_path = toml::find_fs_path_or(gui, "saveDataPath", save_data_path);
- 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);
+ settings_addon_install_dir =
+ toml::find_fs_path_or(gui, "addonInstallDir", settings_addon_install_dir);
+
+ entry_count += gui.size();
}
if (data.contains("Settings")) {
const toml::value& settings = data.at("Settings");
+ m_language = toml::find_or(settings, "consoleLanguage", m_language);
- m_language = toml::find_or(settings, "consoleLanguage", 1);
+ entry_count += settings.size();
}
if (data.contains("Keys")) {
const toml::value& keys = data.at("Keys");
- trophyKey = toml::find_or(keys, "TrophyKey", "");
+ trophyKey = toml::find_or(keys, "TrophyKey", trophyKey);
+
+ entry_count += keys.size();
}
- // 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"};
-
- if (std::find(allowed_languages.begin(), allowed_languages.end(), emulator_language) ==
- allowed_languages.end()) {
- emulator_language = "en_US"; // Default to en_US if not in the list
+ // Run save after loading to generate any missing fields with default values.
+ if (entry_count != total_entries) {
+ fmt::print("Outdated config detected, updating config file.\n");
save(path);
}
}
@@ -953,33 +808,33 @@ 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;
data["Input"]["cursorState"] = cursorState;
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
- data["Input"]["backButtonBehavior"] = backButtonBehavior;
data["Input"]["useSpecialPad"] = useSpecialPad;
data["Input"]["specialPadClass"] = specialPadClass;
data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled;
data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig;
- data["GPU"]["screenWidth"] = screenWidth;
- data["GPU"]["screenHeight"] = screenHeight;
+ data["GPU"]["screenWidth"] = windowWidth;
+ data["GPU"]["screenHeight"] = windowHeight;
+ data["GPU"]["internalScreenWidth"] = internalScreenWidth;
+ data["GPU"]["internalScreenHeight"] = internalScreenHeight;
data["GPU"]["nullGpu"] = isNullGpu;
data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers;
+ data["GPU"]["readbacks"] = readbacksEnabled;
+ data["GPU"]["readbackLinearImages"] = readbackLinearImagesEnabled;
+ data["GPU"]["directMemoryAccess"] = directMemoryAccessEnabled;
data["GPU"]["dumpShaders"] = shouldDumpShaders;
data["GPU"]["patchShaders"] = shouldPatchShaders;
data["GPU"]["vblankDivider"] = vblankDivider;
@@ -1034,9 +889,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
@@ -1045,90 +897,56 @@ void save(const std::filesystem::path& path) {
std::ofstream file(path, std::ios::binary);
file << data;
file.close();
-
- saveMainWindow(path);
-}
-
-void saveMainWindow(const std::filesystem::path& path) {
- toml::ordered_value data;
-
- std::error_code error;
- if (std::filesystem::exists(path, error)) {
- try {
- std::ifstream ifs;
- ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
- ifs.open(path, std::ios_base::binary);
- data = toml::parse(
- ifs, std::string{fmt::UTF(path.filename().u8string()).data});
- } catch (const std::exception& ex) {
- fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what());
- return;
- }
- } else {
- if (error) {
- fmt::print("Filesystem error: {}\n", error.message());
- }
- fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string()));
- }
-
- data["GUI"]["mw_width"] = m_window_size_W;
- data["GUI"]["mw_height"] = m_window_size_H;
- data["GUI"]["theme"] = mw_themes;
- data["GUI"]["iconSize"] = m_icon_size;
- data["GUI"]["sliderPos"] = m_slider_pos;
- data["GUI"]["iconSizeGrid"] = m_icon_size_grid;
- data["GUI"]["sliderPosGrid"] = m_slider_pos_grid;
- data["GUI"]["gameTableMode"] = m_table_mode;
- data["GUI"]["geometry_x"] = main_window_geometry_x;
- data["GUI"]["geometry_y"] = main_window_geometry_y;
- data["GUI"]["geometry_w"] = main_window_geometry_w;
- data["GUI"]["geometry_h"] = main_window_geometry_h;
- data["GUI"]["elfDirs"] = m_elf_viewer;
- data["GUI"]["recentFiles"] = m_recent_files;
-
- // Sorting of TOML sections
- sortTomlSections(data);
-
- std::ofstream file(path, std::ios::binary);
- file << data;
- file.close();
}
void setDefaultValues() {
- isHDRAllowed = false;
+ // General
isNeo = false;
isDevKit = false;
- isFullscreen = false;
+ isPSNSignedIn = false;
isTrophyPopupDisabled = false;
- playBGM = false;
- BGMvolume = 50;
- enableDiscordRPC = true;
- screenWidth = 1280;
- screenHeight = 720;
+ trophyNotificationDuration = 6.0;
+ enableDiscordRPC = false;
logFilter = "";
logType = "sync";
userName = "shadPS4";
- if (Common::g_is_release) {
- updateChannel = "Release";
- } else {
- updateChannel = "Nightly";
- }
chooseHomeTab = "General";
+ isShowSplash = false;
+ isSideTrophy = "right";
+ compatibilityData = false;
+ checkCompatibilityOnStartup = false;
+
+ // Input
cursorState = HideCursorState::Idle;
cursorHideTimeout = 5;
- trophyNotificationDuration = 6.0;
- backButtonBehavior = "left";
useSpecialPad = false;
specialPadClass = 1;
- isDebugDump = false;
- isShaderDebug = false;
- isShowSplash = false;
- isAutoUpdate = false;
- isAlwaysShowChangelog = false;
- isSideTrophy = "right";
+ isMotionControlsEnabled = true;
+ useUnifiedInputConfig = true;
+ overrideControllerColor = false;
+ controllerCustomColorRGB[0] = 0;
+ controllerCustomColorRGB[1] = 0;
+ controllerCustomColorRGB[2] = 255;
+
+ // GPU
+ windowWidth = 1280;
+ windowHeight = 720;
+ internalScreenWidth = 1280;
+ internalScreenHeight = 720;
isNullGpu = false;
+ shouldCopyGPUBuffers = false;
+ readbacksEnabled = false;
+ readbackLinearImagesEnabled = false;
+ directMemoryAccessEnabled = false;
shouldDumpShaders = false;
+ shouldPatchShaders = false;
vblankDivider = 1;
+ isFullscreen = false;
+ fullscreenMode = "Windowed";
+ isHDRAllowed = false;
+
+ // Vulkan
+ gpuId = -1;
vkValidation = false;
vkValidationSync = false;
vkValidationGpu = false;
@@ -1136,13 +954,18 @@ void setDefaultValues() {
vkHostMarkers = false;
vkGuestMarkers = false;
rdocEnable = false;
- emulator_language = "en_US";
+
+ // Debug
+ isDebugDump = false;
+ isShaderDebug = false;
+ isSeparateLogFilesEnabled = false;
+ isFpsColor = true;
+
+ // GUI
+ load_game_size = true;
+
+ // Settings
m_language = 1;
- gpuId = -1;
- compatibilityData = false;
- checkCompatibilityOnStartup = false;
- backgroundImageOpacity = 50;
- showBackgroundImage = true;
}
constexpr std::string_view GetDefaultKeyboardConfig() {
@@ -1168,7 +991,7 @@ l3 = x
r3 = m
options = enter
-touchpad = space
+touchpad_center = space
pad_up = up
pad_down = down
@@ -1200,7 +1023,7 @@ r2 = r2
r3 = r3
options = options
-touchpad = back
+touchpad_center = back
pad_up = pad_up
pad_down = pad_down
diff --git a/src/common/config.h b/src/common/config.h
index aba23621c..e54425676 100644
--- a/src/common/config.h
+++ b/src/common/config.h
@@ -14,172 +14,129 @@ 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);
-void saveMainWindow(const std::filesystem::path& path);
std::string getTrophyKey();
void setTrophyKey(std::string key);
+bool getIsFullscreen();
+void setIsFullscreen(bool enable);
+std::string getFullscreenMode();
+void setFullscreenMode(std::string mode);
+u32 getWindowWidth();
+u32 getWindowHeight();
+void setWindowWidth(u32 width);
+void setWindowHeight(u32 height);
+u32 getInternalScreenWidth();
+u32 getInternalScreenHeight();
+void setInternalScreenWidth(u32 width);
+void setInternalScreenHeight(u32 height);
+bool debugDump();
+void setDebugDump(bool enable);
+s32 getGpuId();
+void setGpuId(s32 selectedGpuId);
+bool allowHDR();
+void setAllowHDR(bool enable);
+bool collectShadersForDebug();
+void setCollectShaderForDebug(bool enable);
+bool showSplash();
+void setShowSplash(bool enable);
+std::string sideTrophy();
+void setSideTrophy(std::string side);
+bool nullGpu();
+void setNullGpu(bool enable);
+bool copyGPUCmdBuffers();
+void setCopyGPUCmdBuffers(bool enable);
+bool readbacks();
+void setReadbacks(bool enable);
+bool readbackLinearImages();
+bool directMemoryAccess();
+void setDirectMemoryAccess(bool enable);
+bool dumpShaders();
+void setDumpShaders(bool enable);
+u32 vblankDiv();
+void setVblankDiv(u32 value);
+bool getisTrophyPopupDisabled();
+void setisTrophyPopupDisabled(bool disable);
+s16 getCursorState();
+void setCursorState(s16 cursorState);
+bool vkValidationEnabled();
+void setVkValidation(bool enable);
+bool vkValidationSyncEnabled();
+void setVkSyncValidation(bool enable);
+bool getVkCrashDiagnosticEnabled();
+void setVkCrashDiagnosticEnabled(bool enable);
+bool getVkHostMarkersEnabled();
+void setVkHostMarkersEnabled(bool enable);
+bool getVkGuestMarkersEnabled();
+void setVkGuestMarkersEnabled(bool enable);
+bool getEnableDiscordRPC();
+void setEnableDiscordRPC(bool enable);
+bool isRdocEnabled();
+void setRdocEnabled(bool enable);
+std::string getLogType();
+void setLogType(const std::string& type);
+std::string getLogFilter();
+void setLogFilter(const std::string& type);
+double getTrophyNotificationDuration();
+void setTrophyNotificationDuration(double newTrophyNotificationDuration);
+int getCursorHideTimeout();
+void setCursorHideTimeout(int newcursorHideTimeout);
+void setSeparateLogFilesEnabled(bool enabled);
+bool getSeparateLogFilesEnabled();
+u32 GetLanguage();
+void setLanguage(u32 language);
+void setUseSpecialPad(bool use);
+bool getUseSpecialPad();
+void setSpecialPadClass(int type);
+int getSpecialPadClass();
+bool getPSNSignedIn();
+void setPSNSignedIn(bool sign); // no ui setting
+bool patchShaders(); // no set
+bool fpsColor(); // no set
+bool isNeoModeConsole();
+void setNeoMode(bool enable); // no ui setting
+bool isDevKitConsole(); // no set
+bool vkValidationGpuEnabled(); // no set
+bool getIsMotionControlsEnabled();
+void setIsMotionControlsEnabled(bool use);
+
+// TODO
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();
-
-std::string getLogFilter();
-std::string getLogType();
std::string getUserName();
-std::string getUpdateChannel();
std::string getChooseHomeTab();
-
-s16 getCursorState();
-int getCursorHideTimeout();
-double getTrophyNotificationDuration();
-std::string getBackButtonBehavior();
-bool getUseSpecialPad();
-int getSpecialPadClass();
-bool getIsMotionControlsEnabled();
bool GetUseUnifiedInputConfig();
void SetUseUnifiedInputConfig(bool use);
bool GetOverrideControllerColor();
void SetOverrideControllerColor(bool enable);
int* GetControllerCustomColor();
void SetControllerCustomColor(int r, int b, int g);
-
-u32 getScreenWidth();
-u32 getScreenHeight();
-s32 getGpuId();
-bool allowHDR();
-
-bool debugDump();
-bool collectShadersForDebug();
-bool showSplash();
-bool autoUpdate();
-bool alwaysShowChangelog();
-std::string sideTrophy();
-bool nullGpu();
-bool copyGPUCmdBuffers();
-bool dumpShaders();
-bool patchShaders();
-bool isRdocEnabled();
-bool fpsColor();
-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);
-void setCopyGPUCmdBuffers(bool enable);
-void setDumpShaders(bool enable);
-void setVblankDiv(u32 value);
-void setGpuId(s32 selectedGpuId);
-void setScreenWidth(u32 width);
-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 setCursorState(s16 cursorState);
-void setCursorHideTimeout(int newcursorHideTimeout);
-void setTrophyNotificationDuration(double newTrophyNotificationDuration);
-void setBackButtonBehavior(const std::string& type);
-void setUseSpecialPad(bool use);
-void setSpecialPadClass(int type);
-void setIsMotionControlsEnabled(bool use);
-
-void setLogType(const std::string& type);
-void setLogFilter(const std::string& type);
-void setSeparateLogFilesEnabled(bool enabled);
-bool getSeparateLogFilesEnabled();
-void setVkValidation(bool enable);
-void setVkSyncValidation(bool enable);
-void setRdocEnabled(bool enable);
-
-bool vkValidationEnabled();
-bool vkValidationSyncEnabled();
-bool vkValidationGpuEnabled();
-bool getVkCrashDiagnosticEnabled();
-bool getVkHostMarkersEnabled();
-bool getVkGuestMarkersEnabled();
-void setVkCrashDiagnosticEnabled(bool enable);
-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();
void setDefaultValues();
// todo: name and function location pending
std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id = "");
-// settings
-u32 GetLanguage();
}; // namespace Config
diff --git a/src/common/io_file.h b/src/common/io_file.h
index 45787a092..cb01e154a 100644
--- a/src/common/io_file.h
+++ b/src/common/io_file.h
@@ -186,7 +186,9 @@ public:
template
size_t WriteRaw(const void* data, size_t size) const {
- return std::fwrite(data, sizeof(T), size, file);
+ auto bytes = std::fwrite(data, sizeof(T), size, file);
+ std::fflush(file);
+ return bytes;
}
template
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 231cbf849..05935fbdc 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -140,6 +140,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
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 e4eae59af..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
@@ -107,6 +108,7 @@ enum class Class : u8 {
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/range_lock.h b/src/common/range_lock.h
new file mode 100644
index 000000000..efe6eb549
--- /dev/null
+++ b/src/common/range_lock.h
@@ -0,0 +1,101 @@
+// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+#include
+
+namespace Common {
+
+// From boost thread locking
+
+template
+struct RangeLockGuard {
+ Iterator begin;
+ Iterator end;
+
+ RangeLockGuard(Iterator begin_, Iterator end_) : begin(begin_), end(end_) {
+ LockRange(begin, end);
+ }
+
+ void release() {
+ begin = end;
+ }
+
+ ~RangeLockGuard() {
+ for (; begin != end; ++begin) {
+ begin->unlock();
+ }
+ }
+};
+
+template
+Iterator TryLockRange(Iterator begin, Iterator end) {
+ using LockType = typename std::iterator_traits::value_type;
+
+ if (begin == end) {
+ return end;
+ }
+
+ std::unique_lock guard(*begin, std::try_to_lock);
+ if (!guard.owns_lock()) {
+ return begin;
+ }
+
+ Iterator failed = TryLockRange(++begin, end);
+ if (failed == end) {
+ guard.release();
+ }
+
+ return failed;
+}
+
+template
+void LockRange(Iterator begin, Iterator end) {
+ using LockType = typename std::iterator_traits::value_type;
+
+ if (begin == end) {
+ return;
+ }
+
+ bool start_with_begin = true;
+ Iterator second = begin;
+ ++second;
+ Iterator next = second;
+
+ while (true) {
+ std::unique_lock begin_lock(*begin, std::defer_lock);
+ if (start_with_begin) {
+ begin_lock.lock();
+
+ const Iterator failed_lock = TryLockRange(next, end);
+ if (failed_lock == end) {
+ begin_lock.release();
+ return;
+ }
+
+ start_with_begin = false;
+ next = failed_lock;
+ } else {
+ RangeLockGuard guard(next, end);
+
+ if (begin_lock.try_lock()) {
+ const Iterator failed_lock = TryLockRange(second, next);
+ if (failed_lock == next) {
+ begin_lock.release();
+ guard.release();
+ return;
+ }
+
+ start_with_begin = false;
+ next = failed_lock;
+ } else {
+ start_with_begin = true;
+ next = second;
+ }
+ }
+ }
+}
+
+} // 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/shared_first_mutex.h b/src/common/shared_first_mutex.h
new file mode 100644
index 000000000..b150c956b
--- /dev/null
+++ b/src/common/shared_first_mutex.h
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+#include
+
+namespace Common {
+
+// Like std::shared_mutex, but reader has priority over writer.
+class SharedFirstMutex {
+public:
+ void lock() {
+ std::unique_lock lock(mtx);
+ cv.wait(lock, [this]() { return !writer_active && readers == 0; });
+ writer_active = true;
+ }
+
+ void unlock() {
+ std::lock_guard lock(mtx);
+ writer_active = false;
+ cv.notify_all();
+ }
+
+ void lock_shared() {
+ std::unique_lock lock(mtx);
+ cv.wait(lock, [this]() { return !writer_active; });
+ ++readers;
+ }
+
+ void unlock_shared() {
+ std::lock_guard lock(mtx);
+ if (--readers == 0) {
+ cv.notify_all();
+ }
+ }
+
+private:
+ std::mutex mtx;
+ std::condition_variable cv;
+ int readers = 0;
+ bool writer_active = false;
+};
+
+} // namespace Common
diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp
index 2e66bdf83..846bb5eb4 100644
--- a/src/core/address_space.cpp
+++ b/src/core/address_space.cpp
@@ -302,14 +302,15 @@ struct AddressSpace::Impl {
new_flags = PAGE_READWRITE;
} else if (read && !write) {
new_flags = PAGE_READONLY;
- } else if (execute && !read && not write) {
+ } else if (execute && !read && !write) {
new_flags = PAGE_EXECUTE;
} else if (!read && !write && !execute) {
new_flags = PAGE_NOACCESS;
} else {
LOG_CRITICAL(Common_Memory,
- "Unsupported protection flag combination for address {:#x}, size {}",
- virtual_addr, size);
+ "Unsupported protection flag combination for address {:#x}, size {}, "
+ "read={}, write={}, execute={}",
+ virtual_addr, size, read, write, execute);
return;
}
@@ -357,9 +358,17 @@ enum PosixPageProtection {
[[nodiscard]] constexpr PosixPageProtection ToPosixProt(Core::MemoryProt prot) {
if (True(prot & Core::MemoryProt::CpuReadWrite) ||
True(prot & Core::MemoryProt::GpuReadWrite)) {
- return PAGE_READWRITE;
+ if (True(prot & Core::MemoryProt::CpuExec)) {
+ return PAGE_EXECUTE_READWRITE;
+ } else {
+ return PAGE_READWRITE;
+ }
} else if (True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead)) {
- return PAGE_READONLY;
+ if (True(prot & Core::MemoryProt::CpuExec)) {
+ return PAGE_EXECUTE_READ;
+ } else {
+ return PAGE_READONLY;
+ }
} else {
return PAGE_NOACCESS;
}
diff --git a/src/core/address_space.h b/src/core/address_space.h
index d7f3efc75..85b4c36ac 100644
--- a/src/core/address_space.h
+++ b/src/core/address_space.h
@@ -11,6 +11,7 @@
namespace Core {
enum class MemoryPermission : u32 {
+ None = 0,
Read = 1 << 0,
Write = 1 << 1,
ReadWrite = Read | Write,
diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp
index 8937ef04b..e4f65cd31 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;
@@ -161,7 +163,9 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
mask = (1ULL << length) - 1;
}
- ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64.");
+ if (length + index > 64) {
+ mask = 0xFFFF'FFFF'FFFF'FFFF;
+ }
// Get lower qword from xmm register
c.vmovq(scratch1, xmm_dst);
@@ -175,8 +179,8 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
c.mov(scratch2, mask);
c.and_(scratch1, scratch2);
- // Writeback to xmm register, extrq instruction says top 64-bits are undefined so we don't
- // care to preserve them
+ // Writeback to xmm register, extrq instruction says top 64-bits are undefined but zeroed on
+ // AMD CPUs
c.vmovq(xmm_dst, scratch1);
c.pop(scratch2);
@@ -245,7 +249,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;
@@ -284,7 +289,9 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
mask_value = (1ULL << length) - 1;
}
- ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64.");
+ if (length + index > 64) {
+ mask_value = 0xFFFF'FFFF'FFFF'FFFF;
+ }
c.vmovq(scratch1, xmm_src);
c.vmovq(scratch2, xmm_dst);
@@ -304,8 +311,9 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
// dst |= src
c.or_(scratch2, scratch1);
- // Insert scratch2 into low 64 bits of dst, upper 64 bits are unaffected
- c.vpinsrq(xmm_dst, xmm_dst, scratch2, 0);
+ // Insert scratch2 into low 64 bits of dst, upper 64 bits are undefined but zeroed on AMD
+ // CPUs
+ c.vmovq(xmm_dst, scratch2);
c.pop(mask);
c.pop(scratch2);
@@ -371,7 +379,7 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
c.and_(scratch2, mask);
c.or_(scratch2, scratch1);
- // Upper 64 bits are undefined in insertq
+ // Upper 64 bits are undefined in insertq but AMD CPUs zero them
c.vmovq(xmm_dst, scratch2);
c.pop(mask);
@@ -383,8 +391,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 +444,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 +523,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 +531,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;
@@ -594,6 +640,7 @@ static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) {
lowQWordDst >>= index;
lowQWordDst &= mask;
+ memset((u8*)dst + sizeof(u64), 0, sizeof(u64));
memcpy(dst, &lowQWordDst, sizeof(lowQWordDst));
Common::IncrementRip(ctx, 4);
@@ -634,6 +681,7 @@ static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) {
lowQWordDst &= ~(mask << index);
lowQWordDst |= lowQWordSrc << index;
+ memset((u8*)dst + sizeof(u64), 0, sizeof(u64));
memcpy(dst, &lowQWordDst, sizeof(lowQWordDst));
Common::IncrementRip(ctx, 4);
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/companion/companion_error.h b/src/core/libraries/companion/companion_error.h
index 2d1a3833c..0459c33f8 100644
--- a/src/core/libraries/companion/companion_error.h
+++ b/src/core/libraries/companion/companion_error.h
@@ -3,6 +3,8 @@
#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;
@@ -18,3 +20,8 @@ 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_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 9cf340050..8c3ab1612 100644
--- a/src/core/libraries/gnmdriver/gnmdriver.cpp
+++ b/src/core/libraries/gnmdriver/gnmdriver.cpp
@@ -2834,7 +2834,7 @@ void RegisterlibSceGnmDriver(Core::Loader::SymbolsResolver* sym) {
}
if (Config::copyGPUCmdBuffers()) {
- liverpool->reserveCopyBufferSpace();
+ liverpool->ReserveCopyBufferSpace();
}
Platform::IrqC::Instance()->Register(Platform::InterruptId::GpuIdle, ResetSubmissionLock,
diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp
index 1c61bc276..54e856e87 100644
--- a/src/core/libraries/ime/ime.cpp
+++ b/src/core/libraries/ime/ime.cpp
@@ -43,8 +43,8 @@ public:
openEvent.param.rect.x = m_param.ime.posx;
openEvent.param.rect.y = m_param.ime.posy;
} else {
- openEvent.param.resource_id_array.userId = 1;
- openEvent.param.resource_id_array.resourceId[0] = 1;
+ openEvent.param.resource_id_array.user_id = 1;
+ openEvent.param.resource_id_array.resource_id[0] = 1;
}
// Are we supposed to call the event handler on init with
@@ -59,10 +59,10 @@ public:
}
}
- s32 Update(OrbisImeEventHandler handler) {
+ Error Update(OrbisImeEventHandler handler) {
if (!m_ime_mode) {
/* We don't handle any events for ImeKeyboard */
- return ORBIS_OK;
+ return Error::OK;
}
std::unique_lock lock{g_ime_state.queue_mutex};
@@ -73,7 +73,7 @@ public:
Execute(handler, &event, false);
}
- return ORBIS_OK;
+ return Error::OK;
}
void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) {
@@ -94,14 +94,14 @@ public:
}
}
- s32 SetText(const char16_t* text, u32 length) {
+ Error SetText(const char16_t* text, u32 length) {
g_ime_state.SetText(text, length);
- return ORBIS_OK;
+ return Error::OK;
}
- s32 SetCaret(const OrbisImeCaret* caret) {
+ Error SetCaret(const OrbisImeCaret* caret) {
g_ime_state.SetCaret(caret->index);
- return ORBIS_OK;
+ return Error::OK;
}
bool IsIme() {
@@ -222,11 +222,11 @@ int PS4_SYSV_ABI sceImeGetPanelPositionAndForm() {
return ORBIS_OK;
}
-s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height) {
+Error PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height) {
LOG_INFO(Lib_Ime, "called");
if (!width || !height) {
- return ORBIS_IME_ERROR_INVALID_ADDRESS;
+ return Error::INVALID_ADDRESS;
}
switch (param->type) {
@@ -244,18 +244,18 @@ s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32*
break;
}
- return ORBIS_OK;
+ return Error::OK;
}
-s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) {
+Error PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) {
LOG_INFO(Lib_Ime, "(STUBBED) called");
if (!g_keyboard_handler) {
- return ORBIS_IME_ERROR_NOT_OPENED;
+ return Error::NOT_OPENED;
}
g_keyboard_handler.release();
- return ORBIS_OK;
+ return Error::OK;
}
int PS4_SYSV_ABI sceImeKeyboardGetInfo() {
@@ -268,25 +268,25 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() {
return ORBIS_OK;
}
-s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) {
+Error PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) {
LOG_INFO(Lib_Ime, "called");
if (!param) {
- return ORBIS_IME_ERROR_INVALID_ADDRESS;
+ return Error::INVALID_ADDRESS;
}
if (!param->arg) {
- return ORBIS_IME_ERROR_INVALID_ARG;
+ return Error::INVALID_ARG;
}
if (!param->handler) {
- return ORBIS_IME_ERROR_INVALID_HANDLER;
+ return Error::INVALID_HANDLER;
}
if (g_keyboard_handler) {
- return ORBIS_IME_ERROR_BUSY;
+ return Error::BUSY;
}
g_keyboard_handler = std::make_unique(param);
- return ORBIS_OK;
+ return Error::OK;
}
int PS4_SYSV_ABI sceImeKeyboardOpenInternal() {
@@ -304,18 +304,18 @@ int PS4_SYSV_ABI sceImeKeyboardUpdate() {
return ORBIS_OK;
}
-s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended) {
+Error PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const OrbisImeParamExtended* extended) {
LOG_INFO(Lib_Ime, "called");
if (!param) {
- return ORBIS_IME_ERROR_INVALID_ADDRESS;
+ return Error::INVALID_ADDRESS;
}
if (g_ime_handler) {
- return ORBIS_IME_ERROR_BUSY;
+ return Error::BUSY;
}
g_ime_handler = std::make_unique(param);
- return ORBIS_OK;
+ return Error::OK;
}
int PS4_SYSV_ABI sceImeOpenInternal() {
@@ -339,27 +339,27 @@ int PS4_SYSV_ABI sceImeSetCandidateIndex() {
return ORBIS_OK;
}
-int PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) {
+Error PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) {
LOG_TRACE(Lib_Ime, "called");
if (!g_ime_handler) {
- return ORBIS_IME_ERROR_NOT_OPENED;
+ return Error::NOT_OPENED;
}
if (!caret) {
- return ORBIS_IME_ERROR_INVALID_ADDRESS;
+ return Error::INVALID_ADDRESS;
}
return g_ime_handler->SetCaret(caret);
}
-s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length) {
+Error PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length) {
LOG_TRACE(Lib_Ime, "called");
if (!g_ime_handler) {
- return ORBIS_IME_ERROR_NOT_OPENED;
+ return Error::NOT_OPENED;
}
if (!text) {
- return ORBIS_IME_ERROR_INVALID_ADDRESS;
+ return Error::INVALID_ADDRESS;
}
return g_ime_handler->SetText(text, length);
@@ -370,7 +370,7 @@ int PS4_SYSV_ABI sceImeSetTextGeometry() {
return ORBIS_OK;
}
-s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
+Error PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
if (g_ime_handler) {
g_ime_handler->Update(handler);
}
@@ -380,10 +380,10 @@ s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
}
if (!g_ime_handler || !g_keyboard_handler) {
- return ORBIS_IME_ERROR_NOT_OPENED;
+ return Error::NOT_OPENED;
}
- return ORBIS_OK;
+ return Error::OK;
}
int PS4_SYSV_ABI sceImeVshClearPreedit() {
diff --git a/src/core/libraries/ime/ime.h b/src/core/libraries/ime/ime.h
index fcf381048..c2b80809c 100644
--- a/src/core/libraries/ime/ime.h
+++ b/src/core/libraries/ime/ime.h
@@ -13,72 +13,6 @@ class SymbolsResolver;
namespace Libraries::Ime {
-constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048;
-
-enum class OrbisImeKeyboardOption : u32 {
- Default = 0,
- Repeat = 1,
- RepeatEachKey = 2,
- AddOsk = 4,
- EffectiveWithIme = 8,
- DisableResume = 16,
- DisableCapslockWithoutShift = 32,
-};
-DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption)
-
-enum class OrbisImeOption : u32 {
- DEFAULT = 0,
- MULTILINE = 1,
- NO_AUTO_CAPITALIZATION = 2,
- PASSWORD = 4,
- LANGUAGES_FORCED = 8,
- EXT_KEYBOARD = 16,
- NO_LEARNING = 32,
- FIXED_POSITION = 64,
- DISABLE_RESUME = 256,
- DISABLE_AUTO_SPACE = 512,
- DISABLE_POSITION_ADJUSTMENT = 2048,
- EXPANDED_PREEDIT_BUFFER = 4096,
- USE_JAPANESE_EISUU_KEY_AS_CAPSLOCK = 8192,
- USE_2K_COORDINATES = 16384,
-};
-DECLARE_ENUM_FLAG_OPERATORS(OrbisImeOption)
-
-struct OrbisImeKeyboardParam {
- OrbisImeKeyboardOption option;
- s8 reserved1[4];
- void* arg;
- OrbisImeEventHandler handler;
- s8 reserved2[8];
-};
-
-struct OrbisImeParam {
- s32 user_id;
- OrbisImeType type;
- u64 supported_languages;
- OrbisImeEnterLabel enter_label;
- OrbisImeInputMethod input_method;
- OrbisImeTextFilter filter;
- OrbisImeOption option;
- u32 maxTextLength;
- char16_t* inputTextBuffer;
- float posx;
- float posy;
- OrbisImeHorizontalAlignment horizontal_alignment;
- OrbisImeVerticalAlignment vertical_alignment;
- void* work;
- void* arg;
- OrbisImeEventHandler handler;
- s8 reserved[8];
-};
-
-struct OrbisImeCaret {
- f32 x;
- f32 y;
- u32 height;
- u32 index;
-};
-
int PS4_SYSV_ABI FinalizeImeModule();
int PS4_SYSV_ABI InitializeImeModule();
int PS4_SYSV_ABI sceImeCheckFilterText();
@@ -98,22 +32,22 @@ int PS4_SYSV_ABI sceImeDisableController();
int PS4_SYSV_ABI sceImeFilterText();
int PS4_SYSV_ABI sceImeForTestFunction();
int PS4_SYSV_ABI sceImeGetPanelPositionAndForm();
-s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height);
-s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId);
+Error PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height);
+Error PS4_SYSV_ABI sceImeKeyboardClose(s32 userId);
int PS4_SYSV_ABI sceImeKeyboardGetInfo();
int PS4_SYSV_ABI sceImeKeyboardGetResourceId();
-s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param);
+Error PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param);
int PS4_SYSV_ABI sceImeKeyboardOpenInternal();
int PS4_SYSV_ABI sceImeKeyboardSetMode();
int PS4_SYSV_ABI sceImeKeyboardUpdate();
-s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended);
+Error PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const OrbisImeParamExtended* extended);
int PS4_SYSV_ABI sceImeOpenInternal();
void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param);
int PS4_SYSV_ABI sceImeSetCandidateIndex();
-s32 PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret);
-s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length);
+Error PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret);
+Error PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length);
int PS4_SYSV_ABI sceImeSetTextGeometry();
-s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler);
+Error PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler);
int PS4_SYSV_ABI sceImeVshClearPreedit();
int PS4_SYSV_ABI sceImeVshClose();
int PS4_SYSV_ABI sceImeVshConfirmPreedit();
diff --git a/src/core/libraries/ime/ime_common.h b/src/core/libraries/ime/ime_common.h
index 96f073dc5..5c0030030 100644
--- a/src/core/libraries/ime/ime_common.h
+++ b/src/core/libraries/ime/ime_common.h
@@ -3,9 +3,108 @@
#pragma once
+#include "common/enum.h"
#include "common/types.h"
#include "core/libraries/rtc/rtc.h"
+constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048;
+constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 2048;
+
+enum class Error : u32 {
+ OK = 0x0,
+ BUSY = 0x80bc0001,
+ NOT_OPENED = 0x80bc0002,
+ NO_MEMORY = 0x80bc0003,
+ CONNECTION_FAILED = 0x80bc0004,
+ TOO_MANY_REQUESTS = 0x80bc0005,
+ INVALID_TEXT = 0x80bc0006,
+ EVENT_OVERFLOW = 0x80bc0007,
+ NOT_ACTIVE = 0x80bc0008,
+ IME_SUSPENDING = 0x80bc0009,
+ DEVICE_IN_USE = 0x80bc000a,
+ INVALID_USER_ID = 0x80bc0010,
+ INVALID_TYPE = 0x80bc0011,
+ INVALID_SUPPORTED_LANGUAGES = 0x80bc0012,
+ INVALID_ENTER_LABEL = 0x80bc0013,
+ INVALID_INPUT_METHOD = 0x80bc0014,
+ INVALID_OPTION = 0x80bc0015,
+ INVALID_MAX_TEXT_LENGTH = 0x80bc0016,
+ INVALID_INPUT_TEXT_BUFFER = 0x80bc0017,
+ INVALID_POSX = 0x80bc0018,
+ INVALID_POSY = 0x80bc0019,
+ INVALID_HORIZONTALIGNMENT = 0x80bc001a,
+ INVALID_VERTICALALIGNMENT = 0x80bc001b,
+ INVALID_EXTENDED = 0x80bc001c,
+ INVALID_KEYBOARD_TYPE = 0x80bc001d,
+ INVALID_WORK = 0x80bc0020,
+ INVALID_ARG = 0x80bc0021,
+ INVALID_HANDLER = 0x80bc0022,
+ NO_RESOURCE_ID = 0x80bc0023,
+ INVALID_MODE = 0x80bc0024,
+ INVALID_PARAM = 0x80bc0030,
+ INVALID_ADDRESS = 0x80bc0031,
+ INVALID_RESERVED = 0x80bc0032,
+ INVALID_TIMING = 0x80bc0033,
+ INTERNAL = 0x80bc00ff,
+ DIALOG_INVALID_TITLE = 0x80bc0101,
+ DIALOG_NOT_RUNNING = 0x80bc0105,
+ DIALOG_NOT_FINISHED = 0x80bc0106,
+ DIALOG_NOT_IN_USE = 0x80bc0107
+};
+
+enum class OrbisImeOption : u32 {
+ DEFAULT = 0,
+ MULTILINE = 1,
+ NO_AUTO_CAPITALIZATION = 2,
+ PASSWORD = 4,
+ LANGUAGES_FORCED = 8,
+ EXT_KEYBOARD = 16,
+ NO_LEARNING = 32,
+ FIXED_POSITION = 64,
+ DISABLE_COPY_PASTE = 128,
+ DISABLE_RESUME = 256,
+ DISABLE_AUTO_SPACE = 512,
+ DISABLE_POSITION_ADJUSTMENT = 2048,
+ EXPANDED_PREEDIT_BUFFER = 4096,
+ USE_JAPANESE_EISUU_KEY_AS_CAPSLOCK = 8192,
+ USE_2K_COORDINATES = 16384,
+};
+DECLARE_ENUM_FLAG_OPERATORS(OrbisImeOption);
+
+enum class OrbisImeLanguage : u64 {
+ DANISH = 0x0000000000000001,
+ GERMAN = 0x0000000000000002,
+ ENGLISH_US = 0x0000000000000004,
+ SPANISH = 0x0000000000000008,
+ FRENCH = 0x0000000000000010,
+ ITALIAN = 0x0000000000000020,
+ DUTCH = 0x0000000000000040,
+ NORWEGIAN = 0x0000000000000080,
+ POLISH = 0x0000000000000100,
+ PORTUGUESE_PT = 0x0000000000000200,
+ RUSSIAN = 0x0000000000000400,
+ FINNISH = 0x0000000000000800,
+ SWEDISH = 0x0000000000001000,
+ JAPANESE = 0x0000000000002000,
+ KOREAN = 0x0000000000004000,
+ SIMPLIFIED_CHINESE = 0x0000000000008000,
+ TRADITIONAL_CHINESE = 0x0000000000010000,
+ PORTUGUESE_BR = 0x0000000000020000,
+ ENGLISH_GB = 0x0000000000040000,
+ TURKISH = 0x0000000000080000,
+ SPANISH_LA = 0x0000000000100000,
+ ARABIC = 0x0000000001000000,
+ FRENCH_CA = 0x0000000002000000,
+ THAI = 0x0000000004000000,
+ CZECH = 0x0000000008000000,
+ GREEK = 0x0000000010000000,
+ INDONESIAN = 0x0000000020000000,
+ VIETNAMESE = 0x0000000040000000,
+ ROMANIAN = 0x0000000080000000,
+ HUNGARIAN = 0x0000000100000000,
+};
+DECLARE_ENUM_FLAG_OPERATORS(OrbisImeLanguage);
+
enum class OrbisImeType : u32 {
Default = 0,
BasicLatin = 1,
@@ -41,6 +140,7 @@ enum class OrbisImeEventId : u32 {
Open = 0,
UpdateText = 1,
UpdateCaret = 2,
+ ChangeSize = 3,
PressClose = 4,
PressEnter = 5,
Abort = 6,
@@ -51,6 +151,10 @@ enum class OrbisImeEventId : u32 {
CandidateDone = 11,
CandidateCancel = 12,
ChangeDevice = 14,
+ JumpToNextObject = 15,
+ JumpToBeforeObject = 16,
+ ChangeWindowType = 17,
+
ChangeInputMethodState = 18,
KeyboardOpen = 256,
@@ -110,6 +214,13 @@ enum class OrbisImeDeviceType : u32 {
RemoteOsk = 3,
};
+enum class OrbisImePanelPriority : u32 {
+ Default = 0,
+ Alphabet = 1,
+ Symbol = 2,
+ Accent = 3,
+};
+
struct OrbisImeRect {
f32 x;
f32 y;
@@ -117,8 +228,22 @@ struct OrbisImeRect {
u32 height;
};
+struct OrbisImeColor {
+ u8 r;
+ u8 g;
+ u8 b;
+ u8 a;
+};
+
+enum class OrbisImeTextAreaMode : u32 {
+ Disable = 0,
+ Edit = 1,
+ Preedit = 2,
+ Select = 3,
+};
+
struct OrbisImeTextAreaProperty {
- u32 mode; // OrbisImeTextAreaMode
+ OrbisImeTextAreaMode mode;
u32 index;
s32 length;
};
@@ -135,14 +260,14 @@ struct OrbisImeKeycode {
char16_t character;
u32 status;
OrbisImeKeyboardType type;
- s32 user_id;
+ s32 user_id; // Todo: switch to OrbisUserServiceUserId
u32 resource_id;
Libraries::Rtc::OrbisRtcTick timestamp;
};
struct OrbisImeKeyboardResourceIdArray {
- s32 userId;
- u32 resourceId[5];
+ s32 user_id; // Todo: switch to OrbisUserServiceUserId
+ u32 resource_id[5];
};
enum class OrbisImeCaretMovementDirection : u32 {
@@ -159,6 +284,16 @@ enum class OrbisImeCaretMovementDirection : u32 {
Bottom = 10,
};
+enum class OrbisImePanelType : u32 {
+ Hide = 0,
+ Osk = 1,
+ Dialog = 2,
+ Candidate = 3,
+ Edit = 4,
+ EditAndCandidate = 5,
+ Accessibility = 6,
+};
+
union OrbisImeEventParam {
OrbisImeRect rect;
OrbisImeEditText text;
@@ -168,6 +303,7 @@ union OrbisImeEventParam {
char16_t* candidate_word;
s32 candidate_index;
OrbisImeDeviceType device_type;
+ OrbisImePanelType panel_type;
u32 input_method_state;
s8 reserved[64];
};
@@ -177,7 +313,95 @@ struct OrbisImeEvent {
OrbisImeEventParam param;
};
+using OrbisImeExtKeyboardFilter = PS4_SYSV_ABI int (*)(const OrbisImeKeycode* srcKeycode,
+ u16* outKeycode, u32* outStatus,
+ void* reserved);
+
using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength,
const char16_t* srcText, u32 srcTextLength);
using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e);
+
+enum class OrbisImeKeyboardOption : u32 {
+ Default = 0,
+ Repeat = 1,
+ RepeatEachKey = 2,
+ AddOsk = 4,
+ EffectiveWithIme = 8,
+ DisableResume = 16,
+ DisableCapslockWithoutShift = 32,
+};
+DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption)
+
+struct OrbisImeKeyboardParam {
+ OrbisImeKeyboardOption option;
+ s8 reserved1[4];
+ void* arg;
+ OrbisImeEventHandler handler;
+ s8 reserved2[8];
+};
+
+struct OrbisImeParam {
+ s32 user_id; // Todo: switch to OrbisUserServiceUserId
+ OrbisImeType type;
+ u64 supported_languages; // OrbisImeLanguage flags
+ OrbisImeEnterLabel enter_label;
+ OrbisImeInputMethod input_method;
+ OrbisImeTextFilter filter;
+ OrbisImeOption option;
+ u32 maxTextLength;
+ char16_t* inputTextBuffer;
+ f32 posx;
+ f32 posy;
+ OrbisImeHorizontalAlignment horizontal_alignment;
+ OrbisImeVerticalAlignment vertical_alignment;
+ void* work;
+ void* arg;
+ OrbisImeEventHandler handler;
+ s8 reserved[8];
+};
+
+struct OrbisImeCaret {
+ f32 x;
+ f32 y;
+ u32 height;
+ u32 index;
+};
+
+struct OrbisImeDialogParam {
+ s32 user_id;
+ OrbisImeType type;
+ u64 supported_languages; // OrbisImeLanguage flags
+ OrbisImeEnterLabel enter_label;
+ OrbisImeInputMethod input_method;
+ OrbisImeTextFilter filter;
+ OrbisImeOption option;
+ u32 max_text_length;
+ char16_t* input_text_buffer;
+ f32 posx;
+ f32 posy;
+ OrbisImeHorizontalAlignment horizontal_alignment;
+ OrbisImeVerticalAlignment vertical_alignment;
+ const char16_t* placeholder;
+ const char16_t* title;
+ s8 reserved[16];
+};
+
+struct OrbisImeParamExtended {
+ u32 option; // OrbisImeExtOption flags
+ OrbisImeColor color_base;
+ OrbisImeColor color_line;
+ OrbisImeColor color_text_field;
+ OrbisImeColor color_preedit;
+ OrbisImeColor color_button_default;
+ OrbisImeColor color_button_function;
+ OrbisImeColor color_button_symbol;
+ OrbisImeColor color_text;
+ OrbisImeColor color_special;
+ OrbisImePanelPriority priority;
+ char* additional_dictionary_path;
+ OrbisImeExtKeyboardFilter ext_keyboard_filter;
+ u32 disable_device;
+ u32 ext_keyboard_mode;
+ s8 reserved[60];
+};
diff --git a/src/core/libraries/ime/ime_dialog.cpp b/src/core/libraries/ime/ime_dialog.cpp
index f8e8bc137..ee6a5868d 100644
--- a/src/core/libraries/ime/ime_dialog.cpp
+++ b/src/core/libraries/ime/ime_dialog.cpp
@@ -20,19 +20,19 @@ static OrbisImeDialogResult g_ime_dlg_result{};
static ImeDialogState g_ime_dlg_state{};
static ImeDialogUi g_ime_dlg_ui;
-static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) {
- if (False(~option &
- (OrbisImeDialogOption::Multiline | OrbisImeDialogOption::NoAutoCompletion))) {
+static bool IsValidOption(OrbisImeOption option, OrbisImeType type) {
+ if (False(~option & (OrbisImeOption::MULTILINE |
+ OrbisImeOption::NO_AUTO_CAPITALIZATION /* NoAutoCompletion */))) {
return false;
}
- if (True(option & OrbisImeDialogOption::Multiline) && type != OrbisImeType::Default &&
+ if (True(option & OrbisImeOption::MULTILINE) && type != OrbisImeType::Default &&
type != OrbisImeType::BasicLatin) {
return false;
}
- if (True(option & OrbisImeDialogOption::NoAutoCompletion) && type != OrbisImeType::Number &&
- type != OrbisImeType::BasicLatin) {
+ if (True(option & OrbisImeOption::NO_AUTO_CAPITALIZATION /* NoAutoCompletion */) &&
+ type != OrbisImeType::Number && type != OrbisImeType::BasicLatin) {
return false;
}
@@ -96,7 +96,7 @@ Error PS4_SYSV_ABI sceImeDialogGetPanelSize(const OrbisImeDialogParam* param, u3
case OrbisImeType::Url:
case OrbisImeType::Mail:
*width = 500; // original: 793
- if (True(param->option & OrbisImeDialogOption::Multiline)) {
+ if (True(param->option & OrbisImeOption::MULTILINE)) {
*height = 300; // original: 576
} else {
*height = 150; // original: 476
@@ -230,21 +230,20 @@ void DumpImeDialogParam(const Libraries::ImeDialog::OrbisImeDialogParam* param,
}
Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) {
-
- DumpImeDialogParam(param, extended);
-
+ LOG_INFO(Lib_ImeDialog, ">> sceImeDialogInit: entering, param={}, extended={}",
+ static_cast(param), static_cast(extended));
if (g_ime_dlg_status != OrbisImeDialogStatus::None) {
- LOG_INFO(Lib_ImeDialog, "IME dialog is already running");
+ LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: busy (status=%u)", (u32)g_ime_dlg_status);
return Error::BUSY;
}
if (param == nullptr) {
- LOG_INFO(Lib_ImeDialog, "called with param (NULL)");
+ LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: param is null");
return Error::INVALID_ADDRESS;
}
if (!magic_enum::enum_contains(param->type)) {
- LOG_INFO(Lib_ImeDialog, "Invalid param->type");
+ LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid param->type=%u", (u32)param->type);
return Error::INVALID_ADDRESS;
}
@@ -252,16 +251,14 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt
// TODO: do correct param->supportedLanguages validation
if (param->posx < 0.0f ||
- param->posx >=
- MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LargeResolution)]) {
- LOG_INFO(Lib_ImeDialog, "Invalid param->posx");
+ param->posx >= MAX_X_POSITIONS[False(param->option & OrbisImeOption::USE_2K_COORDINATES)]) {
+ LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid posx=%f", param->posx);
return Error::INVALID_POSX;
}
if (param->posy < 0.0f ||
- param->posy >=
- MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LargeResolution)]) {
- LOG_INFO(Lib_ImeDialog, "Invalid param->posy");
+ param->posy >= MAX_Y_POSITIONS[False(param->option & OrbisImeOption::USE_2K_COORDINATES)]) {
+ LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid posy=%f", param->posy);
return Error::INVALID_POSY;
}
@@ -276,12 +273,13 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt
}
if (!IsValidOption(param->option, param->type)) {
- LOG_INFO(Lib_ImeDialog, "Invalid param->option");
+ LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid option=0x%X for type=%u",
+ static_cast(param->option), (u32)param->type);
return Error::INVALID_PARAM;
}
if (param->input_text_buffer == nullptr) {
- LOG_INFO(Lib_ImeDialog, "Invalid param->inputTextBuffer");
+ LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: input_text_buffer is null");
return Error::INVALID_INPUT_TEXT_BUFFER;
}
@@ -304,16 +302,24 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt
}
}
- if (param->max_text_length > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) {
- LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength ({})", param->max_text_length);
+ if (param->max_text_length == 0 || param->max_text_length > ORBIS_IME_MAX_TEXT_LENGTH) {
+ LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid max_text_length=%u",
+ param->max_text_length);
return Error::INVALID_MAX_TEXT_LENGTH;
}
+ // Title string validation
+ if (param->title != nullptr && !std::char_traits::length(param->title)) {
+ LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: title is empty");
+ return Error::INVALID_PARAM;
+ }
+
g_ime_dlg_result = {};
g_ime_dlg_state = ImeDialogState(param, extended);
g_ime_dlg_status = OrbisImeDialogStatus::Running;
g_ime_dlg_ui = ImeDialogUi(&g_ime_dlg_state, &g_ime_dlg_status, &g_ime_dlg_result);
+ LOG_INFO(Lib_ImeDialog, "<< sceImeDialogInit: successful, status now=Running");
return Error::OK;
}
diff --git a/src/core/libraries/ime/ime_dialog.h b/src/core/libraries/ime/ime_dialog.h
index 526e5f022..a056fdd5e 100644
--- a/src/core/libraries/ime/ime_dialog.h
+++ b/src/core/libraries/ime/ime_dialog.h
@@ -13,50 +13,6 @@ class SymbolsResolver;
namespace Libraries::ImeDialog {
-constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 2048;
-
-enum class Error : u32 {
- OK = 0x0,
- BUSY = 0x80bc0001,
- NOT_OPENED = 0x80bc0002,
- NO_MEMORY = 0x80bc0003,
- CONNECTION_FAILED = 0x80bc0004,
- TOO_MANY_REQUESTS = 0x80bc0005,
- INVALID_TEXT = 0x80bc0006,
- EVENT_OVERFLOW = 0x80bc0007,
- NOT_ACTIVE = 0x80bc0008,
- IME_SUSPENDING = 0x80bc0009,
- DEVICE_IN_USE = 0x80bc000a,
- INVALID_USER_ID = 0x80bc0010,
- INVALID_TYPE = 0x80bc0011,
- INVALID_SUPPORTED_LANGUAGES = 0x80bc0012,
- INVALID_ENTER_LABEL = 0x80bc0013,
- INVALID_INPUT_METHOD = 0x80bc0014,
- INVALID_OPTION = 0x80bc0015,
- INVALID_MAX_TEXT_LENGTH = 0x80bc0016,
- INVALID_INPUT_TEXT_BUFFER = 0x80bc0017,
- INVALID_POSX = 0x80bc0018,
- INVALID_POSY = 0x80bc0019,
- INVALID_HORIZONTALIGNMENT = 0x80bc001a,
- INVALID_VERTICALALIGNMENT = 0x80bc001b,
- INVALID_EXTENDED = 0x80bc001c,
- INVALID_KEYBOARD_TYPE = 0x80bc001d,
- INVALID_WORK = 0x80bc0020,
- INVALID_ARG = 0x80bc0021,
- INVALID_HANDLER = 0x80bc0022,
- NO_RESOURCE_ID = 0x80bc0023,
- INVALID_MODE = 0x80bc0024,
- INVALID_PARAM = 0x80bc0030,
- INVALID_ADDRESS = 0x80bc0031,
- INVALID_RESERVED = 0x80bc0032,
- INVALID_TIMING = 0x80bc0033,
- INTERNAL = 0x80bc00ff,
- DIALOG_INVALID_TITLE = 0x80bc0101,
- DIALOG_NOT_RUNNING = 0x80bc0105,
- DIALOG_NOT_FINISHED = 0x80bc0106,
- DIALOG_NOT_IN_USE = 0x80bc0107,
-};
-
enum class OrbisImeDialogStatus : u32 {
None = 0,
Running = 1,
@@ -69,87 +25,11 @@ enum class OrbisImeDialogEndStatus : u32 {
Aborted = 2,
};
-enum class OrbisImeDialogOption : u32 {
- Default = 0,
- Multiline = 1,
- NoAutoCorrection = 2,
- NoAutoCompletion = 4,
- // TODO: Document missing options
- LargeResolution = 1024,
-};
-DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption)
-
-enum class OrbisImePanelPriority : u32 {
- Default = 0,
- Alphabet = 1,
- Symbol = 2,
- Accent = 3,
-};
-
-struct OrbisImeColor {
- u8 r;
- u8 g;
- u8 b;
- u8 a;
-};
-
struct OrbisImeDialogResult {
OrbisImeDialogEndStatus endstatus;
s32 reserved[12];
};
-struct OrbisImeKeycode {
- u16 keycode;
- char16_t character;
- u32 status;
- OrbisImeKeyboardType type;
- s32 user_id;
- u32 resource_id;
- u64 timestamp;
-};
-
-using OrbisImeExtKeyboardFilter = PS4_SYSV_ABI int (*)(const OrbisImeKeycode* srcKeycode,
- u16* outKeycode, u32* outStatus,
- void* reserved);
-
-struct OrbisImeDialogParam {
- s32 user_id;
- OrbisImeType type;
- u64 supported_languages;
- OrbisImeEnterLabel enter_label;
- OrbisImeInputMethod input_method;
- OrbisImeTextFilter filter;
- OrbisImeDialogOption option;
- u32 max_text_length;
- char16_t* input_text_buffer;
- float posx;
- float posy;
- OrbisImeHorizontalAlignment horizontal_alignment;
- OrbisImeVerticalAlignment vertical_alignment;
- const char16_t* placeholder;
- const char16_t* title;
- s8 reserved[16];
-};
-
-struct OrbisImeParamExtended {
- u32 option; // OrbisImeDialogOptionExtended
- OrbisImeColor color_base;
- OrbisImeColor color_line;
- OrbisImeColor color_text_field;
- OrbisImeColor color_preedit;
- OrbisImeColor color_button_default;
- OrbisImeColor color_button_function;
- OrbisImeColor color_button_symbol;
- OrbisImeColor color_text;
- OrbisImeColor color_special;
- OrbisImePanelPriority priority;
- char* additional_dictionary_path;
- OrbisImeExtKeyboardFilter ext_keyboard_filter;
- uint32_t disable_device;
- uint32_t ext_keyboard_mode;
- int8_t reserved[60];
-};
-
Error PS4_SYSV_ABI sceImeDialogAbort();
Error PS4_SYSV_ABI sceImeDialogForceClose();
Error PS4_SYSV_ABI sceImeDialogForTestFunction();
diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp
index 92a1a630b..9679a4b77 100644
--- a/src/core/libraries/ime/ime_dialog_ui.cpp
+++ b/src/core/libraries/ime/ime_dialog_ui.cpp
@@ -48,15 +48,18 @@ static void KeyboardCallbackBridge(const VirtualKeyEvent* evt) {
* ImeDialogState : constructors, helpers
*─────────────────────────────────────────────────────────────*/
ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param,
- const OrbisImeParamExtended* extended)
- : extended_param_(extended) {
+ const OrbisImeParamExtended* extended) {
+ LOG_INFO(Lib_ImeDialog, ">> ImeDialogState::Ctor: param={}, text_buffer={}",
+ static_cast(param),
+ static_cast(param ? param->input_text_buffer : nullptr));
if (!param) {
+ LOG_ERROR(Lib_ImeDialog, " param==nullptr, returning without init");
return;
}
/* basic param copy */
user_id = param->user_id;
- is_multi_line = True(param->option & OrbisImeDialogOption::Multiline);
+ is_multi_line = True(param->option & OrbisImeOption::MULTILINE);
is_numeric = param->type == OrbisImeType::Number;
type = param->type;
enter_label = param->enter_label;
@@ -292,6 +295,7 @@ void ImeDialogUi::Free() {
*─────────────────────────────────────────────────────────────*/
void ImeDialogUi::Draw() {
std::unique_lock lock{draw_mutex};
+ LOG_INFO(Lib_ImeDialog, ">> ImeDialogUi::Draw: first_render=%d", first_render);
if (!state) {
return;
@@ -329,9 +333,13 @@ void ImeDialogUi::Draw() {
/* ---------- input box ---------- */
if (state->is_multi_line) {
+ LOG_INFO(Lib_ImeDialog, " Drawing multi-line widget…");
DrawMultiLineInputText();
+ LOG_INFO(Lib_ImeDialog, " Done DrawMultiLineInputText");
} else {
+ LOG_INFO(Lib_ImeDialog, " Drawing input text widget…");
DrawInputText();
+ LOG_INFO(Lib_ImeDialog, " Done DrawInputText");
}
/* ---------- dummy prediction bar with Cancel button ---------- */
@@ -351,6 +359,7 @@ void ImeDialogUi::Draw() {
ImGui::PopStyleColor();
first_render = false;
+ LOG_INFO(Lib_ImeDialog, "<< ImeDialogUi::Draw complete");
}
/*─────────────────────────────────────────────────────────────*
@@ -375,7 +384,7 @@ void ImeDialogUi::DrawInputText() {
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
- state->max_text_length, input_size, ImGuiInputTextFlags_CallbackCharFilter,
+ state->max_text_length + 1, input_size, ImGuiInputTextFlags_CallbackCharFilter,
InputTextCallback, this)) {
state->input_changed = true;
}
@@ -421,7 +430,7 @@ void ImeDialogUi::DrawMultiLineInputText() {
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
- state->max_text_length, input_size, flags, InputTextCallback, this)) {
+ state->max_text_length + 1, input_size, flags, InputTextCallback, this)) {
state->input_changed = true;
}
if (ImGui::IsItemHovered()) {
@@ -437,13 +446,19 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ImeDialogUi* ui = static_cast(data->UserData);
ASSERT(ui);
+ LOG_DEBUG(Lib_ImeDialog, ">> InputTextCallback: EventFlag={}, EventChar={}", data->EventFlag,
+ data->EventChar);
+
// Should we filter punctuation?
if (ui->state->is_numeric && (data->EventChar < '0' || data->EventChar > '9') &&
data->EventChar != '\b' && data->EventChar != ',' && data->EventChar != '.') {
+ LOG_INFO(Lib_ImeDialog, "InputTextCallback: rejecting non-digit char '{}'",
+ static_cast(data->EventChar));
return 1;
}
if (!ui->state->keyboard_filter) {
+ LOG_DEBUG(Lib_ImeDialog, "InputTextCallback: no keyboard_filter, accepting char");
return 0;
}
@@ -459,20 +474,24 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
// use the current language?)
.user_id = ui->state->user_id,
.resource_id = 0,
- .timestamp = 0,
+ .timestamp = {0},
};
if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) {
- LOG_ERROR(Lib_ImeDialog, "Failed to convert orbis char to utf8");
+ LOG_ERROR(Lib_ImeDialog, "InputTextCallback: ConvertUTF8ToOrbis failed");
return 0;
}
+ LOG_DEBUG(Lib_ImeDialog, "InputTextCallback: converted to Orbis char={:#X}",
+ static_cast(src_keycode.character));
src_keycode.keycode = src_keycode.character; // TODO set this to the correct value
u16 out_keycode;
u32 out_status;
- ui->state->CallKeyboardFilter(&src_keycode, &out_keycode, &out_status);
-
+ bool keep = ui->state->CallKeyboardFilter(&src_keycode, &out_keycode, &out_status);
+ LOG_DEBUG(Lib_ImeDialog,
+ "InputTextCallback: CallKeyboardFilter returned %s (keycode=0x%X, status=0x%X)",
+ keep ? "true" : "false", out_keycode, out_status);
// TODO. set the keycode
return 0;
diff --git a/src/core/libraries/ime/ime_ui.cpp b/src/core/libraries/ime/ime_ui.cpp
index 37f25e200..c49c70ede 100644
--- a/src/core/libraries/ime/ime_ui.cpp
+++ b/src/core/libraries/ime/ime_ui.cpp
@@ -199,7 +199,7 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
eventParam.caret_index = data->CursorPos;
eventParam.area_num = 1;
- eventParam.text_area[0].mode = 1; // Edit mode
+ eventParam.text_area[0].mode = OrbisImeTextAreaMode::Edit;
eventParam.text_area[0].index = data->CursorPos;
eventParam.text_area[0].length = data->BufTextLen;
diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp
index 958019cd3..4d1b116c5 100644
--- a/src/core/libraries/kernel/equeue.cpp
+++ b/src/core/libraries/kernel/equeue.cpp
@@ -98,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 = [&] {
@@ -120,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) {
@@ -140,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);
}
@@ -172,38 +178,46 @@ 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;
}
bool EqueueInternal::EventExists(u64 id, s16 filter) {
@@ -266,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) {
@@ -327,6 +332,11 @@ 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;
}
diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h
index a0367c66a..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"
@@ -98,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) {
@@ -129,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) {}
@@ -145,13 +158,14 @@ public:
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;
}
@@ -164,8 +178,8 @@ 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;
};
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev);
diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp
index ad372325c..76d1a3339 100644
--- a/src/core/libraries/kernel/file_system.cpp
+++ b/src/core/libraries/kernel/file_system.cpp
@@ -293,6 +293,7 @@ s64 PS4_SYSV_ABI write(s32 fd, const void* buf, size_t nbytes) {
}
return result;
}
+
return file->f.WriteRaw(buf, nbytes);
}
@@ -750,7 +751,24 @@ s32 PS4_SYSV_ABI posix_rename(const char* from, const char* to) {
*__Error() = POSIX_ENOTEMPTY;
return -1;
}
+
+ // On Windows, std::filesystem::rename will error if the file has been opened before.
std::filesystem::copy(src_path, dst_path, std::filesystem::copy_options::overwrite_existing);
+ auto* h = Common::Singleton::Instance();
+ auto file = h->GetFile(src_path);
+ if (file) {
+ // We need to force ReadWrite if the file had Write access before
+ // Otherwise f.Open will clear the file contents.
+ auto access_mode = file->f.GetAccessMode() == Common::FS::FileAccessMode::Write
+ ? Common::FS::FileAccessMode::ReadWrite
+ : file->f.GetAccessMode();
+ file->f.Close();
+ std::filesystem::remove(src_path);
+ file->f.Open(dst_path, access_mode);
+ } else {
+ std::filesystem::remove(src_path);
+ }
+
return ORBIS_OK;
}
@@ -1050,6 +1068,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 180850217..61d2e2f2b 100644
--- a/src/core/libraries/kernel/kernel.cpp
+++ b/src/core/libraries/kernel/kernel.cpp
@@ -76,21 +76,21 @@ static PS4_SYSV_ABI void stack_chk_fail() {
UNREACHABLE();
}
-static thread_local int g_posix_errno = 0;
+static thread_local s32 g_posix_errno = 0;
-int* PS4_SYSV_ABI __Error() {
+s32* PS4_SYSV_ABI __Error() {
return &g_posix_errno;
}
-void ErrSceToPosix(int error) {
+void ErrSceToPosix(s32 error) {
g_posix_errno = error - ORBIS_KERNEL_ERROR_UNKNOWN;
}
-int ErrnoToSceKernelError(int error) {
+s32 ErrnoToSceKernelError(s32 error) {
return error + ORBIS_KERNEL_ERROR_UNKNOWN;
}
-void SetPosixErrno(int e) {
+void SetPosixErrno(s32 e) {
// Some error numbers are different between supported OSes
switch (e) {
case EPERM:
@@ -132,15 +132,15 @@ void SetPosixErrno(int e) {
}
}
-static uint64_t g_mspace_atomic_id_mask = 0;
-static uint64_t g_mstate_table[64] = {0};
+static u64 g_mspace_atomic_id_mask = 0;
+static u64 g_mstate_table[64] = {0};
struct HeapInfoInfo {
- uint64_t size = sizeof(HeapInfoInfo);
- uint32_t flag;
- uint32_t getSegmentInfo;
- uint64_t* mspace_atomic_id_mask;
- uint64_t* mstate_table;
+ u64 size = sizeof(HeapInfoInfo);
+ u32 flag;
+ u32 getSegmentInfo;
+ u64* mspace_atomic_id_mask;
+ u64* mstate_table;
};
void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) {
@@ -159,7 +159,7 @@ struct OrbisKernelUuid {
};
static_assert(sizeof(OrbisKernelUuid) == 0x10);
-int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
+s32 PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
if (!orbisUuid) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
@@ -176,7 +176,7 @@ int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) {
return ORBIS_OK;
}
-int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) {
+s32 PS4_SYSV_ABI kernel_ioctl(s32 fd, u64 cmd, VA_ARGS) {
auto* h = Common::Singleton::Instance();
auto* file = h->GetFile(fd);
if (file == nullptr) {
@@ -190,7 +190,7 @@ int PS4_SYSV_ABI kernel_ioctl(int fd, u64 cmd, VA_ARGS) {
return -1;
}
VA_CTX(ctx);
- int result = file->device->ioctl(cmd, &ctx);
+ s32 result = file->device->ioctl(cmd, &ctx);
LOG_TRACE(Lib_Kernel, "ioctl: fd = {:X} cmd = {:X} result = {}", fd, cmd, result);
if (result < 0) {
ErrSceToPosix(result);
@@ -204,15 +204,15 @@ const char* PS4_SYSV_ABI sceKernelGetFsSandboxRandomWord() {
return path;
}
-int PS4_SYSV_ABI _sigprocmask() {
+s32 PS4_SYSV_ABI _sigprocmask() {
return ORBIS_OK;
}
-int PS4_SYSV_ABI posix_getpagesize() {
+s32 PS4_SYSV_ABI posix_getpagesize() {
return 16_KB;
}
-int PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s,
+s32 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);
@@ -221,7 +221,7 @@ int PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s,
LOG_ERROR(Lib_Net, "socket id is invalid = {}", s);
return -1;
}
- int returncode = sock->GetSocketAddress(addr, paddrlen);
+ s32 returncode = sock->GetSocketAddress(addr, paddrlen);
if (returncode >= 0) {
LOG_ERROR(Lib_Net, "return code : {:#x}", (u32)returncode);
return 0;
@@ -230,6 +230,19 @@ int PS4_SYSV_ABI posix_getsockname(Libraries::Net::OrbisNetId s,
LOG_ERROR(Lib_Net, "error code returned : {:#x}", (u32)returncode);
return -1;
}
+
+// stubbed on non-devkit consoles
+s32 PS4_SYSV_ABI sceKernelGetGPI() {
+ LOG_DEBUG(Kernel, "called");
+ return ORBIS_OK;
+}
+
+// stubbed on non-devkit consoles
+s32 PS4_SYSV_ABI sceKernelSetGPO() {
+ LOG_DEBUG(Kernel, "called");
+ return ORBIS_OK;
+}
+
void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
service_thread = std::jthread{KernelServiceThread};
@@ -273,6 +286,13 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
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);
+
+ LIB_FUNCTION("4oXYe9Xmk0Q", "libkernel", 1, "libkernel", 1, 1, sceKernelGetGPI);
+ LIB_FUNCTION("ca7v6Cxulzs", "libkernel", 1, "libkernel", 1, 1, sceKernelSetGPO);
}
} // namespace Libraries::Kernel
diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h
index aaa22aec1..0529c06d5 100644
--- a/src/core/libraries/kernel/kernel.h
+++ b/src/core/libraries/kernel/kernel.h
@@ -12,10 +12,10 @@ class SymbolsResolver;
namespace Libraries::Kernel {
-void ErrSceToPosix(int result);
-int ErrnoToSceKernelError(int e);
-void SetPosixErrno(int e);
-int* PS4_SYSV_ABI __Error();
+void ErrSceToPosix(s32 result);
+s32 ErrnoToSceKernelError(s32 e);
+void SetPosixErrno(s32 e);
+s32* PS4_SYSV_ABI __Error();
template
struct OrbisWrapperImpl;
@@ -33,7 +33,7 @@ struct OrbisWrapperImpl {
#define ORBIS(func) (Libraries::Kernel::OrbisWrapperImpl::wrap)
-int* PS4_SYSV_ABI __Error();
+s32* PS4_SYSV_ABI __Error();
void RegisterKernel(Core::Loader::SymbolsResolver* sym);
diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp
index cb41a664a..e0c359f2c 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"
@@ -24,8 +23,8 @@ u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize() {
return memory->GetTotalDirectSize();
}
-int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len,
- u64 alignment, int memoryType, s64* physAddrOut) {
+s32 PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len,
+ u64 alignment, s32 memoryType, s64* physAddrOut) {
if (searchStart < 0 || searchEnd < 0) {
LOG_ERROR(Kernel_Vmm, "Invalid parameters!");
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -72,13 +71,13 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u
return ORBIS_OK;
}
-s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType,
+s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(u64 len, u64 alignment, s32 memoryType,
s64* physAddrOut) {
const auto searchEnd = static_cast(sceKernelGetDirectMemorySize());
return sceKernelAllocateDirectMemory(0, searchEnd, len, alignment, memoryType, physAddrOut);
}
-s32 PS4_SYSV_ABI sceKernelCheckedReleaseDirectMemory(u64 start, size_t len) {
+s32 PS4_SYSV_ABI sceKernelCheckedReleaseDirectMemory(u64 start, u64 len) {
if (len == 0) {
return ORBIS_OK;
}
@@ -88,7 +87,7 @@ s32 PS4_SYSV_ABI sceKernelCheckedReleaseDirectMemory(u64 start, size_t len) {
return ORBIS_OK;
}
-s32 PS4_SYSV_ABI sceKernelReleaseDirectMemory(u64 start, size_t len) {
+s32 PS4_SYSV_ABI sceKernelReleaseDirectMemory(u64 start, u64 len) {
if (len == 0) {
return ORBIS_OK;
}
@@ -97,11 +96,10 @@ s32 PS4_SYSV_ABI sceKernelReleaseDirectMemory(u64 start, size_t len) {
return ORBIS_OK;
}
-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);
+s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchEnd, u64 alignment,
+ u64* physAddrOut, u64* sizeOut) {
+ LOG_INFO(Kernel_Vmm, "called searchStart = {:#x}, searchEnd = {:#x}, alignment = {:#x}",
+ searchStart, searchEnd, alignment);
if (physAddrOut == nullptr || sizeOut == nullptr) {
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -110,7 +108,7 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE
auto* memory = Core::Memory::Instance();
PAddr physAddr{};
- size_t size{};
+ u64 size{};
s32 result = memory->DirectQueryAvailable(searchStart, searchEnd, alignment, &physAddr, &size);
if (size == 0) {
@@ -123,14 +121,14 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE
return result;
}
-s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
- size_t infoSize) {
+s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, s32 flags, OrbisVirtualQueryInfo* info,
+ u64 infoSize) {
LOG_INFO(Kernel_Vmm, "called addr = {}, flags = {:#x}", fmt::ptr(addr), flags);
auto* memory = Core::Memory::Instance();
return memory->VirtualQuery(std::bit_cast(addr), flags, info);
}
-s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u64 alignment) {
+s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, s32 flags, u64 alignment) {
LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}, flags = {:#x}, alignment = {:#x}",
fmt::ptr(*addr), len, flags, alignment);
if (addr == nullptr) {
@@ -152,14 +150,15 @@ 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));
}
return result;
}
-int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags,
+s32 PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, s32 prot, s32 flags,
s64 directMemoryStart, u64 alignment,
const char* name) {
LOG_INFO(Kernel_Vmm,
@@ -202,7 +201,7 @@ int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, i
return ret;
}
-int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags,
+s32 PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, s32 prot, s32 flags,
s64 directMemoryStart, u64 alignment) {
LOG_INFO(Kernel_Vmm, "called, redirected to sceKernelMapNamedDirectMemory");
return sceKernelMapNamedDirectMemory(addr, len, prot, flags, directMemoryStart, alignment,
@@ -222,9 +221,10 @@ s32 PS4_SYSV_ABI sceKernelMapDirectMemory2(void** addr, u64 len, s32 type, s32 p
return ret;
}
-s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
- int flags, const char* name) {
-
+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;
@@ -243,46 +243,55 @@ 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");
}
-int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot) {
+s32 PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot) {
auto* memory = Core::Memory::Instance();
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);
}
-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);
+s32 PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, s32 flags, OrbisQueryInfo* query_info,
+ u64 infoSize) {
+ LOG_INFO(Kernel_Vmm, "called offset = {:#x}, flags = {:#x}", offset, flags);
auto* memory = Core::Memory::Instance();
return memory->DirectMemoryQuery(offset, flags == 1, query_info);
}
-s32 PS4_SYSV_ABI sceKernelAvailableFlexibleMemorySize(size_t* out_size) {
+s32 PS4_SYSV_ABI sceKernelAvailableFlexibleMemorySize(u64* out_size) {
auto* memory = Core::Memory::Instance();
*out_size = memory->GetAvailableFlexibleSize();
LOG_INFO(Kernel_Vmm, "called size = {:#x}", *out_size);
@@ -294,7 +303,7 @@ void PS4_SYSV_ABI _sceKernelRtldSetApplicationHeapAPI(void* func[]) {
linker->SetHeapAPI(func);
}
-int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut,
+s32 PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, s32* directMemoryTypeOut,
void** directMemoryStartOut,
void** directMemoryEndOut) {
LOG_WARNING(Kernel_Vmm, "called, direct memory addr = {:#x}", addr);
@@ -303,23 +312,23 @@ int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut
directMemoryEndOut);
}
-int PS4_SYSV_ABI sceKernelIsStack(void* addr, void** start, void** end) {
+s32 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) {
+s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, s32 numEntries,
+ s32* numEntriesOut) {
return sceKernelBatchMap2(entries, numEntries, numEntriesOut,
MemoryFlags::SCE_KERNEL_MAP_FIXED); // 0x10, 0x410?
}
-s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries,
- int* numEntriesOut, int flags) {
- int result = ORBIS_OK;
- int processed = 0;
- for (int i = 0; i < numEntries; i++, processed++) {
+s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, s32 numEntries,
+ s32* numEntriesOut, s32 flags) {
+ s32 result = ORBIS_OK;
+ s32 processed = 0;
+ for (s32 i = 0; i < numEntries; i++, processed++) {
if (entries == nullptr || entries[i].length == 0 || entries[i].operation > 4) {
result = ORBIS_KERNEL_ERROR_EINVAL;
break; // break and assign a value to numEntriesOut.
@@ -344,7 +353,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;
@@ -359,7 +368,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);
@@ -380,7 +389,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;
@@ -396,8 +405,8 @@ 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;
@@ -439,10 +448,10 @@ 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 (len == 0 || !Common::Is2MBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!");
@@ -456,14 +465,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;
@@ -482,7 +493,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;
@@ -523,12 +534,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry*
break;
}
case OrbisKernelMemoryPoolOpcode::Protect: {
- result = sceKernelMProtect(entry.protect_params.addr, entry.protect_params.len,
+ result = sceKernelMprotect(entry.protect_params.addr, entry.protect_params.len,
entry.protect_params.prot);
break;
}
case OrbisKernelMemoryPoolOpcode::TypeProtect: {
- result = sceKernelMTypeProtect(
+ result = sceKernelMtypeprotect(
entry.type_protect_params.addr, entry.type_protect_params.len,
entry.type_protect_params.type, entry.type_protect_params.prot);
break;
@@ -553,30 +564,49 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry*
return result;
}
-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();
+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);
+ const auto is_exec = True(mem_prot & Core::MemoryProt::CpuExec);
+
+ 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, "anon", is_exec);
} 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) {
@@ -589,7 +619,7 @@ s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) {
return ORBIS_OK;
}
-int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) {
+s32 PS4_SYSV_ABI sceKernelMunmap(void* addr, u64 len) {
LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len);
if (len == 0) {
return ORBIS_KERNEL_ERROR_EINVAL;
@@ -598,8 +628,8 @@ int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) {
return memory->UnmapMemory(std::bit_cast(addr), len);
}
-int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) {
- int result = sceKernelMunmap(addr, len);
+s32 PS4_SYSV_ABI posix_munmap(void* addr, u64 len) {
+ s32 result = sceKernelMunmap(addr, len);
if (result < 0) {
LOG_ERROR(Kernel_Pthread, "posix_munmap: error = {}", result);
ErrSceToPosix(result);
@@ -608,12 +638,12 @@ int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) {
return result;
}
-static constexpr int MAX_PRT_APERTURES = 3;
+static constexpr s32 MAX_PRT_APERTURES = 3;
static constexpr VAddr PRT_AREA_START_ADDR = 0x1000000000;
-static constexpr size_t PRT_AREA_SIZE = 0xec00000000;
-static std::array, MAX_PRT_APERTURES> PrtApertures{};
+static constexpr u64 PRT_AREA_SIZE = 0xec00000000;
+static std::array, MAX_PRT_APERTURES> PrtApertures{};
-int PS4_SYSV_ABI sceKernelSetPrtAperture(int id, VAddr address, size_t size) {
+s32 PS4_SYSV_ABI sceKernelSetPrtAperture(s32 id, VAddr address, u64 size) {
if (id < 0 || id >= MAX_PRT_APERTURES) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
@@ -630,11 +660,14 @@ 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;
}
-int PS4_SYSV_ABI sceKernelGetPrtAperture(int id, VAddr* address, size_t* size) {
+s32 PS4_SYSV_ABI sceKernelGetPrtAperture(s32 id, VAddr* address, u64* size) {
if (id < 0 || id >= MAX_PRT_APERTURES) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
@@ -678,8 +711,10 @@ 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", "libkernel", 1, "libkernel", 1, 1, posix_mprotect);
+ 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);
diff --git a/src/core/libraries/kernel/memory.h b/src/core/libraries/kernel/memory.h
index 92e158a00..ce4ec64fe 100644
--- a/src/core/libraries/kernel/memory.h
+++ b/src/core/libraries/kernel/memory.h
@@ -52,13 +52,13 @@ constexpr u32 ORBIS_KERNEL_MAXIMUM_NAME_LENGTH = 32;
struct OrbisQueryInfo {
uintptr_t start;
uintptr_t end;
- int memoryType;
+ s32 memoryType;
};
struct OrbisVirtualQueryInfo {
uintptr_t start;
uintptr_t end;
- size_t offset;
+ u64 offset;
s32 protection;
s32 memory_type;
u8 is_flexible : 1;
@@ -73,12 +73,12 @@ static_assert(sizeof(OrbisVirtualQueryInfo) == 72,
struct OrbisKernelBatchMapEntry {
void* start;
- size_t offset;
- size_t length;
+ u64 offset;
+ u64 length;
char protection;
char type;
- short reserved;
- int operation;
+ s16 reserved;
+ s32 operation;
};
enum class OrbisKernelMemoryPoolOpcode : u32 {
@@ -124,59 +124,57 @@ struct OrbisKernelMemoryPoolBatchEntry {
};
u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize();
-int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len,
- u64 alignment, int memoryType, s64* physAddrOut);
-int PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, int prot, int flags,
+s32 PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len,
+ u64 alignment, s32 memoryType, s64* physAddrOut);
+s32 PS4_SYSV_ABI sceKernelMapNamedDirectMemory(void** addr, u64 len, s32 prot, s32 flags,
s64 directMemoryStart, u64 alignment,
const char* name);
-int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags,
+s32 PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, s32 prot, s32 flags,
s64 directMemoryStart, u64 alignment);
-s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType,
+s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(u64 len, u64 alignment, s32 memoryType,
s64* physAddrOut);
-s32 PS4_SYSV_ABI sceKernelReleaseDirectMemory(u64 start, size_t len);
-s32 PS4_SYSV_ABI sceKernelCheckedReleaseDirectMemory(u64 start, size_t len);
-s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchEnd,
- size_t alignment, u64* physAddrOut,
- size_t* sizeOut);
-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);
-int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot);
+s32 PS4_SYSV_ABI sceKernelReleaseDirectMemory(u64 start, u64 len);
+s32 PS4_SYSV_ABI sceKernelCheckedReleaseDirectMemory(u64 start, u64 len);
+s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchEnd, u64 alignment,
+ u64* physAddrOut, u64* sizeOut);
+s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, s32 flags, OrbisVirtualQueryInfo* info,
+ u64 infoSize);
+s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, s32 flags, u64 alignment);
+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);
+s32 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);
-s32 PS4_SYSV_ABI sceKernelAvailableFlexibleMemorySize(size_t* sizeOut);
+s32 PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, s32 flags, OrbisQueryInfo* query_info,
+ u64 infoSize);
+s32 PS4_SYSV_ABI sceKernelAvailableFlexibleMemorySize(u64* sizeOut);
void PS4_SYSV_ABI _sceKernelRtldSetApplicationHeapAPI(void* func[]);
-int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut,
+s32 PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, s32* directMemoryTypeOut,
void** directMemoryStartOut,
void** directMemoryEndOut);
-int PS4_SYSV_ABI sceKernelIsStack(void* addr, void** start, void** end);
+s32 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 sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, s32 numEntries,
+ s32* numEntriesOut);
+s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, s32 numEntries,
+ s32* numEntriesOut, s32 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);
+s32 PS4_SYSV_ABI sceKernelMunmap(void* addr, u64 len);
void RegisterMemory(Core::Loader::SymbolsResolver* sym);
diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp
index 8a37e78d5..e22509a3a 100644
--- a/src/core/libraries/kernel/process.cpp
+++ b/src/core/libraries/kernel/process.cpp
@@ -103,7 +103,7 @@ s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, s32 flags,
auto* linker = Common::Singleton::Instance();
auto* module = linker->FindByAddress(addr);
if (!module) {
- return ORBIS_KERNEL_ERROR_EFAULT;
+ return ORBIS_KERNEL_ERROR_ESRCH;
}
const auto mod_info = module->GetModuleInfoEx();
@@ -118,11 +118,23 @@ s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, s32 flags,
return ORBIS_OK;
}
-int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags,
+s32 PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, s32 flags,
Core::OrbisKernelModuleInfoEx* info) {
+ if (flags >= 3) {
+ std::memset(info, 0, sizeof(Core::OrbisKernelModuleInfoEx));
+ return ORBIS_KERNEL_ERROR_EINVAL;
+ }
+ if (info == nullptr) {
+ return ORBIS_KERNEL_ERROR_EFAULT;
+ }
+
LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags);
auto* linker = Common::Singleton::Instance();
auto* module = linker->FindByAddress(addr);
+ if (!module) {
+ return ORBIS_KERNEL_ERROR_ESRCH;
+ }
+
*info = module->GetModuleInfoEx();
return ORBIS_OK;
}
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 61310bfb5..59b427d22 100644
--- a/src/core/libraries/kernel/threads/pthread.cpp
+++ b/src/core/libraries/kernel/threads/pthread.cpp
@@ -576,8 +576,19 @@ int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize
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;
- return posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp);
+ 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,
@@ -585,11 +596,23 @@ int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize
if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL;
}
- auto* attr_ptr = &thread->attr;
- if (const auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, 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;
}
- return thread->SetAffinity(thread->attr.cpuset);
+
+ 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 scePthreadGetaffinity(PthreadT thread, u64* mask) {
diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp
index 71f6438a6..e098b00a4 100644
--- a/src/core/libraries/kernel/threads/pthread_attr.cpp
+++ b/src/core/libraries/kernel/threads/pthread_attr.cpp
@@ -306,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/libs.cpp b/src/core/libraries/libs.cpp
index 2ab46d3a0..762c1e762 100644
--- a/src/core/libraries/libs.cpp
+++ b/src/core/libraries/libs.cpp
@@ -10,6 +10,7 @@
#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"
@@ -59,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"
@@ -126,6 +128,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* 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/network/net.cpp b/src/core/libraries/network/net.cpp
index 0ef4a84f5..9607f0d78 100644
--- a/src/core/libraries/network/net.cpp
+++ b/src/core/libraries/network/net.cpp
@@ -955,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() {
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/ngs2/ngs2_impl.cpp b/src/core/libraries/ngs2/ngs2_impl.cpp
index 1248f76d7..141ac41ba 100644
--- a/src/core/libraries/ngs2/ngs2_impl.cpp
+++ b/src/core/libraries/ngs2/ngs2_impl.cpp
@@ -100,6 +100,11 @@ s32 SystemSetupCore(StackBuffer* stackBuffer, const OrbisNgs2SystemOption* optio
return ORBIS_NGS2_ERROR_INVALID_SAMPLE_RATE;
}
+ if (outSystem) {
+ // dummy handle
+ outSystem->systemHandle = 1;
+ }
+
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 6de84bd93..c0642f81c 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,27 @@ 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;
+
+ if (contextId.index >= trophy_contexts.size()) {
+ return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
+ }
ContextKey contextkey = trophy_contexts[contextId];
trophy_contexts.erase(contextId);
@@ -206,15 +214,17 @@ s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(OrbisNpTrophyHandle handle) {
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
- if (handle >= trophy_handles.size()) {
+ 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;
}
- if (!trophy_handles.is_allocated({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)});
+ 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..59964fa58 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,82 @@ 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) {
- const auto gyro_poll_rate = engine->GetAccelPollRate();
- if (gyro_poll_rate != 0.0f) {
- GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
- 1.0f / gyro_poll_rate, pData->orientation);
- }
+
+ // Only do this on handle 1 for now
+ if (engine && handle == 1) {
+ 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->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/save_instance.cpp b/src/core/libraries/save_data/save_instance.cpp
index a7ce3d35f..05253eb23 100644
--- a/src/core/libraries/save_data/save_instance.cpp
+++ b/src/core/libraries/save_data/save_instance.cpp
@@ -22,25 +22,25 @@ static Core::FileSys::MntPoints* g_mnt = Common::Singleton default_title = {
- {"ja_JP", "セーブデータ"},
- {"en_US", "Saved Data"},
- {"fr_FR", "Données sauvegardées"},
- {"es_ES", "Datos guardados"},
- {"de_DE", "Gespeicherte Daten"},
- {"it_IT", "Dati salvati"},
- {"nl_NL", "Opgeslagen data"},
- {"pt_PT", "Dados guardados"},
- {"ru_RU", "Сохраненные данные"},
- {"ko_KR", "저장 데이터"},
- {"zh_CN", "保存数据"},
- {"fi_FI", "Tallennetut tiedot"},
- {"sv_SE", "Sparade data"},
- {"da_DK", "Gemte data"},
- {"no_NO", "Lagrede data"},
- {"pl_PL", "Zapisane dane"},
- {"pt_BR", "Dados salvos"},
- {"tr_TR", "Kayıtlı Veriler"},
+static const std::unordered_map default_title = {
+ {0/*"ja_JP"*/, "セーブデータ"},
+ {1/*"en_US"*/, "Saved Data"},
+ {2/*"fr_FR"*/, "Données sauvegardées"},
+ {3/*"es_ES"*/, "Datos guardados"},
+ {4/*"de_DE"*/, "Gespeicherte Daten"},
+ {5/*"it_IT"*/, "Dati salvati"},
+ {6/*"nl_NL"*/, "Opgeslagen data"},
+ {7/*"pt_PT"*/, "Dados guardados"},
+ {8/*"ru_RU"*/, "Сохраненные данные"},
+ {9/*"ko_KR"*/, "저장 데이터"},
+ {10/*"zh_CN"*/, "保存数据"},
+ {12/*"fi_FI"*/, "Tallennetut tiedot"},
+ {13/*"sv_SE"*/, "Sparade data"},
+ {14/*"da_DK"*/, "Gemte data"},
+ {15/*"no_NO"*/, "Lagrede data"},
+ {16/*"pl_PL"*/, "Zapisane dane"},
+ {17/*"pt_BR"*/, "Dados salvos"},
+ {19/*"tr_TR"*/, "Kayıtlı Veriler"},
};
// clang-format on
@@ -71,9 +71,9 @@ fs::path SaveInstance::GetParamSFOPath(const fs::path& dir_path) {
void SaveInstance::SetupDefaultParamSFO(PSF& param_sfo, std::string dir_name,
std::string game_serial) {
- std::string locale = Config::getEmulatorLanguage();
+ int locale = Config::GetLanguage();
if (!default_title.contains(locale)) {
- locale = "en_US";
+ locale = 1; // default to en_US if not found
}
#define P(type, key, ...) param_sfo.Add##type(std::string{key}, __VA_ARGS__)
diff --git a/src/core/libraries/videodec/videodec2.cpp b/src/core/libraries/videodec/videodec2.cpp
index 4f9379151..8c91e2bf1 100644
--- a/src/core/libraries/videodec/videodec2.cpp
+++ b/src/core/libraries/videodec/videodec2.cpp
@@ -140,7 +140,7 @@ s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
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;
}
@@ -167,23 +167,44 @@ s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outp
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()) {
+ if (outputInfo->pictureCount == 0) {
LOG_ERROR(Lib_Vdec2, "No picture info available");
return ORBIS_OK;
}
- if (p1stPictureInfoOut) {
- OrbisVideodec2AvcPictureInfo* picInfo =
- static_cast(p1stPictureInfoOut);
- if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) {
- LOG_ERROR(Lib_Vdec2, "Invalid struct size");
- return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
+ // If the game uses the older Videodec2 structs, we need to accomodate that.
+ if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
+ if (gLegacyPictureInfos.empty()) {
+ LOG_ERROR(Lib_Vdec2, "No picture info available");
+ return ORBIS_OK;
+ }
+ if (p1stPictureInfoOut) {
+ OrbisVideodec2LegacyAvcPictureInfo* picInfo =
+ static_cast(p1stPictureInfoOut);
+ if (picInfo->thisSize != sizeof(OrbisVideodec2LegacyAvcPictureInfo)) {
+ LOG_ERROR(Lib_Vdec2, "Invalid struct size");
+ return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
+ }
+ *picInfo = gLegacyPictureInfos.back();
+ }
+ } else {
+ if (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)) {
+ LOG_ERROR(Lib_Vdec2, "Invalid struct size");
+ return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
+ }
+ *picInfo = gPictureInfos.back();
}
- *picInfo = gPictureInfos.back();
}
if (outputInfo->pictureCount > 1) {
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..725d2335f 100644
--- a/src/core/libraries/videodec/videodec2_avc.h
+++ b/src/core/libraries/videodec/videodec2_avc.h
@@ -55,6 +55,76 @@ 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);
+
+// An older version of the OrbisVideodec2AvcPictureInfo struct
+// Keeping this is needed for compatiblity with older games.
+struct OrbisVideodec2LegacyAvcPictureInfo {
+ u64 thisSize;
+
+ bool isValid;
+
+ u64 ptsData;
+ u64 dtsData;
+ u64 attachedData;
+
+ u8 idrPictureflag;
+
+ u8 profile_idc;
+ u8 level_idc;
+ u32 pic_width_in_mbs_minus1;
+ u32 pic_height_in_map_units_minus1;
+ u8 frame_mbs_only_flag;
+
+ u8 frame_cropping_flag;
+ u32 frameCropLeftOffset;
+ u32 frameCropRightOffset;
+ u32 frameCropTopOffset;
+ u32 frameCropBottomOffset;
+
+ u8 aspect_ratio_info_present_flag;
+ u8 aspect_ratio_idc;
+ u16 sar_width;
+ u16 sar_height;
+
+ u8 video_signal_type_present_flag;
+ u8 video_format;
+ u8 video_full_range_flag;
+ u8 colour_description_present_flag;
+ u8 colour_primaries;
+ u8 transfer_characteristics;
+ u8 matrix_coefficients;
+
+ u8 timing_info_present_flag;
+ u32 num_units_in_tick;
+ u32 time_scale;
+ u8 fixed_frame_rate_flag;
+
+ u8 bitstream_restriction_flag;
+ u8 max_dec_frame_buffering;
+
+ u8 pic_struct_present_flag;
+ u8 pic_struct;
+ u8 field_pic_flag;
+ u8 bottom_field_flag;
+};
+static_assert(sizeof(OrbisVideodec2LegacyAvcPictureInfo) == 0x68);
} // 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..667fb79ac 100644
--- a/src/core/libraries/videodec/videodec2_impl.cpp
+++ b/src/core/libraries/videodec/videodec2_impl.cpp
@@ -12,6 +12,7 @@
namespace Libraries::Vdec2 {
std::vector