Merge remote-tracking branch 'origin/main'

This commit is contained in:
kalaposfos13 2025-01-11 07:53:02 +01:00
commit 81bc56b5d2
215 changed files with 19420 additions and 21553 deletions

View File

@ -0,0 +1,55 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Application Bug Report
description: Problem with the application itself (ie. bad file path handling, UX issue)
title: "[APP BUG]: "
body:
- type: markdown
attributes:
value: |
## Important: Read First
**Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only.
If you have a support request or are unsure about the nature of your issue please contact us on [discord](https://discord.gg/bFJxfftGW6).**
Please make an effort to make sure your issue isn't already reported.
Do not create issues involving software piracy, our rules specifically prohibit this. Otherwise your issue will be closed and you will be banned in this repository.
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched for a similar issue in this repository and did not find one.
required: true
- label: I am using an official build obtained from [releases](https://github.com/shadps4-emu/shadPS4/releases) or updated one of those builds using its in-app updater.
required: true
- type: textarea
id: desc
attributes:
label: Describe the Bug
description: "A clear and concise description of what the bug is"
validations:
required: true
- type: textarea
id: repro
attributes:
label: Reproduction Steps
description: "Detailed steps to reproduce the behavior"
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: "A clear and concise description of what you expected to happen"
validations:
required: false
- type: input
id: os
attributes:
label: Specify OS Version
placeholder: "Example: Windows 11, Arch Linux, MacOS 15"
validations:
required: true

10
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
blank_issues_enabled: false
contact_links:
- name: Discord
url: https://discord.gg/bFJxfftGW6
about: Get direct support and hang out with us
- name: Wiki
url: https://github.com/shadps4-emu/shadPS4/wiki
about: Information, guides, etc.

View File

@ -0,0 +1,54 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Feature Request
description: Suggest a new feature or improve an existing one
title: "[Feature Request]: "
body:
- type: markdown
attributes:
value: |
## Important: Read First
Please make an effort to make sure your issue isn't already reported.
Do not create issues involving software piracy, our rules specifically prohibit this. Otherwise your issue will be closed and you will be banned in this repository.
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched for a similar issue in this repository and did not find one.
required: true
- type: textarea
id: desc
attributes:
label: Description
description: |
A concise description of the feature you want
Include step by step examples of how the feature should work under various circumstances
validations:
required: true
- type: textarea
id: reason
attributes:
label: Reason
description: |
Give a reason why you want this feature
- How will it make things easier for you?
- How does this feature help your enjoyment of the emulator?
- What does it provide that isn't being provided currently?
validations:
required: true
- type: textarea
id: examples
attributes:
label: Examples
description: |
Provide examples of the feature as implemented by other software
Include screenshots or video if you like to help demonstrate how you'd like this feature to work
validations:
required: false

View File

@ -0,0 +1,95 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
name: Game Emulation Bug Report
description: Problem in a game (ie. graphical artifacts, crashes, etc.)
title: "[GAME BUG]: "
body:
- type: markdown
attributes:
value: |
## Important: Read First
**Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only.
If you have a support request or are unsure about the nature of your issue please contact us on [discord](https://discord.gg/bFJxfftGW6).**
This repository does not provide support for modded games. You should perform and test a clean game installation before submitting an issue.
This repository does not provide support for game patches. If you are having issues with patches please refer to [Cheats and Patches Repository](https://github.com/shadps4-emu/ps4_cheats).
Before submitting an issue please check [Game Compatibility Repository](https://github.com/shadps4-emu/shadps4-game-compatibility) for the information about the status of the game.
Please make an effort to make sure your issue isn't already reported.
Do not create issues involving software piracy, our rules specifically prohibit this. Otherwise your issue will be closed and you will be banned in this repository.
- type: checkboxes
id: checklist
attributes:
label: Checklist (we expect you to perform these steps before opening the issue)
options:
- label: I have searched for a similar issue in this repository and did not find one.
required: true
- label: I am using an official build obtained from [releases](https://github.com/shadps4-emu/shadPS4/releases) or updated one of those builds using its in-app updater.
required: true
- label: I have re-dumped the game and performed a clean install without mods and the issue is still present.
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.
required: true
- type: textarea
id: desc
attributes:
label: Describe the Bug
description: "A clear and concise description of what the bug is"
validations:
required: true
- type: textarea
id: repro
attributes:
label: Reproduction Steps
description: "Detailed steps to reproduce the behavior"
validations:
required: true
- type: input
id: os
attributes:
label: Specify OS Version
placeholder: "Example: Windows 11, Arch Linux, MacOS 15"
validations:
required: true
- type: input
id: cpu
attributes:
label: CPU
placeholder: "Example: Intel Core i7-8700"
validations:
required: true
- type: input
id: gpu
attributes:
label: GPU
placeholder: "Example: nVidia GTX 1650"
validations:
required: true
- type: input
id: ram
attributes:
label: Amount of RAM in GB
placeholder: "Example: 16 GB"
validations:
required: true
- type: input
id: vram
attributes:
label: Amount of VRAM in GB
placeholder: "Example: 8 GB"
validations:
required: true
- type: textarea
id: logs
attributes:
label: "Log File"
description: Drag and drop the log file here. It can be found by right clicking on a game name -> Open Folder... -> Open Log Folder. Make sure that the log type is set to `sync`.
validations:
required: true

View File

@ -14,14 +14,14 @@ env:
jobs: jobs:
reuse: reuse:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
continue-on-error: true continue-on-error: true
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: fsfe/reuse-action@v5 - uses: fsfe/reuse-action@v5
clang-format: clang-format:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
continue-on-error: true continue-on-error: true
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -39,7 +39,7 @@ jobs:
run: ./.ci/clang-format.sh run: ./.ci/clang-format.sh
get-info: get-info:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
outputs: outputs:
date: ${{ steps.vars.outputs.date }} date: ${{ steps.vars.outputs.date }}
shorthash: ${{ steps.vars.outputs.shorthash }} shorthash: ${{ steps.vars.outputs.shorthash }}
@ -57,7 +57,7 @@ jobs:
echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
windows-sdl: windows-sdl:
runs-on: windows-latest runs-on: windows-2025
needs: get-info needs: get-info
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -101,7 +101,7 @@ jobs:
path: ${{github.workspace}}/build/shadPS4.exe path: ${{github.workspace}}/build/shadPS4.exe
windows-qt: windows-qt:
runs-on: windows-latest runs-on: windows-2025
needs: get-info needs: get-info
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -376,6 +376,78 @@ jobs:
name: shadps4-linux-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }} name: shadps4-linux-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: Shadps4-qt.AppImage path: Shadps4-qt.AppImage
linux-sdl-gcc:
runs-on: ubuntu-24.04
needs: get-info
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-configuration
with:
path: |
${{github.workspace}}/build
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
restore-keys: |
${{ env.cache-name }}-
- name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.14
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
linux-qt-gcc:
runs-on: ubuntu-24.04
needs: get-info
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-configuration
with:
path: |
${{github.workspace}}/build
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
restore-keys: |
${{ env.cache-name }}-
- name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.14
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-build
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
pre-release: pre-release:
if: github.ref == 'refs/heads/main' && github.repository == 'shadps4-emu/shadPS4' && github.event_name == 'push' if: github.ref == 'refs/heads/main' && github.repository == 'shadps4-emu/shadPS4' && github.event_name == 'push'
needs: [get-info, windows-sdl, windows-qt, macos-sdl, macos-qt, linux-sdl, linux-qt] needs: [get-info, windows-sdl, windows-qt, macos-sdl, macos-qt, linux-sdl, linux-qt]

View File

@ -106,7 +106,7 @@ git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH) git_branch_name(GIT_BRANCH)
string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S") string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp" @ONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY)
find_package(Boost 1.84.0 CONFIG) find_package(Boost 1.84.0 CONFIG)
find_package(FFmpeg 5.1.2 MODULE) find_package(FFmpeg 5.1.2 MODULE)
@ -188,6 +188,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp
src/core/libraries/ajm/ajm_context.cpp src/core/libraries/ajm/ajm_context.cpp
src/core/libraries/ajm/ajm_context.h src/core/libraries/ajm/ajm_context.h
src/core/libraries/ajm/ajm_error.h src/core/libraries/ajm/ajm_error.h
src/core/libraries/ajm/ajm_instance_statistics.cpp
src/core/libraries/ajm/ajm_instance_statistics.h
src/core/libraries/ajm/ajm_instance.cpp src/core/libraries/ajm/ajm_instance.cpp
src/core/libraries/ajm/ajm_instance.h src/core/libraries/ajm/ajm_instance.h
src/core/libraries/ajm/ajm_mp3.cpp src/core/libraries/ajm/ajm_mp3.cpp
@ -198,15 +200,16 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
src/core/libraries/audio/audioin.h src/core/libraries/audio/audioin.h
src/core/libraries/audio/audioout.cpp src/core/libraries/audio/audioout.cpp
src/core/libraries/audio/audioout.h src/core/libraries/audio/audioout.h
src/core/libraries/audio/sdl_audio.cpp src/core/libraries/audio/audioout_backend.h
src/core/libraries/audio/sdl_audio.h
src/core/libraries/audio/audioout_error.h src/core/libraries/audio/audioout_error.h
src/core/libraries/audio/sdl_audio.cpp
src/core/libraries/ngs2/ngs2.cpp src/core/libraries/ngs2/ngs2.cpp
src/core/libraries/ngs2/ngs2.h src/core/libraries/ngs2/ngs2.h
) )
set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp
src/core/libraries/gnmdriver/gnmdriver.h src/core/libraries/gnmdriver/gnmdriver.h
src/core/libraries/gnmdriver/gnmdriver_init.h
src/core/libraries/gnmdriver/gnm_error.h src/core/libraries/gnmdriver/gnm_error.h
) )
@ -333,6 +336,8 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/share_play/shareplay.h src/core/libraries/share_play/shareplay.h
src/core/libraries/razor_cpu/razor_cpu.cpp src/core/libraries/razor_cpu/razor_cpu.cpp
src/core/libraries/razor_cpu/razor_cpu.h src/core/libraries/razor_cpu/razor_cpu.h
src/core/libraries/mouse/mouse.cpp
src/core/libraries/mouse/mouse.h
) )
set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
@ -423,6 +428,8 @@ set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
src/core/libraries/screenshot/screenshot.h src/core/libraries/screenshot/screenshot.h
src/core/libraries/move/move.cpp
src/core/libraries/move/move.h
) )
set(DEV_TOOLS src/core/devtools/layer.cpp set(DEV_TOOLS src/core/devtools/layer.cpp
@ -517,7 +524,7 @@ set(COMMON src/common/logging/backend.cpp
src/common/number_utils.cpp src/common/number_utils.cpp
src/common/memory_patcher.h src/common/memory_patcher.h
src/common/memory_patcher.cpp src/common/memory_patcher.cpp
src/common/scm_rev.cpp ${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp
src/common/scm_rev.h src/common/scm_rev.h
) )
@ -694,6 +701,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/ir/post_order.h src/shader_recompiler/ir/post_order.h
src/shader_recompiler/ir/program.cpp src/shader_recompiler/ir/program.cpp
src/shader_recompiler/ir/program.h src/shader_recompiler/ir/program.h
src/shader_recompiler/ir/reinterpret.h
src/shader_recompiler/ir/reg.h src/shader_recompiler/ir/reg.h
src/shader_recompiler/ir/type.cpp src/shader_recompiler/ir/type.cpp
src/shader_recompiler/ir/type.h src/shader_recompiler/ir/type.h
@ -907,11 +915,19 @@ endif()
if (APPLE) if (APPLE)
if (ENABLE_QT_GUI) if (ENABLE_QT_GUI)
# Include MoltenVK in the app bundle, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers. # Include MoltenVK in the app bundle, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers.
target_sources(shadps4 PRIVATE externals/MoltenVK/MoltenVK_icd.json) set(MVK_ICD ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK_icd.json)
set_source_files_properties(externals/MoltenVK/MoltenVK_icd.json target_sources(shadps4 PRIVATE ${MVK_ICD})
PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d) set_source_files_properties(${MVK_ICD} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d)
add_custom_command(TARGET shadps4 POST_BUILD
COMMAND cmake -E copy $<TARGET_LINKER_FILE:MoltenVK> $<TARGET_BUNDLE_DIR:shadps4>/Contents/Frameworks/libMoltenVK.dylib) set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib)
set(MVK_DYLIB_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/Frameworks/libMoltenVK.dylib)
add_custom_command(
OUTPUT ${MVK_DYLIB_DST}
DEPENDS ${MVK_DYLIB_SRC}
COMMAND cmake -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST})
add_custom_target(CopyMoltenVK DEPENDS ${MVK_DYLIB_DST})
add_dependencies(CopyMoltenVK MoltenVK)
add_dependencies(shadps4 CopyMoltenVK)
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../Frameworks") set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../Frameworks")
else() else()
# For non-bundled SDL build, just do a normal library link. # For non-bundled SDL build, just do a normal library link.
@ -1032,7 +1048,6 @@ install(TARGETS shadps4 BUNDLE DESTINATION .)
if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux") if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
install(FILES "dist/net.shadps4.shadPS4.desktop" DESTINATION "share/applications") install(FILES "dist/net.shadps4.shadPS4.desktop" DESTINATION "share/applications")
install(FILES "dist/net.shadps4.shadPS4.releases.xml" DESTINATION "share/metainfo/releases")
install(FILES "dist/net.shadps4.shadPS4.metainfo.xml" DESTINATION "share/metainfo") install(FILES "dist/net.shadps4.shadPS4.metainfo.xml" DESTINATION "share/metainfo")
install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps" RENAME "net.shadps4.shadPS4.png") install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps" RENAME "net.shadps4.shadPS4.png")
install(FILES "src/images/net.shadps4.shadPS4.svg" DESTINATION "share/icons/hicolor/scalable/apps") install(FILES "src/images/net.shadps4.shadPS4.svg" DESTINATION "share/icons/hicolor/scalable/apps")

View File

@ -123,6 +123,11 @@ Keyboard and mouse inputs can be customized in the settings menu by clicking the
- [**skmp**](https://github.com/skmp) - [**skmp**](https://github.com/skmp)
- [**wheremyfoodat**](https://github.com/wheremyfoodat) - [**wheremyfoodat**](https://github.com/wheremyfoodat)
- [**raziel1000**](https://github.com/raziel1000) - [**raziel1000**](https://github.com/raziel1000)
- [**viniciuslrangel**](https://github.com/viniciuslrangel)
- [**roamic**](https://github.com/vladmikhalin)
- [**poly**](https://github.com/polybiusproxy)
- [**squidbus**](https://github.com/squidbus)
- [**frodo**](https://github.com/baggins183)
Logo is done by [**Xphalnos**](https://github.com/Xphalnos) Logo is done by [**Xphalnos**](https://github.com/Xphalnos)

View File

@ -11,7 +11,6 @@ path = [
"dist/net.shadps4.shadPS4.desktop", "dist/net.shadps4.shadPS4.desktop",
"dist/net.shadps4.shadPS4_metadata.pot", "dist/net.shadps4.shadPS4_metadata.pot",
"dist/net.shadps4.shadPS4.metainfo.xml", "dist/net.shadps4.shadPS4.metainfo.xml",
"dist/net.shadps4.shadPS4.releases.xml",
"documents/changelog.md", "documents/changelog.md",
"documents/Quickstart/2.png", "documents/Quickstart/2.png",
"documents/Screenshots/*", "documents/Screenshots/*",

View File

@ -36,9 +36,30 @@
<categories> <categories>
<category translate="no">Game</category> <category translate="no">Game</category>
</categories> </categories>
<releases type="external" url="https://cdn.jsdelivr.net/gh/fpiesche/flatpak-builds/apps/net.shadps4.shadPS4/net.shadps4.shadPS4.releases.xml"> <releases>
<release version="v.0.4.0" date="2024-11-03"> <release version="0.5.0" date="2024-12-25">
<description></description> <url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.5.0</url>
</release>
<release version="0.4.0" date="2024-10-31">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.4.0</url>
</release>
<release version="0.3.0" date="2024-09-23">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.3.0</url>
</release>
<release version="0.2.0" date="2024-08-15">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.2.0</url>
</release>
<release version="0.1.0" date="2024-07-01">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/0.1.0</url>
</release>
<release version="0.0.3" date="2024-03-23">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.3</url>
</release>
<release version="0.0.2" date="2023-10-21">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.2</url>
</release>
<release version="0.0.1" date="2024-09-29">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.1</url>
</release> </release>
</releases> </releases>
<content_rating type="oars-1.1"/> <content_rating type="oars-1.1"/>

View File

@ -1,23 +0,0 @@
<releases>
<release version="0.4.0" date="2024-10-31">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.4.0</url>
</release>
<release version="0.3.0" date="2024-09-23">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.3.0</url>
</release>
<release version="0.2.0" date="2024-08-15">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.2.0</url>
</release>
<release version="0.1.0" date="2024-07-01">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/0.1.0</url>
</release>
<release version="0.0.3" date="2024-03-23">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.3</url>
</release>
<release version="0.0.2" date="2023-10-21">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.2</url>
</release>
<release version="0.0.1" date="2024-09-29">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.1</url>
</release>
</releases>

View File

@ -64,7 +64,7 @@ Go through the Git for Windows installation as normal
Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\` Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\`
To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal: To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal:
`C:\Qt\6.7.3\msvc2022_64\bin\windeployqt.exe "C:\path\to\shadps4.exe"` `C:\Qt\6.7.3\msvc2022_64\bin\windeployqt6.exe "C:\path\to\shadps4.exe"`
(Change Qt path if you've installed it to non-default path) (Change Qt path if you've installed it to non-default path)
## Option 2: MSYS2/MinGW ## Option 2: MSYS2/MinGW

View File

@ -213,9 +213,7 @@ endif()
# Discord RPC # Discord RPC
if (ENABLE_DISCORD_RPC) if (ENABLE_DISCORD_RPC)
set(BUILD_EXAMPLES OFF)
add_subdirectory(discord-rpc) add_subdirectory(discord-rpc)
target_include_directories(discord-rpc INTERFACE discord-rpc/include)
endif() endif()
# GCN Headers # GCN Headers

@ -1 +1 @@
Subproject commit 5ad3ee5d2f84342950c3fe93dec97719574d1932 Subproject commit 9f0b616d9e2c39464d2a859b79dbc655c4a30e7e

@ -1 +1 @@
Subproject commit 4ec218155d73bcb8022f8f7ca72305d801f84beb Subproject commit 51b09d426a4a1bcfa6ee6d4894e57d669f4a2e65

2
externals/sirit vendored

@ -1 +1 @@
Subproject commit 1e74f4ef8d2a0e3221a4de51977663f342b53c35 Subproject commit 26ad5a9d0fe13260b0d7d6c64419d01a196b2e32

View File

@ -33,6 +33,7 @@ namespace Config {
static bool isNeo = false; static bool isNeo = false;
static bool isFullscreen = false; static bool isFullscreen = false;
static std::string fullscreenMode = "borderless";
static bool playBGM = false; static bool playBGM = false;
static bool isTrophyPopupDisabled = false; static bool isTrophyPopupDisabled = false;
static int BGMvolume = 50; static int BGMvolume = 50;
@ -47,6 +48,7 @@ static std::string updateChannel;
static std::string backButtonBehavior = "left"; static std::string backButtonBehavior = "left";
static bool useSpecialPad = false; static bool useSpecialPad = false;
static int specialPadClass = 1; static int specialPadClass = 1;
static bool isMotionControlsEnabled = true;
static bool isDebugDump = false; static bool isDebugDump = false;
static bool isShaderDebug = false; static bool isShaderDebug = false;
static bool isShowSplash = false; static bool isShowSplash = false;
@ -67,6 +69,7 @@ static int cursorHideTimeout = 5; // 5 seconds (default)
static bool separateupdatefolder = false; static bool separateupdatefolder = false;
static bool compatibilityData = false; static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false; static bool checkCompatibilityOnStartup = false;
static std::string trophyKey;
// Gui // Gui
std::vector<std::filesystem::path> settings_install_dirs = {}; std::vector<std::filesystem::path> settings_install_dirs = {};
@ -91,14 +94,26 @@ std::string emulator_language = "en";
// Language // Language
u32 m_language = 1; // english u32 m_language = 1; // english
bool isNeoMode() { std::string getTrophyKey() {
return trophyKey;
}
void setTrophyKey(std::string key) {
trophyKey = key;
}
bool isNeoModeConsole() {
return isNeo; return isNeo;
} }
bool isFullscreenMode() { bool getIsFullscreen() {
return isFullscreen; return isFullscreen;
} }
std::string getFullscreenMode() {
return fullscreenMode;
}
bool getisTrophyPopupDisabled() { bool getisTrophyPopupDisabled() {
return isTrophyPopupDisabled; return isTrophyPopupDisabled;
} }
@ -163,6 +178,10 @@ int getSpecialPadClass() {
return specialPadClass; return specialPadClass;
} }
bool getIsMotionControlsEnabled() {
return isMotionControlsEnabled;
}
bool debugDump() { bool debugDump() {
return isDebugDump; return isDebugDump;
} }
@ -295,10 +314,14 @@ void setVblankDiv(u32 value) {
vblankDivider = value; vblankDivider = value;
} }
void setFullscreenMode(bool enable) { void setIsFullscreen(bool enable) {
isFullscreen = enable; isFullscreen = enable;
} }
void setFullscreenMode(std::string mode) {
fullscreenMode = mode;
}
void setisTrophyPopupDisabled(bool disable) { void setisTrophyPopupDisabled(bool disable) {
isTrophyPopupDisabled = disable; isTrophyPopupDisabled = disable;
} }
@ -359,6 +382,10 @@ void setSpecialPadClass(int type) {
specialPadClass = type; specialPadClass = type;
} }
void setIsMotionControlsEnabled(bool use) {
isMotionControlsEnabled = use;
}
void setSeparateUpdateEnabled(bool use) { void setSeparateUpdateEnabled(bool use) {
separateupdatefolder = use; separateupdatefolder = use;
} }
@ -557,6 +584,7 @@ void load(const std::filesystem::path& path) {
isNeo = toml::find_or<bool>(general, "isPS4Pro", false); isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
isFullscreen = toml::find_or<bool>(general, "Fullscreen", false); isFullscreen = toml::find_or<bool>(general, "Fullscreen", false);
fullscreenMode = toml::find_or<std::string>(general, "FullscreenMode", "borderless");
playBGM = toml::find_or<bool>(general, "playBGM", false); playBGM = toml::find_or<bool>(general, "playBGM", false);
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false); isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50); BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
@ -585,6 +613,7 @@ void load(const std::filesystem::path& path) {
backButtonBehavior = toml::find_or<std::string>(input, "backButtonBehavior", "left"); backButtonBehavior = toml::find_or<std::string>(input, "backButtonBehavior", "left");
useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", false); useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", false);
specialPadClass = toml::find_or<int>(input, "specialPadClass", 1); specialPadClass = toml::find_or<int>(input, "specialPadClass", 1);
isMotionControlsEnabled = toml::find_or<bool>(input, "isMotionControlsEnabled", true);
} }
if (data.contains("GPU")) { if (data.contains("GPU")) {
@ -652,6 +681,11 @@ void load(const std::filesystem::path& path) {
m_language = toml::find_or<int>(settings, "consoleLanguage", 1); m_language = toml::find_or<int>(settings, "consoleLanguage", 1);
} }
if (data.contains("Keys")) {
const toml::value& keys = data.at("Keys");
trophyKey = toml::find_or<std::string>(keys, "TrophyKey", "");
}
} }
void save(const std::filesystem::path& path) { void save(const std::filesystem::path& path) {
@ -677,6 +711,7 @@ void save(const std::filesystem::path& path) {
data["General"]["isPS4Pro"] = isNeo; data["General"]["isPS4Pro"] = isNeo;
data["General"]["Fullscreen"] = isFullscreen; data["General"]["Fullscreen"] = isFullscreen;
data["General"]["FullscreenMode"] = fullscreenMode;
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
data["General"]["playBGM"] = playBGM; data["General"]["playBGM"] = playBGM;
data["General"]["BGMvolume"] = BGMvolume; data["General"]["BGMvolume"] = BGMvolume;
@ -695,6 +730,7 @@ void save(const std::filesystem::path& path) {
data["Input"]["backButtonBehavior"] = backButtonBehavior; data["Input"]["backButtonBehavior"] = backButtonBehavior;
data["Input"]["useSpecialPad"] = useSpecialPad; data["Input"]["useSpecialPad"] = useSpecialPad;
data["Input"]["specialPadClass"] = specialPadClass; data["Input"]["specialPadClass"] = specialPadClass;
data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled;
data["GPU"]["screenWidth"] = screenWidth; data["GPU"]["screenWidth"] = screenWidth;
data["GPU"]["screenHeight"] = screenHeight; data["GPU"]["screenHeight"] = screenHeight;
data["GPU"]["nullGpu"] = isNullGpu; data["GPU"]["nullGpu"] = isNullGpu;
@ -712,6 +748,8 @@ void save(const std::filesystem::path& path) {
data["Debug"]["DebugDump"] = isDebugDump; data["Debug"]["DebugDump"] = isDebugDump;
data["Debug"]["CollectShader"] = isShaderDebug; data["Debug"]["CollectShader"] = isShaderDebug;
data["Keys"]["TrophyKey"] = trophyKey;
std::vector<std::string> install_dirs; std::vector<std::string> install_dirs;
for (const auto& dirString : settings_install_dirs) { for (const auto& dirString : settings_install_dirs) {
install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data}); install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data});

View File

@ -15,8 +15,11 @@ void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path); void save(const std::filesystem::path& path);
void saveMainWindow(const std::filesystem::path& path); void saveMainWindow(const std::filesystem::path& path);
bool isNeoMode(); std::string getTrophyKey();
bool isFullscreenMode(); void setTrophyKey(std::string key);
bool getIsFullscreen();
std::string getFullscreenMode();
bool isNeoModeConsole();
bool getPlayBGM(); bool getPlayBGM();
int getBGMvolume(); int getBGMvolume();
bool getisTrophyPopupDisabled(); bool getisTrophyPopupDisabled();
@ -35,6 +38,7 @@ int getCursorHideTimeout();
std::string getBackButtonBehavior(); std::string getBackButtonBehavior();
bool getUseSpecialPad(); bool getUseSpecialPad();
int getSpecialPadClass(); int getSpecialPadClass();
bool getIsMotionControlsEnabled();
u32 getScreenWidth(); u32 getScreenWidth();
u32 getScreenHeight(); u32 getScreenHeight();
@ -62,7 +66,8 @@ void setVblankDiv(u32 value);
void setGpuId(s32 selectedGpuId); void setGpuId(s32 selectedGpuId);
void setScreenWidth(u32 width); void setScreenWidth(u32 width);
void setScreenHeight(u32 height); void setScreenHeight(u32 height);
void setFullscreenMode(bool enable); void setIsFullscreen(bool enable);
void setFullscreenMode(std::string mode);
void setisTrophyPopupDisabled(bool disable); void setisTrophyPopupDisabled(bool disable);
void setPlayBGM(bool enable); void setPlayBGM(bool enable);
void setBGMvolume(int volume); void setBGMvolume(int volume);
@ -81,6 +86,7 @@ void setCursorHideTimeout(int newcursorHideTimeout);
void setBackButtonBehavior(const std::string& type); void setBackButtonBehavior(const std::string& type);
void setUseSpecialPad(bool use); void setUseSpecialPad(bool use);
void setSpecialPadClass(int type); void setSpecialPadClass(int type);
void setIsMotionControlsEnabled(bool use);
void setLogType(const std::string& type); void setLogType(const std::string& type);
void setLogFilter(const std::string& type); void setLogFilter(const std::string& type);
@ -139,4 +145,4 @@ std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id = "")
// settings // settings
u32 GetLanguage(); u32 GetLanguage();
}; // namespace Config }; // namespace Config

View File

@ -7,6 +7,7 @@
#include <string_view> #include <string_view>
#include "assert.h" #include "assert.h"
#include "bit_field.h"
#include "singleton.h" #include "singleton.h"
#include "types.h" #include "types.h"
@ -16,6 +17,46 @@ class Emulator;
namespace Common { namespace Common {
union PSFAttributes {
/// Supports initial user's logout
BitField<0, 1, u32> support_initial_user_logout;
/// Enter button for the common dialog is cross.
BitField<1, 1, u32> enter_button_cross;
/// Warning dialog for PS Move is displayed in the options menu.
BitField<2, 1, u32> ps_move_warning;
/// Supports stereoscopic 3D.
BitField<3, 1, u32> support_stereoscopic_3d;
/// Suspends when PS button is pressed.
BitField<4, 1, u32> ps_button_suspend;
/// Enter button for the common dialog is assigned by the system software.
BitField<5, 1, u32> enter_button_system;
/// Overrides share menu behavior.
BitField<6, 1, u32> override_share_menu;
/// Suspends when PS button is pressed and special output resolution is set.
BitField<8, 1, u32> special_res_ps_button_suspend;
/// Enable HDCP.
BitField<9, 1, u32> enable_hdcp;
/// Disable HDCP for non-game.
BitField<10, 1, u32> disable_hdcp_non_game;
/// Supports PS VR.
BitField<14, 1, u32> support_ps_vr;
/// CPU mode (6 CPU)
BitField<15, 1, u32> six_cpu_mode;
/// CPU mode (7 CPU)
BitField<16, 1, u32> seven_cpu_mode;
/// Supports PS4 Pro (Neo) mode.
BitField<23, 1, u32> support_neo_mode;
/// Requires PS VR.
BitField<26, 1, u32> require_ps_vr;
/// Supports HDR.
BitField<29, 1, u32> support_hdr;
/// Display location.
BitField<31, 1, u32> display_location;
u32 raw{};
};
static_assert(sizeof(PSFAttributes) == 4);
class ElfInfo { class ElfInfo {
friend class Core::Emulator; friend class Core::Emulator;
@ -26,6 +67,7 @@ class ElfInfo {
std::string app_ver{}; std::string app_ver{};
u32 firmware_ver = 0; u32 firmware_ver = 0;
u32 raw_firmware_ver = 0; u32 raw_firmware_ver = 0;
PSFAttributes psf_attributes{};
public: public:
static constexpr u32 FW_15 = 0x1500000; static constexpr u32 FW_15 = 0x1500000;
@ -34,6 +76,7 @@ public:
static constexpr u32 FW_20 = 0x2000000; static constexpr u32 FW_20 = 0x2000000;
static constexpr u32 FW_25 = 0x2500000; static constexpr u32 FW_25 = 0x2500000;
static constexpr u32 FW_30 = 0x3000000; static constexpr u32 FW_30 = 0x3000000;
static constexpr u32 FW_35 = 0x3500000;
static constexpr u32 FW_40 = 0x4000000; static constexpr u32 FW_40 = 0x4000000;
static constexpr u32 FW_45 = 0x4500000; static constexpr u32 FW_45 = 0x4500000;
static constexpr u32 FW_50 = 0x5000000; static constexpr u32 FW_50 = 0x5000000;
@ -67,6 +110,11 @@ public:
ASSERT(initialized); ASSERT(initialized);
return raw_firmware_ver; return raw_firmware_ver;
} }
[[nodiscard]] const PSFAttributes& GetPSFAttributes() const {
ASSERT(initialized);
return psf_attributes;
}
}; };
} // namespace Common } // namespace Common

View File

@ -97,6 +97,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Http) \ SUB(Lib, Http) \
SUB(Lib, Ssl) \ SUB(Lib, Ssl) \
SUB(Lib, SysModule) \ SUB(Lib, SysModule) \
SUB(Lib, Move) \
SUB(Lib, NpManager) \ SUB(Lib, NpManager) \
SUB(Lib, NpScore) \ SUB(Lib, NpScore) \
SUB(Lib, NpTrophy) \ SUB(Lib, NpTrophy) \
@ -125,6 +126,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Vdec2) \ SUB(Lib, Vdec2) \
SUB(Lib, Videodec) \ SUB(Lib, Videodec) \
SUB(Lib, RazorCpu) \ SUB(Lib, RazorCpu) \
SUB(Lib, Mouse) \
CLS(Frontend) \ CLS(Frontend) \
CLS(Render) \ CLS(Render) \
SUB(Render, Vulkan) \ SUB(Render, Vulkan) \

View File

@ -57,8 +57,9 @@ enum class Class : u8 {
Lib_MsgDlg, ///< The LibSceMsgDialog implementation. Lib_MsgDlg, ///< The LibSceMsgDialog implementation.
Lib_AudioOut, ///< The LibSceAudioOut implementation. Lib_AudioOut, ///< The LibSceAudioOut implementation.
Lib_AudioIn, ///< The LibSceAudioIn implementation. Lib_AudioIn, ///< The LibSceAudioIn implementation.
Lib_Move, ///< The LibSceMove implementation.
Lib_Net, ///< The LibSceNet implementation. Lib_Net, ///< The LibSceNet implementation.
Lib_NetCtl, ///< The LibSecNetCtl implementation. Lib_NetCtl, ///< The LibSceNetCtl implementation.
Lib_SaveData, ///< The LibSceSaveData implementation. Lib_SaveData, ///< The LibSceSaveData implementation.
Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation. Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation.
Lib_Ssl, ///< The LibSceSsl implementation. Lib_Ssl, ///< The LibSceSsl implementation.
@ -92,6 +93,7 @@ enum class Class : u8 {
Lib_Vdec2, ///< The LibSceVideodec2 implementation. Lib_Vdec2, ///< The LibSceVideodec2 implementation.
Lib_Videodec, ///< The LibSceVideodec implementation. Lib_Videodec, ///< The LibSceVideodec implementation.
Lib_RazorCpu, ///< The LibRazorCpu implementation. Lib_RazorCpu, ///< The LibRazorCpu implementation.
Lib_Mouse, ///< The LibSceMouse implementation
Frontend, ///< Emulator UI Frontend, ///< Emulator UI
Render, ///< Video Core Render, ///< Video Core
Render_Vulkan, ///< Vulkan backend Render_Vulkan, ///< Vulkan backend

View File

@ -4,11 +4,6 @@
#include "common/native_clock.h" #include "common/native_clock.h"
#include "common/rdtsc.h" #include "common/rdtsc.h"
#include "common/uint128.h" #include "common/uint128.h"
#ifdef _WIN64
#include <pthread_time.h>
#else
#include <time.h>
#endif
namespace Common { namespace Common {
@ -34,10 +29,4 @@ u64 NativeClock::GetUptime() const {
return FencedRDTSC(); return FencedRDTSC();
} }
u64 NativeClock::GetProcessTimeUS() const {
timespec ret;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ret);
return ret.tv_nsec / 1000 + ret.tv_sec * 1000000;
}
} // namespace Common } // namespace Common

View File

@ -20,7 +20,6 @@ public:
u64 GetTimeUS(u64 base_ptc = 0) const; u64 GetTimeUS(u64 base_ptc = 0) const;
u64 GetTimeMS(u64 base_ptc = 0) const; u64 GetTimeMS(u64 base_ptc = 0) const;
u64 GetUptime() const; u64 GetUptime() const;
u64 GetProcessTimeUS() const;
private: private:
u64 rdtsc_frequency; u64 rdtsc_frequency;

View File

@ -26,7 +26,7 @@ asm(".zerofill GUEST_SYSTEM,GUEST_SYSTEM,__guest_system,0xFBFC00000");
namespace Core { namespace Core {
static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO; static constexpr size_t BackingSize = SCE_KERNEL_TOTAL_MEM_PRO;
#ifdef _WIN32 #ifdef _WIN32

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <array> #include <array>
#include "crypto.h" #include "crypto.h"
CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() { CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() {
@ -137,20 +138,20 @@ void Crypto::aesCbcCfb128DecryptEntry(std::span<const CryptoPP::byte, 32> ivkey,
} }
} }
void Crypto::decryptEFSM(std::span<CryptoPP::byte, 16> NPcommID, void Crypto::decryptEFSM(std::span<CryptoPP::byte, 16> trophyKey,
std::span<CryptoPP::byte, 16> NPcommID,
std::span<CryptoPP::byte, 16> efsmIv, std::span<CryptoPP::byte> ciphertext, std::span<CryptoPP::byte, 16> efsmIv, std::span<CryptoPP::byte> ciphertext,
std::span<CryptoPP::byte> decrypted) { std::span<CryptoPP::byte> decrypted) {
std::vector<CryptoPP::byte> TrophyKey = {0x21, 0xF4, 0x1A, 0x6B, 0xAD, 0x8A, 0x1D, 0x3E,
0xCA, 0x7A, 0xD5, 0x86, 0xC1, 0x01, 0xB7, 0xA9};
std::vector<CryptoPP::byte> TrophyIV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// step 1: Encrypt NPcommID // step 1: Encrypt NPcommID
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encrypt; CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encrypt;
encrypt.SetKeyWithIV(TrophyKey.data(), TrophyKey.size(), TrophyIV.data());
std::vector<CryptoPP::byte> trophyIv(16, 0);
std::vector<CryptoPP::byte> trpKey(16); std::vector<CryptoPP::byte> trpKey(16);
encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data());
encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16); encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16);
// step 2: decrypt efsm. // step 2: decrypt efsm.
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decrypt; CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decrypt;
decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data()); decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data());

View File

@ -32,7 +32,8 @@ public:
void aesCbcCfb128DecryptEntry(std::span<const CryptoPP::byte, 32> ivkey, void aesCbcCfb128DecryptEntry(std::span<const CryptoPP::byte, 32> ivkey,
std::span<CryptoPP::byte> ciphertext, std::span<CryptoPP::byte> ciphertext,
std::span<CryptoPP::byte> decrypted); std::span<CryptoPP::byte> decrypted);
void decryptEFSM(std::span<CryptoPP::byte, 16>, std::span<CryptoPP::byte, 16> efsmIv, void decryptEFSM(std::span<CryptoPP::byte, 16> trophyKey,
std::span<CryptoPP::byte, 16> NPcommID, std::span<CryptoPP::byte, 16> efsmIv,
std::span<CryptoPP::byte> ciphertext, std::span<CryptoPP::byte> decrypted); std::span<CryptoPP::byte> ciphertext, std::span<CryptoPP::byte> decrypted);
void PfsGenCryptoKey(std::span<const CryptoPP::byte, 32> ekpfs, void PfsGenCryptoKey(std::span<const CryptoPP::byte, 32> ekpfs,
std::span<const CryptoPP::byte, 16> seed, std::span<const CryptoPP::byte, 16> seed,

View File

@ -11,6 +11,7 @@
#include "libraries/kernel/time.h" #include "libraries/kernel/time.h"
#include "libraries/system/msgdialog.h" #include "libraries/system/msgdialog.h"
#include "video_core/amdgpu/pm4_cmds.h" #include "video_core/amdgpu/pm4_cmds.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
using namespace DebugStateType; using namespace DebugStateType;
@ -168,8 +169,12 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
if ((*dump)->regs.stage_enable.IsStageEnabled(i)) { if ((*dump)->regs.stage_enable.IsStageEnabled(i)) {
auto stage = (*dump)->regs.ProgramForStage(i); auto stage = (*dump)->regs.ProgramForStage(i);
if (stage->address_lo != 0) { if (stage->address_lo != 0) {
const auto& info = AmdGpu::Liverpool::SearchBinaryInfo(stage->Address<u32*>());
auto code = stage->Code(); auto code = stage->Code();
(*dump)->stages[i] = PipelineShaderProgramDump{ (*dump)->stages[i] = PipelineShaderProgramDump{
.name = Vulkan::PipelineCache::GetShaderName(Shader::StageFromIndex(i),
info.shader_hash),
.hash = info.shader_hash,
.user_data = *stage, .user_data = *stage,
.code = std::vector<u32>{code.begin(), code.end()}, .code = std::vector<u32>{code.begin(), code.end()},
}; };
@ -191,7 +196,10 @@ void DebugStateImpl::PushRegsDumpCompute(uintptr_t base_addr, uintptr_t header_a
auto& cs = (*dump)->regs.cs_program; auto& cs = (*dump)->regs.cs_program;
cs = cs_state; cs = cs_state;
const auto& info = AmdGpu::Liverpool::SearchBinaryInfo(cs.Address<u32*>());
(*dump)->cs_data = PipelineComputerProgramDump{ (*dump)->cs_data = PipelineComputerProgramDump{
.name = Vulkan::PipelineCache::GetShaderName(Shader::Stage::Compute, info.shader_hash),
.hash = info.shader_hash,
.cs_program = cs, .cs_program = cs,
.code = std::vector<u32>{cs.Code().begin(), cs.Code().end()}, .code = std::vector<u32>{cs.Code().begin(), cs.Code().end()},
}; };

View File

@ -50,11 +50,15 @@ struct QueueDump {
}; };
struct PipelineShaderProgramDump { struct PipelineShaderProgramDump {
std::string name;
u64 hash;
Vulkan::Liverpool::ShaderProgram user_data{}; Vulkan::Liverpool::ShaderProgram user_data{};
std::vector<u32> code{}; std::vector<u32> code{};
}; };
struct PipelineComputerProgramDump { struct PipelineComputerProgramDump {
std::string name;
u64 hash;
Vulkan::Liverpool::ComputeProgram cs_program{}; Vulkan::Liverpool::ComputeProgram cs_program{};
std::vector<u32> code{}; std::vector<u32> code{};
}; };

View File

@ -1174,7 +1174,7 @@ CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump,
} }
} }
void CmdListViewer::Draw(bool only_batches_view) { void CmdListViewer::Draw(bool only_batches_view, CmdListFilter& filter) {
const auto& ctx = *GetCurrentContext(); const auto& ctx = *GetCurrentContext();
if (batch_view.open) { if (batch_view.open) {
@ -1285,6 +1285,41 @@ void CmdListViewer::Draw(bool only_batches_view) {
} }
auto& batch = std::get<BatchInfo>(event); auto& batch = std::get<BatchInfo>(event);
// filtering
{
bool remove = false;
if (filter.shader_name[0] != '\0') {
remove = true;
std::string_view shader_name{filter.shader_name};
const auto& data = frame_dump->regs.find(batch.command_addr);
if (data != frame_dump->regs.end()) {
DebugStateType::RegDump& dump = data->second;
if (dump.is_compute) {
if (dump.cs_data.name.contains(shader_name)) {
remove = false;
break;
}
} else {
for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; ++i) {
if (dump.regs.stage_enable.IsStageEnabled(i)) {
auto& stage = dump.stages[i];
if (stage.name.contains(shader_name)) {
remove = false;
break;
}
}
}
}
}
}
if (remove) {
continue;
}
}
auto const* pm4_hdr = auto const* pm4_hdr =
reinterpret_cast<PM4Header const*>(cmdb_addr + batch.start_addr); reinterpret_cast<PM4Header const*>(cmdb_addr + batch.start_addr);

View File

@ -35,6 +35,10 @@ void ParseDepthControl(u32 value, bool begin_table = true);
void ParseEqaa(u32 value, bool begin_table = true); void ParseEqaa(u32 value, bool begin_table = true);
void ParseZInfo(u32 value, bool begin_table = true); void ParseZInfo(u32 value, bool begin_table = true);
struct CmdListFilter {
char shader_name[128]{};
};
class CmdListViewer { class CmdListViewer {
DebugStateType::FrameDump* frame_dump; DebugStateType::FrameDump* frame_dump;
@ -70,7 +74,7 @@ public:
explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector<u32>& cmd_list, explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector<u32>& cmd_list,
uintptr_t base_addr = 0, std::string name = ""); uintptr_t base_addr = 0, std::string name = "");
void Draw(bool only_batches_view = false); void Draw(bool only_batches_view, CmdListFilter& filter);
}; };
} // namespace Core::Devtools::Widget } // namespace Core::Devtools::Widget

View File

@ -117,7 +117,7 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
inline std::optional<std::string> exec_cli(const char* cli) { inline std::optional<std::string> exec_cli(const char* cli) {
std::array<char, 64> buffer{}; std::array<char, 64> buffer{};
std::string output; std::string output;
const auto f = popen(cli, "rt"); const auto f = popen(cli, "r");
if (!f) { if (!f) {
pclose(f); pclose(f);
return {}; return {};

View File

@ -132,6 +132,15 @@ void FrameDumpViewer::Draw() {
} }
} }
EndDisabled(); EndDisabled();
SameLine();
if (BeginMenu("Filter")) {
TextUnformatted("Shader name");
SameLine();
InputText("##filter_shader", filter.shader_name, sizeof(filter.shader_name));
ImGui::EndMenu();
}
TextEx("Submit num"); TextEx("Submit num");
SameLine(); SameLine();
@ -187,7 +196,7 @@ void FrameDumpViewer::Draw() {
EndGroup(); EndGroup();
} }
if (is_showing && selected_cmd != -1) { if (is_showing && selected_cmd != -1) {
cmd_list_viewer[selected_cmd].Draw(is_collapsed); cmd_list_viewer[selected_cmd].Draw(is_collapsed, filter);
} }
End(); End();
} }

View File

@ -27,6 +27,8 @@ class FrameDumpViewer {
s32 selected_queue_num2; s32 selected_queue_num2;
s32 selected_cmd = -1; s32 selected_cmd = -1;
CmdListFilter filter;
public: public:
bool is_open = true; bool is_open = true;

View File

@ -19,6 +19,57 @@ constexpr float BAR_HEIGHT_MULT = 1.25f;
constexpr float FRAME_GRAPH_PADDING_Y = 3.0f; constexpr float FRAME_GRAPH_PADDING_Y = 3.0f;
constexpr static float FRAME_GRAPH_HEIGHT = 50.0f; constexpr static float FRAME_GRAPH_HEIGHT = 50.0f;
void FrameGraph::DrawFrameGraph() {
// Frame graph - inspired by
// https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times
const float full_width = GetContentRegionAvail().x;
auto pos = GetCursorScreenPos();
const ImVec2 size{full_width, FRAME_GRAPH_HEIGHT + FRAME_GRAPH_PADDING_Y * 2.0f};
ItemSize(size);
if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) {
return;
}
float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv());
float cur_pos_x = pos.x + full_width;
pos.y += FRAME_GRAPH_PADDING_Y;
const float final_pos_y = pos.y + FRAME_GRAPH_HEIGHT;
auto& draw_list = *GetWindowDrawList();
draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y},
{pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y},
IM_COL32(0x33, 0x33, 0x33, 0xFF));
draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true);
for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) {
const auto& frame_info = frame_list[(DebugState.GetFrameNum() - i) % FRAME_BUFFER_SIZE];
const float dt_factor = target_dt / frame_info.delta;
const float width = std::ceil(BAR_WIDTH_MULT / dt_factor);
const float height =
std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * FRAME_GRAPH_HEIGHT;
ImU32 color;
if (dt_factor >= 0.95f) { // BLUE
color = IM_COL32(0x33, 0x33, 0xFF, 0xFF);
} else if (dt_factor >= 0.5f) { // GREEN <> YELLOW
float t = 1.0f - (dt_factor - 0.5f) * 2.0f;
int r = (int)(0xFF * t);
color = IM_COL32(r, 0xFF, 0, 0xFF);
} else { // YELLOW <> RED
float t = dt_factor * 2.0f;
int g = (int)(0xFF * t);
color = IM_COL32(0xFF, g, 0, 0xFF);
}
draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height}, {cur_pos_x, final_pos_y},
color);
cur_pos_x -= width;
if (cur_pos_x < width) {
break;
}
}
draw_list.PopClipRect();
}
void FrameGraph::Draw() { void FrameGraph::Draw() {
if (!is_open) { if (!is_open) {
return; return;
@ -43,55 +94,9 @@ void FrameGraph::Draw() {
Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate); Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate);
Text("Flip frame: %d Gnm submit frame: %d", DebugState.flip_frame_count.load(), Text("Flip frame: %d Gnm submit frame: %d", DebugState.flip_frame_count.load(),
DebugState.gnm_frame_count.load()); DebugState.gnm_frame_count.load());
SeparatorText("Frame graph"); SeparatorText("Frame graph");
DrawFrameGraph();
const float full_width = GetContentRegionAvail().x;
// Frame graph - inspired by
// https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times
auto pos = GetCursorScreenPos();
const ImVec2 size{full_width, FRAME_GRAPH_HEIGHT + FRAME_GRAPH_PADDING_Y * 2.0f};
ItemSize(size);
if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) {
return;
}
float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv());
float cur_pos_x = pos.x + full_width;
pos.y += FRAME_GRAPH_PADDING_Y;
const float final_pos_y = pos.y + FRAME_GRAPH_HEIGHT;
draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y},
{pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y},
IM_COL32(0x33, 0x33, 0x33, 0xFF));
draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true);
for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) {
const auto& frame_info = frame_list[(DebugState.GetFrameNum() - i) % FRAME_BUFFER_SIZE];
const float dt_factor = target_dt / frame_info.delta;
const float width = std::ceil(BAR_WIDTH_MULT / dt_factor);
const float height =
std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * FRAME_GRAPH_HEIGHT;
ImU32 color;
if (dt_factor >= 0.95f) { // BLUE
color = IM_COL32(0x33, 0x33, 0xFF, 0xFF);
} else if (dt_factor >= 0.5f) { // GREEN <> YELLOW
float t = 1.0f - (dt_factor - 0.5f) * 2.0f;
int r = (int)(0xFF * t);
color = IM_COL32(r, 0xFF, 0, 0xFF);
} else { // YELLOW <> RED
float t = dt_factor * 2.0f;
int g = (int)(0xFF * t);
color = IM_COL32(0xFF, g, 0, 0xFF);
}
draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height},
{cur_pos_x, final_pos_y}, color);
cur_pos_x -= width;
if (cur_pos_x < width) {
break;
}
}
draw_list.PopClipRect();
} }
End(); End();
} }

View File

@ -16,6 +16,8 @@ class FrameGraph {
std::array<FrameInfo, FRAME_BUFFER_SIZE> frame_list{}; std::array<FrameInfo, FRAME_BUFFER_SIZE> frame_list{};
void DrawFrameGraph();
public: public:
bool is_open = true; bool is_open = true;

View File

@ -66,7 +66,7 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) {
"GetColorSliceSize()", buffer.GetColorSliceSize(), "GetColorSliceSize()", buffer.GetColorSliceSize(),
"GetTilingMode()", buffer.GetTilingMode(), "GetTilingMode()", buffer.GetTilingMode(),
"IsTiled()", buffer.IsTiled(), "IsTiled()", buffer.IsTiled(),
"NumFormat()", buffer.NumFormat() "NumFormat()", buffer.GetNumberFmt()
); );
// clang-format on // clang-format on

View File

@ -292,6 +292,17 @@ void RegView::Draw() {
EndMenuBar(); EndMenuBar();
} }
const char* shader_name = "_";
if (data.is_compute) {
shader_name = data.cs_data.name.c_str();
} else if (selected_shader >= 0) {
shader_name = data.stages[selected_shader].name.c_str();
}
TextUnformatted("Shader: ");
SameLine();
TextUnformatted(shader_name);
if (!data.is_compute && if (!data.is_compute &&
BeginChild("STAGES", {}, BeginChild("STAGES", {},
ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY)) { ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY)) {

View File

@ -112,6 +112,10 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
ReloadShader(value); ReloadShader(value);
} }
} }
SameLine();
if (Button("Copy name")) {
SetClipboardText(value.name.c_str());
}
if (value.is_patched) { if (value.is_patched) {
if (BeginCombo("Shader type", showing_bin ? "SPIRV" : "GLSL", if (BeginCombo("Shader type", showing_bin ? "SPIRV" : "GLSL",
@ -229,9 +233,16 @@ void ShaderList::Draw() {
return; return;
} }
InputTextEx("##search_shader", "Search by name", search_box, sizeof(search_box), {},
ImGuiInputTextFlags_None);
auto width = GetContentRegionAvail().x; auto width = GetContentRegionAvail().x;
int i = 0; int i = 0;
for (const auto& shader : DebugState.shader_dump_list) { for (const auto& shader : DebugState.shader_dump_list) {
if (search_box[0] != '\0' && !shader.name.contains(search_box)) {
i++;
continue;
}
char name[128]; char name[128];
if (shader.is_patched) { if (shader.is_patched) {
snprintf(name, sizeof(name), "%s (PATCH ON)", shader.name.c_str()); snprintf(name, sizeof(name), "%s (PATCH ON)", shader.name.c_str());

View File

@ -31,6 +31,8 @@ class ShaderList {
std::vector<Selection> open_shaders{}; std::vector<Selection> open_shaders{};
char search_box[128]{};
public: public:
bool open = false; bool open = false;

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/config.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "trp.h" #include "trp.h"
@ -33,12 +34,29 @@ static void removePadding(std::vector<u8>& vec) {
} }
} }
static void hexToBytes(const char* hex, unsigned char* dst) {
for (size_t i = 0; hex[i] != 0; i++) {
const unsigned char value = (hex[i] < 0x3A) ? (hex[i] - 0x30) : (hex[i] - 0x37);
dst[i / 2] |= ((i % 2) == 0) ? (value << 4) : (value);
}
}
bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string titleId) { bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string titleId) {
std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/"; std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/";
if (!std::filesystem::exists(gameSysDir)) { if (!std::filesystem::exists(gameSysDir)) {
LOG_CRITICAL(Common_Filesystem, "Game sce_sys directory doesn't exist"); LOG_CRITICAL(Common_Filesystem, "Game sce_sys directory doesn't exist");
return false; return false;
} }
const auto user_key_str = Config::getTrophyKey();
if (user_key_str.size() != 32) {
LOG_CRITICAL(Common_Filesystem, "Trophy decryption key is not specified");
return false;
}
std::array<CryptoPP::byte, 16> user_key{};
hexToBytes(user_key_str.c_str(), user_key.data());
for (int index = 0; const auto& it : std::filesystem::directory_iterator(gameSysDir)) { for (int index = 0; const auto& it : std::filesystem::directory_iterator(gameSysDir)) {
if (it.is_regular_file()) { if (it.is_regular_file()) {
GetNPcommID(trophyPath, index); GetNPcommID(trophyPath, index);
@ -97,7 +115,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit
return false; return false;
} }
file.Read(ESFM); file.Read(ESFM);
crypto.decryptEFSM(np_comm_id, esfmIv, ESFM, XML); // decrypt crypto.decryptEFSM(user_key, np_comm_id, esfmIv, ESFM, XML); // decrypt
removePadding(XML); removePadding(XML);
std::string xml_name = entry.entry_name; std::string xml_name = entry.entry_name;
size_t pos = xml_name.find("ESFM"); size_t pos = xml_name.find("ESFM");

View File

@ -40,7 +40,8 @@ void MntPoints::UnmountAll() {
m_mnt_pairs.clear(); m_mnt_pairs.clear();
} }
std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) { std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only,
bool force_base_path) {
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf // Evil games like Turok2 pass double slashes e.g /app0//game.kpf
std::string corrected_path(path); std::string corrected_path(path);
size_t pos = corrected_path.find("//"); size_t pos = corrected_path.find("//");
@ -72,7 +73,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
patch_path /= rel_path; patch_path /= rel_path;
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
std::filesystem::exists(patch_path)) { !force_base_path && std::filesystem::exists(patch_path)) {
return patch_path; return patch_path;
} }
@ -132,8 +133,10 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
return std::optional<std::filesystem::path>(current_path); return std::optional<std::filesystem::path>(current_path);
}; };
if (const auto path = search(patch_path)) { if (!force_base_path) {
return *path; if (const auto path = search(patch_path)) {
return *path;
}
} }
if (const auto path = search(host_path)) { if (const auto path = search(host_path)) {
return *path; return *path;
@ -144,6 +147,39 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
return host_path; return host_path;
} }
// TODO: Does not handle mount points inside mount points.
void MntPoints::IterateDirectory(std::string_view guest_directory,
const IterateDirectoryCallback& callback) {
const auto base_path = GetHostPath(guest_directory, nullptr, true);
const auto patch_path = GetHostPath(guest_directory, nullptr, false);
// Only need to consider patch path if it exists and does not resolve to the same as base.
const auto apply_patch = base_path != patch_path && std::filesystem::exists(patch_path);
// Pass 1: Any files that existed in the base directory, using patch directory if needed.
if (std::filesystem::exists(base_path)) {
for (const auto& entry : std::filesystem::directory_iterator(base_path)) {
if (apply_patch) {
const auto patch_entry_path = patch_path / entry.path().filename();
if (std::filesystem::exists(patch_entry_path)) {
callback(patch_entry_path, !std::filesystem::is_directory(patch_entry_path));
continue;
}
}
callback(entry.path(), !entry.is_directory());
}
}
// Pass 2: Any files that exist only in the patch directory.
if (apply_patch) {
for (const auto& entry : std::filesystem::directory_iterator(patch_path)) {
const auto base_entry_path = base_path / entry.path().filename();
if (!std::filesystem::exists(base_entry_path)) {
callback(entry.path(), !entry.is_directory());
}
}
}
}
int HandleTable::CreateHandle() { int HandleTable::CreateHandle() {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};

View File

@ -36,7 +36,11 @@ public:
void UnmountAll(); void UnmountAll();
std::filesystem::path GetHostPath(std::string_view guest_directory, std::filesystem::path GetHostPath(std::string_view guest_directory,
bool* is_read_only = nullptr); bool* is_read_only = nullptr, bool force_base_path = false);
using IterateDirectoryCallback =
std::function<void(const std::filesystem::path& host_path, bool is_file)>;
void IterateDirectory(std::string_view guest_directory,
const IterateDirectoryCallback& callback);
const MntPair* GetMountFromHostPath(const std::string& host_path) { const MntPair* GetMountFromHostPath(const std::string& host_path) {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};

View File

@ -183,13 +183,15 @@ int PS4_SYSV_ABI sceAjmInstanceSwitch() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAjmMemoryRegister() { int PS4_SYSV_ABI sceAjmMemoryRegister(u32 context_id, void* ptr, size_t num_pages) {
LOG_ERROR(Lib_Ajm, "(STUBBED) called"); // All memory is already shared with our implementation since we do not use any hardware.
LOG_TRACE(Lib_Ajm, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAjmMemoryUnregister() { int PS4_SYSV_ABI sceAjmMemoryUnregister(u32 context_id, void* ptr) {
LOG_ERROR(Lib_Ajm, "(STUBBED) called"); // All memory is already shared with our implementation since we do not use any hardware.
LOG_TRACE(Lib_Ajm, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -74,6 +74,26 @@ union AjmJobFlags {
}; };
}; };
enum class AjmStatisticsFlags : u64 {
Memory = 1 << 0,
EnginePerCodec = 1 << 15,
Engine = 1 << 16,
};
DECLARE_ENUM_FLAG_OPERATORS(AjmStatisticsFlags)
union AjmStatisticsJobFlags {
AjmStatisticsJobFlags(AjmJobFlags job_flags) : raw(job_flags.raw) {}
u64 raw;
struct {
u64 version : 3;
u64 : 12;
AjmStatisticsFlags statistics_flags : 17;
u64 : 32;
};
};
static_assert(sizeof(AjmStatisticsJobFlags) == 8);
struct AjmSidebandResult { struct AjmSidebandResult {
s32 result; s32 result;
s32 internal_result; s32 internal_result;
@ -126,6 +146,31 @@ union AjmSidebandInitParameters {
u8 reserved[8]; u8 reserved[8];
}; };
struct AjmSidebandStatisticsEngine {
float usage_batch;
float usage_interval[3];
};
struct AjmSidebandStatisticsEnginePerCodec {
u8 codec_count;
u8 codec_id[3];
float codec_percentage[3];
};
struct AjmSidebandStatisticsMemory {
u32 instance_free;
u32 buffer_free;
u32 batch_size;
u32 input_size;
u32 output_size;
u32 small_size;
};
struct AjmSidebandStatisticsEngineParameters {
u32 interval_count;
float interval[3];
};
union AjmInstanceFlags { union AjmInstanceFlags {
u64 raw; u64 raw;
struct { struct {
@ -178,8 +223,8 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context, AjmCodecType codec_type, AjmI
int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context, u32 instance); int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context, u32 instance);
int PS4_SYSV_ABI sceAjmInstanceExtend(); int PS4_SYSV_ABI sceAjmInstanceExtend();
int PS4_SYSV_ABI sceAjmInstanceSwitch(); int PS4_SYSV_ABI sceAjmInstanceSwitch();
int PS4_SYSV_ABI sceAjmMemoryRegister(); int PS4_SYSV_ABI sceAjmMemoryRegister(u32 context_id, void* ptr, size_t num_pages);
int PS4_SYSV_ABI sceAjmMemoryUnregister(); int PS4_SYSV_ABI sceAjmMemoryUnregister(u32 context_id, void* ptr);
int PS4_SYSV_ABI sceAjmModuleRegister(u32 context, AjmCodecType codec_type, s64 reserved); int PS4_SYSV_ABI sceAjmModuleRegister(u32 context, AjmCodecType codec_type, s64 reserved);
int PS4_SYSV_ABI sceAjmModuleUnregister(); int PS4_SYSV_ABI sceAjmModuleUnregister();
int PS4_SYSV_ABI sceAjmStrError(); int PS4_SYSV_ABI sceAjmStrError();

View File

@ -54,6 +54,8 @@ public:
: m_p_begin(begin), m_p_current(m_p_begin), m_size(size) {} : m_p_begin(begin), m_p_current(m_p_begin), m_size(size) {}
AjmBatchBuffer(std::span<u8> data) AjmBatchBuffer(std::span<u8> data)
: m_p_begin(data.data()), m_p_current(m_p_begin), m_size(data.size()) {} : m_p_begin(data.data()), m_p_current(m_p_begin), m_size(data.size()) {}
AjmBatchBuffer(AjmChunkBuffer& buffer)
: AjmBatchBuffer(reinterpret_cast<u8*>(buffer.p_address), buffer.size) {}
AjmBatchBuffer SubBuffer(size_t size = s_dynamic_extent) { AjmBatchBuffer SubBuffer(size_t size = s_dynamic_extent) {
auto current = m_p_current; auto current = m_p_current;
@ -113,6 +115,88 @@ private:
size_t m_size{}; size_t m_size{};
}; };
AjmJob AjmStatisticsJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
std::optional<AjmJobFlags> job_flags = {};
std::optional<AjmChunkBuffer> input_control_buffer = {};
std::optional<AjmChunkBuffer> output_control_buffer = {};
AjmJob job;
job.instance_id = instance_id;
while (!batch_buffer.IsEmpty()) {
auto& header = batch_buffer.Peek<AjmChunkHeader>();
switch (header.ident) {
case Identifier::AjmIdentInputControlBuf: {
ASSERT_MSG(!input_control_buffer.has_value(),
"Only one instance of input control buffer is allowed per job");
const auto& buffer = batch_buffer.Consume<AjmChunkBuffer>();
if (buffer.p_address != nullptr && buffer.size != 0) {
input_control_buffer = buffer;
}
break;
}
case Identifier::AjmIdentControlFlags: {
ASSERT_MSG(!job_flags.has_value(), "Only one instance of job flags is allowed per job");
auto& chunk = batch_buffer.Consume<AjmChunkFlags>();
job_flags = AjmJobFlags{
.raw = (u64(chunk.header.payload) << 32) + chunk.flags_low,
};
break;
}
case Identifier::AjmIdentReturnAddressBuf: {
// Ignore return address buffers.
batch_buffer.Skip<AjmChunkBuffer>();
break;
}
case Identifier::AjmIdentOutputControlBuf: {
ASSERT_MSG(!output_control_buffer.has_value(),
"Only one instance of output control buffer is allowed per job");
const auto& buffer = batch_buffer.Consume<AjmChunkBuffer>();
if (buffer.p_address != nullptr && buffer.size != 0) {
output_control_buffer = buffer;
}
break;
}
default:
UNREACHABLE_MSG("Unknown chunk: {}", header.ident);
}
}
ASSERT(job_flags.has_value());
job.flags = job_flags.value();
AjmStatisticsJobFlags flags(job.flags);
if (input_control_buffer.has_value()) {
AjmBatchBuffer input_batch(input_control_buffer.value());
if (True(flags.statistics_flags & AjmStatisticsFlags::Engine)) {
job.input.statistics_engine_parameters =
input_batch.Consume<AjmSidebandStatisticsEngineParameters>();
}
}
if (output_control_buffer.has_value()) {
AjmBatchBuffer output_batch(output_control_buffer.value());
job.output.p_result = &output_batch.Consume<AjmSidebandResult>();
*job.output.p_result = AjmSidebandResult{};
if (True(flags.statistics_flags & AjmStatisticsFlags::Engine)) {
job.output.p_engine = &output_batch.Consume<AjmSidebandStatisticsEngine>();
*job.output.p_engine = AjmSidebandStatisticsEngine{};
}
if (True(flags.statistics_flags & AjmStatisticsFlags::EnginePerCodec)) {
job.output.p_engine_per_codec =
&output_batch.Consume<AjmSidebandStatisticsEnginePerCodec>();
*job.output.p_engine_per_codec = AjmSidebandStatisticsEnginePerCodec{};
}
if (True(flags.statistics_flags & AjmStatisticsFlags::Memory)) {
job.output.p_memory = &output_batch.Consume<AjmSidebandStatisticsMemory>();
*job.output.p_memory = AjmSidebandStatisticsMemory{};
}
}
return job;
}
AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
std::optional<AjmJobFlags> job_flags = {}; std::optional<AjmJobFlags> job_flags = {};
std::optional<AjmChunkBuffer> input_control_buffer = {}; std::optional<AjmChunkBuffer> input_control_buffer = {};
@ -155,15 +239,6 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
batch_buffer.Skip<AjmChunkBuffer>(); batch_buffer.Skip<AjmChunkBuffer>();
break; break;
} }
case Identifier::AjmIdentInlineBuf: {
ASSERT_MSG(!output_control_buffer.has_value(),
"Only one instance of inline buffer is allowed per job");
const auto& buffer = batch_buffer.Consume<AjmChunkBuffer>();
if (buffer.p_address != nullptr && buffer.size != 0) {
inline_buffer = buffer;
}
break;
}
case Identifier::AjmIdentOutputRunBuf: { case Identifier::AjmIdentOutputRunBuf: {
auto& buffer = batch_buffer.Consume<AjmChunkBuffer>(); auto& buffer = batch_buffer.Consume<AjmChunkBuffer>();
u8* p_begin = reinterpret_cast<u8*>(buffer.p_address); u8* p_begin = reinterpret_cast<u8*>(buffer.p_address);
@ -186,13 +261,12 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
} }
} }
ASSERT(job_flags.has_value());
job.flags = job_flags.value(); job.flags = job_flags.value();
// Initialize sideband input parameters // Initialize sideband input parameters
if (input_control_buffer.has_value()) { if (input_control_buffer.has_value()) {
AjmBatchBuffer input_batch(reinterpret_cast<u8*>(input_control_buffer->p_address), AjmBatchBuffer input_batch(input_control_buffer.value());
input_control_buffer->size);
const auto sideband_flags = job_flags->sideband_flags; const auto sideband_flags = job_flags->sideband_flags;
if (True(sideband_flags & AjmJobSidebandFlags::Format) && !input_batch.IsEmpty()) { if (True(sideband_flags & AjmJobSidebandFlags::Format) && !input_batch.IsEmpty()) {
job.input.format = input_batch.Consume<AjmSidebandFormat>(); job.input.format = input_batch.Consume<AjmSidebandFormat>();
@ -202,6 +276,9 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
} }
const auto control_flags = job_flags.value().control_flags; const auto control_flags = job_flags.value().control_flags;
if (True(control_flags & AjmJobControlFlags::Resample)) {
job.input.resample_parameters = input_batch.Consume<AjmSidebandResampleParameters>();
}
if (True(control_flags & AjmJobControlFlags::Initialize)) { if (True(control_flags & AjmJobControlFlags::Initialize)) {
job.input.init_params = AjmDecAt9InitializeParameters{}; job.input.init_params = AjmDecAt9InitializeParameters{};
std::memcpy(&job.input.init_params.value(), input_batch.GetCurrent(), std::memcpy(&job.input.init_params.value(), input_batch.GetCurrent(),
@ -209,21 +286,9 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
} }
} }
if (inline_buffer.has_value()) {
AjmBatchBuffer inline_batch(reinterpret_cast<u8*>(inline_buffer->p_address),
inline_buffer->size);
const auto control_flags = job_flags.value().control_flags;
if (True(control_flags & AjmJobControlFlags::Resample)) {
job.input.resample_parameters = inline_batch.Consume<AjmSidebandResampleParameters>();
}
}
// Initialize sideband output parameters // Initialize sideband output parameters
if (output_control_buffer.has_value()) { if (output_control_buffer.has_value()) {
AjmBatchBuffer output_batch(reinterpret_cast<u8*>(output_control_buffer->p_address), AjmBatchBuffer output_batch(output_control_buffer.value());
output_control_buffer->size);
job.output.p_result = &output_batch.Consume<AjmSidebandResult>(); job.output.p_result = &output_batch.Consume<AjmSidebandResult>();
*job.output.p_result = AjmSidebandResult{}; *job.output.p_result = AjmSidebandResult{};
@ -260,9 +325,21 @@ std::shared_ptr<AjmBatch> AjmBatch::FromBatchBuffer(std::span<u8> data) {
AjmBatchBuffer buffer(data); AjmBatchBuffer buffer(data);
while (!buffer.IsEmpty()) { while (!buffer.IsEmpty()) {
auto& job_chunk = buffer.Consume<AjmChunkJob>(); auto& job_chunk = buffer.Consume<AjmChunkJob>();
if (job_chunk.header.ident == AjmIdentInlineBuf) {
// Inline buffers are used to store sideband input data.
// We should just skip them as they do not require any special handling.
buffer.Advance(job_chunk.size);
continue;
}
ASSERT(job_chunk.header.ident == AjmIdentJob); ASSERT(job_chunk.header.ident == AjmIdentJob);
auto instance_id = job_chunk.header.payload; auto instance_id = job_chunk.header.payload;
batch->jobs.push_back(AjmJobFromBatchBuffer(instance_id, buffer.SubBuffer(job_chunk.size))); if (instance_id == AJM_INSTANCE_STATISTICS) {
batch->jobs.push_back(
AjmStatisticsJobFromBatchBuffer(instance_id, buffer.SubBuffer(job_chunk.size)));
} else {
batch->jobs.push_back(
AjmJobFromBatchBuffer(instance_id, buffer.SubBuffer(job_chunk.size)));
}
} }
return batch; return batch;

View File

@ -12,6 +12,7 @@
#include <atomic> #include <atomic>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <optional>
#include <semaphore> #include <semaphore>
#include <span> #include <span>
#include <vector> #include <vector>
@ -22,6 +23,7 @@ struct AjmJob {
struct Input { struct Input {
std::optional<AjmDecAt9InitializeParameters> init_params; std::optional<AjmDecAt9InitializeParameters> init_params;
std::optional<AjmSidebandResampleParameters> resample_parameters; std::optional<AjmSidebandResampleParameters> resample_parameters;
std::optional<AjmSidebandStatisticsEngineParameters> statistics_engine_parameters;
std::optional<AjmSidebandFormat> format; std::optional<AjmSidebandFormat> format;
std::optional<AjmSidebandGaplessDecode> gapless_decode; std::optional<AjmSidebandGaplessDecode> gapless_decode;
std::vector<u8> buffer; std::vector<u8> buffer;
@ -32,6 +34,9 @@ struct AjmJob {
AjmSidebandResult* p_result = nullptr; AjmSidebandResult* p_result = nullptr;
AjmSidebandStream* p_stream = nullptr; AjmSidebandStream* p_stream = nullptr;
AjmSidebandFormat* p_format = nullptr; AjmSidebandFormat* p_format = nullptr;
AjmSidebandStatisticsMemory* p_memory = nullptr;
AjmSidebandStatisticsEnginePerCodec* p_engine_per_codec = nullptr;
AjmSidebandStatisticsEngine* p_engine = nullptr;
AjmSidebandGaplessDecode* p_gapless_decode = nullptr; AjmSidebandGaplessDecode* p_gapless_decode = nullptr;
AjmSidebandMFrame* p_mframe = nullptr; AjmSidebandMFrame* p_mframe = nullptr;
u8* p_codec_info = nullptr; u8* p_codec_info = nullptr;

View File

@ -9,6 +9,7 @@
#include "core/libraries/ajm/ajm_context.h" #include "core/libraries/ajm/ajm_context.h"
#include "core/libraries/ajm/ajm_error.h" #include "core/libraries/ajm/ajm_error.h"
#include "core/libraries/ajm/ajm_instance.h" #include "core/libraries/ajm/ajm_instance.h"
#include "core/libraries/ajm/ajm_instance_statistics.h"
#include "core/libraries/ajm/ajm_mp3.h" #include "core/libraries/ajm/ajm_mp3.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
@ -70,15 +71,19 @@ void AjmContext::ProcessBatch(u32 id, std::span<AjmJob> jobs) {
LOG_TRACE(Lib_Ajm, "Processing job {} for instance {}. flags = {:#x}", id, job.instance_id, LOG_TRACE(Lib_Ajm, "Processing job {} for instance {}. flags = {:#x}", id, job.instance_id,
job.flags.raw); job.flags.raw);
std::shared_ptr<AjmInstance> instance; if (job.instance_id == AJM_INSTANCE_STATISTICS) {
{ AjmInstanceStatistics::Getinstance().ExecuteJob(job);
std::shared_lock lock(instances_mutex); } else {
auto* p_instance = instances.Get(job.instance_id); std::shared_ptr<AjmInstance> instance;
ASSERT_MSG(p_instance != nullptr, "Attempting to execute job on null instance"); {
instance = *p_instance; std::shared_lock lock(instances_mutex);
} auto* p_instance = instances.Get(job.instance_id);
ASSERT_MSG(p_instance != nullptr, "Attempting to execute job on null instance");
instance = *p_instance;
}
instance->ExecuteJob(job); instance->ExecuteJob(job);
}
} }
} }

View File

@ -68,11 +68,11 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
m_codec->Initialize(&params, sizeof(params)); m_codec->Initialize(&params, sizeof(params));
} }
if (job.input.resample_parameters.has_value()) { if (job.input.resample_parameters.has_value()) {
UNREACHABLE_MSG("Unimplemented: resample parameters"); LOG_ERROR(Lib_Ajm, "Unimplemented: resample parameters");
m_resample_parameters = job.input.resample_parameters.value(); m_resample_parameters = job.input.resample_parameters.value();
} }
if (job.input.format.has_value()) { if (job.input.format.has_value()) {
UNREACHABLE_MSG("Unimplemented: format parameters"); LOG_ERROR(Lib_Ajm, "Unimplemented: format parameters");
m_format = job.input.format.value(); m_format = job.input.format.value();
} }
if (job.input.gapless_decode.has_value()) { if (job.input.gapless_decode.has_value()) {

View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/libraries/ajm/ajm.h"
#include "core/libraries/ajm/ajm_instance_statistics.h"
namespace Libraries::Ajm {
void AjmInstanceStatistics::ExecuteJob(AjmJob& job) {
if (job.output.p_engine) {
job.output.p_engine->usage_batch = 0.01;
const auto ic = job.input.statistics_engine_parameters->interval_count;
for (u32 idx = 0; idx < ic; ++idx) {
job.output.p_engine->usage_interval[idx] = 0.01;
}
}
if (job.output.p_engine_per_codec) {
job.output.p_engine_per_codec->codec_count = 1;
job.output.p_engine_per_codec->codec_id[0] = static_cast<u8>(AjmCodecType::At9Dec);
job.output.p_engine_per_codec->codec_percentage[0] = 0.01;
}
if (job.output.p_memory) {
job.output.p_memory->instance_free = 0x400000;
job.output.p_memory->buffer_free = 0x400000;
job.output.p_memory->batch_size = 0x4200;
job.output.p_memory->input_size = 0x2000;
job.output.p_memory->output_size = 0x2000;
job.output.p_memory->small_size = 0x200;
}
}
AjmInstanceStatistics& AjmInstanceStatistics::Getinstance() {
static AjmInstanceStatistics instance;
return instance;
}
} // namespace Libraries::Ajm

View File

@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/libraries/ajm/ajm_batch.h"
namespace Libraries::Ajm {
class AjmInstanceStatistics {
public:
void ExecuteJob(AjmJob& job);
static AjmInstanceStatistics& Getinstance();
};
} // namespace Libraries::Ajm

View File

@ -145,8 +145,10 @@ int PS4_SYSV_ABI sceAppContentDownloadDataFormat() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAppContentDownloadDataGetAvailableSpaceKb() { int PS4_SYSV_ABI sceAppContentDownloadDataGetAvailableSpaceKb(OrbisAppContentMountPoint* mountPoint,
u64* availableSpaceKb) {
LOG_ERROR(Lib_AppContent, "(STUBBED) called"); LOG_ERROR(Lib_AppContent, "(STUBBED) called");
*availableSpaceKb = 1048576;
return ORBIS_OK; return ORBIS_OK;
} }
@ -294,9 +296,9 @@ int PS4_SYSV_ABI sceAppContentTemporaryDataFormat() {
} }
int PS4_SYSV_ABI sceAppContentTemporaryDataGetAvailableSpaceKb( int PS4_SYSV_ABI sceAppContentTemporaryDataGetAvailableSpaceKb(
const OrbisAppContentMountPoint* mountPoint, size_t* availableSpaceKb) { const OrbisAppContentMountPoint* mountPoint, u64* availableSpaceKb) {
LOG_ERROR(Lib_AppContent, "(STUBBED) called"); LOG_ERROR(Lib_AppContent, "(STUBBED) called");
*availableSpaceKb = 1073741824; *availableSpaceKb = 1048576;
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -84,7 +84,8 @@ int PS4_SYSV_ABI sceAppContentDownload0Shrink();
int PS4_SYSV_ABI sceAppContentDownload1Expand(); int PS4_SYSV_ABI sceAppContentDownload1Expand();
int PS4_SYSV_ABI sceAppContentDownload1Shrink(); int PS4_SYSV_ABI sceAppContentDownload1Shrink();
int PS4_SYSV_ABI sceAppContentDownloadDataFormat(); int PS4_SYSV_ABI sceAppContentDownloadDataFormat();
int PS4_SYSV_ABI sceAppContentDownloadDataGetAvailableSpaceKb(); int PS4_SYSV_ABI sceAppContentDownloadDataGetAvailableSpaceKb(OrbisAppContentMountPoint* mountPoint,
u64* availableSpaceKb);
int PS4_SYSV_ABI sceAppContentGetAddcontDownloadProgress(); int PS4_SYSV_ABI sceAppContentGetAddcontDownloadProgress();
int PS4_SYSV_ABI sceAppContentGetAddcontInfo(u32 service_label, int PS4_SYSV_ABI sceAppContentGetAddcontInfo(u32 service_label,
const OrbisNpUnifiedEntitlementLabel* entitlementLabel, const OrbisNpUnifiedEntitlementLabel* entitlementLabel,
@ -105,7 +106,7 @@ int PS4_SYSV_ABI sceAppContentSmallSharedDataMount();
int PS4_SYSV_ABI sceAppContentSmallSharedDataUnmount(); int PS4_SYSV_ABI sceAppContentSmallSharedDataUnmount();
int PS4_SYSV_ABI sceAppContentTemporaryDataFormat(); int PS4_SYSV_ABI sceAppContentTemporaryDataFormat();
int PS4_SYSV_ABI sceAppContentTemporaryDataGetAvailableSpaceKb( int PS4_SYSV_ABI sceAppContentTemporaryDataGetAvailableSpaceKb(
const OrbisAppContentMountPoint* mountPoint, size_t* availableSpaceKb); const OrbisAppContentMountPoint* mountPoint, u64* availableSpaceKb);
int PS4_SYSV_ABI sceAppContentTemporaryDataMount(); int PS4_SYSV_ABI sceAppContentTemporaryDataMount();
int PS4_SYSV_ABI sceAppContentTemporaryDataMount2(OrbisAppContentTemporaryDataOption option, int PS4_SYSV_ABI sceAppContentTemporaryDataMount2(OrbisAppContentTemporaryDataOption option,
OrbisAppContentMountPoint* mountPoint); OrbisAppContentMountPoint* mountPoint);

View File

@ -3,140 +3,48 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <shared_mutex> #include <stop_token>
#include <thread>
#include <magic_enum/magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "common/assert.h" #include "common/assert.h"
#include "common/config.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/thread.h"
#include "core/libraries/audio/audioout.h" #include "core/libraries/audio/audioout.h"
#include "core/libraries/audio/audioout_backend.h"
#include "core/libraries/audio/audioout_error.h" #include "core/libraries/audio/audioout_error.h"
#include "core/libraries/audio/sdl_audio.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
namespace Libraries::AudioOut { namespace Libraries::AudioOut {
struct PortOut { std::mutex port_open_mutex{};
void* impl;
u32 samples_num;
u32 freq;
OrbisAudioOutParamFormat format;
OrbisAudioOutPort type;
int channels_num;
bool is_float;
std::array<int, 8> volume;
u8 sample_size;
bool is_open;
};
std::shared_mutex ports_mutex;
std::array<PortOut, SCE_AUDIO_OUT_NUM_PORTS> ports_out{}; std::array<PortOut, SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
static std::unique_ptr<AudioOutBackend> audio; static std::unique_ptr<AudioOutBackend> audio;
static std::string_view GetAudioOutPort(OrbisAudioOutPort port) { static AudioFormatInfo GetFormatInfo(const OrbisAudioOutParamFormat format) {
switch (port) { static constexpr std::array<AudioFormatInfo, 8> format_infos = {{
case OrbisAudioOutPort::Main: // S16Mono
return "MAIN"; {false, 2, 1, {0}},
case OrbisAudioOutPort::Bgm: // S16Stereo
return "BGM"; {false, 2, 2, {0, 1}},
case OrbisAudioOutPort::Voice: // S16_8CH
return "VOICE"; {false, 2, 8, {0, 1, 2, 3, 4, 5, 6, 7}},
case OrbisAudioOutPort::Personal: // FloatMono
return "PERSONAL"; {true, 4, 1, {0}},
case OrbisAudioOutPort::Padspk: // FloatStereo
return "PADSPK"; {true, 4, 2, {0, 1}},
case OrbisAudioOutPort::Aux: // Float_8CH
return "AUX"; {true, 4, 8, {0, 1, 2, 3, 4, 5, 6, 7}},
default: // S16_8CH_Std
return "INVALID"; {false, 2, 8, {0, 1, 2, 3, 6, 7, 4, 5}},
} // Float_8CH_Std
} {true, 4, 8, {0, 1, 2, 3, 6, 7, 4, 5}},
}};
static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) { const auto index = static_cast<u32>(format);
switch (param) { ASSERT_MSG(index < format_infos.size(), "Unknown audio format {}", index);
case OrbisAudioOutParamFormat::S16Mono: return format_infos[index];
return "S16_MONO";
case OrbisAudioOutParamFormat::S16Stereo:
return "S16_STEREO";
case OrbisAudioOutParamFormat::S16_8CH:
return "S16_8CH";
case OrbisAudioOutParamFormat::FloatMono:
return "FLOAT_MONO";
case OrbisAudioOutParamFormat::FloatStereo:
return "FLOAT_STEREO";
case OrbisAudioOutParamFormat::Float_8CH:
return "FLOAT_8CH";
case OrbisAudioOutParamFormat::S16_8CH_Std:
return "S16_8CH_STD";
case OrbisAudioOutParamFormat::Float_8CH_Std:
return "FLOAT_8CH_STD";
default:
return "INVALID";
}
}
static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) {
switch (attr) {
case OrbisAudioOutParamAttr::None:
return "NONE";
case OrbisAudioOutParamAttr::Restricted:
return "RESTRICTED";
case OrbisAudioOutParamAttr::MixToMain:
return "MIX_TO_MAIN";
default:
return "INVALID";
}
}
static bool IsFormatFloat(const OrbisAudioOutParamFormat format) {
switch (format) {
case OrbisAudioOutParamFormat::S16Mono:
case OrbisAudioOutParamFormat::S16Stereo:
case OrbisAudioOutParamFormat::S16_8CH:
case OrbisAudioOutParamFormat::S16_8CH_Std:
return false;
case OrbisAudioOutParamFormat::FloatMono:
case OrbisAudioOutParamFormat::FloatStereo:
case OrbisAudioOutParamFormat::Float_8CH:
case OrbisAudioOutParamFormat::Float_8CH_Std:
return true;
default:
UNREACHABLE_MSG("Unknown format");
}
}
static int GetFormatNumChannels(const OrbisAudioOutParamFormat format) {
switch (format) {
case OrbisAudioOutParamFormat::S16Mono:
case OrbisAudioOutParamFormat::FloatMono:
return 1;
case OrbisAudioOutParamFormat::S16Stereo:
case OrbisAudioOutParamFormat::FloatStereo:
return 2;
case OrbisAudioOutParamFormat::S16_8CH:
case OrbisAudioOutParamFormat::Float_8CH:
case OrbisAudioOutParamFormat::S16_8CH_Std:
case OrbisAudioOutParamFormat::Float_8CH_Std:
return 8;
default:
UNREACHABLE_MSG("Unknown format");
}
}
static u8 GetFormatSampleSize(const OrbisAudioOutParamFormat format) {
switch (format) {
case OrbisAudioOutParamFormat::S16Mono:
case OrbisAudioOutParamFormat::S16Stereo:
case OrbisAudioOutParamFormat::S16_8CH:
case OrbisAudioOutParamFormat::S16_8CH_Std:
return 2;
case OrbisAudioOutParamFormat::FloatMono:
case OrbisAudioOutParamFormat::FloatStereo:
case OrbisAudioOutParamFormat::Float_8CH:
case OrbisAudioOutParamFormat::Float_8CH_Std:
return 4;
default:
UNREACHABLE_MSG("Unknown format");
}
} }
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() { int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() {
@ -185,15 +93,20 @@ int PS4_SYSV_ABI sceAudioOutClose(s32 handle) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
} }
std::scoped_lock lock(ports_mutex); std::unique_lock open_lock{port_open_mutex};
auto& port = ports_out.at(handle - 1); auto& port = ports_out.at(handle - 1);
if (!port.is_open) { {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; std::unique_lock lock{port.mutex};
if (!port.IsOpen()) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
std::free(port.output_buffer);
port.output_buffer = nullptr;
port.output_ready = false;
port.impl = nullptr;
} }
// Stop outside of port lock scope to prevent deadlocks.
audio->Close(port.impl); port.output_thread.Stop();
port.impl = nullptr;
port.is_open = false;
return ORBIS_OK; return ORBIS_OK;
} }
@ -262,35 +175,34 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
} }
std::scoped_lock lock(ports_mutex); auto& port = ports_out.at(handle - 1);
const auto& port = ports_out.at(handle - 1); {
if (!port.is_open) { std::unique_lock lock{port.mutex};
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; if (!port.IsOpen()) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
switch (port.type) {
case OrbisAudioOutPort::Main:
case OrbisAudioOutPort::Bgm:
case OrbisAudioOutPort::Voice:
state->output = 1;
state->channel = port.format_info.num_channels > 2 ? 2 : port.format_info.num_channels;
break;
case OrbisAudioOutPort::Personal:
case OrbisAudioOutPort::Padspk:
state->output = 4;
state->channel = 1;
break;
case OrbisAudioOutPort::Aux:
state->output = 0;
state->channel = 0;
break;
default:
UNREACHABLE();
}
state->rerouteCounter = 0;
state->volume = 127;
} }
state->rerouteCounter = 0;
state->volume = 127;
switch (port.type) {
case OrbisAudioOutPort::Main:
case OrbisAudioOutPort::Bgm:
case OrbisAudioOutPort::Voice:
state->output = 1;
state->channel = port.channels_num > 2 ? 2 : port.channels_num;
break;
case OrbisAudioOutPort::Personal:
case OrbisAudioOutPort::Padspk:
state->output = 4;
state->channel = 1;
break;
case OrbisAudioOutPort::Aux:
state->output = 0;
state->channel = 0;
break;
default:
UNREACHABLE();
}
return ORBIS_OK; return ORBIS_OK;
} }
@ -358,6 +270,31 @@ int PS4_SYSV_ABI sceAudioOutMbusInit() {
return ORBIS_OK; return ORBIS_OK;
} }
static void AudioOutputThread(PortOut* port, const std::stop_token& stop) {
{
const auto thread_name = fmt::format("shadPS4:AudioOutputThread:{}", fmt::ptr(port));
Common::SetCurrentThreadName(thread_name.c_str());
}
Common::AccurateTimer timer(
std::chrono::nanoseconds(1000000000ULL * port->buffer_frames / port->sample_rate));
while (true) {
timer.Start();
{
std::unique_lock lock{port->mutex};
if (port->output_cv.wait(lock, stop, [&] { return port->output_ready; })) {
port->impl->Output(port->output_buffer);
port->output_ready = false;
}
}
port->output_cv.notify_one();
if (stop.stop_requested()) {
break;
}
timer.End();
}
}
s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
OrbisAudioOutPort port_type, s32 index, u32 length, OrbisAudioOutPort port_type, s32 index, u32 length,
u32 sample_rate, u32 sample_rate,
@ -365,9 +302,9 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
LOG_INFO(Lib_AudioOut, LOG_INFO(Lib_AudioOut,
"id = {} port_type = {} index = {} length = {} sample_rate = {} " "id = {} port_type = {} index = {} length = {} sample_rate = {} "
"param_type = {} attr = {}", "param_type = {} attr = {}",
user_id, GetAudioOutPort(port_type), index, length, sample_rate, user_id, magic_enum::enum_name(port_type), index, length, sample_rate,
GetAudioOutParamFormat(param_type.data_format), magic_enum::enum_name(param_type.data_format.Value()),
GetAudioOutParamAttr(param_type.attributes)); magic_enum::enum_name(param_type.attributes.Value()));
if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) && if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) &&
(port_type != OrbisAudioOutPort::Aux)) { (port_type != OrbisAudioOutPort::Aux)) {
LOG_ERROR(Lib_AudioOut, "Invalid port type"); LOG_ERROR(Lib_AudioOut, "Invalid port type");
@ -398,24 +335,30 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT; return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT;
} }
std::scoped_lock lock{ports_mutex}; std::unique_lock open_lock{port_open_mutex};
const auto port = std::ranges::find(ports_out, false, &PortOut::is_open); const auto port =
std::ranges::find_if(ports_out, [&](const PortOut& p) { return !p.IsOpen(); });
if (port == ports_out.end()) { if (port == ports_out.end()) {
LOG_ERROR(Lib_AudioOut, "Audio ports are full"); LOG_ERROR(Lib_AudioOut, "Audio ports are full");
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
} }
port->is_open = true; {
port->type = port_type; std::unique_lock port_lock(port->mutex);
port->samples_num = length;
port->freq = sample_rate;
port->format = format;
port->is_float = IsFormatFloat(format);
port->channels_num = GetFormatNumChannels(format);
port->sample_size = GetFormatSampleSize(format);
port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB);
port->impl = audio->Open(port->is_float, port->channels_num, port->freq); port->type = port_type;
port->format_info = GetFormatInfo(format);
port->sample_rate = sample_rate;
port->buffer_frames = length;
port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB);
port->impl = audio->Open(*port);
port->output_buffer = std::malloc(port->BufferSize());
port->output_ready = false;
port->output_thread.Run(
[port](const std::stop_token& stop) { AudioOutputThread(&*port, stop); });
}
return std::distance(ports_out.begin(), port) + 1; return std::distance(ports_out.begin(), port) + 1;
} }
@ -424,29 +367,33 @@ int PS4_SYSV_ABI sceAudioOutOpenEx() {
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) { s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) {
if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
} }
if (ptr == nullptr) {
// Nothing to output
return ORBIS_OK;
}
auto& port = ports_out.at(handle - 1); auto& port = ports_out.at(handle - 1);
if (!port.is_open) { {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; std::unique_lock lock{port.mutex};
if (!port.IsOpen()) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
port.output_cv.wait(lock, [&] { return !port.output_ready; });
if (ptr != nullptr && port.IsOpen()) {
std::memcpy(port.output_buffer, ptr, port.BufferSize());
port.output_ready = true;
}
} }
port.output_cv.notify_one();
const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
audio->Output(port.impl, ptr, data_size);
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) { int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) {
for (u32 i = 0; i < num; i++) { for (u32 i = 0; i < num; i++) {
if (const auto err = sceAudioOutOutput(param[i].handle, param[i].ptr); err != 0) const auto [handle, ptr] = param[i];
return err; if (const auto ret = sceAudioOutOutput(handle, ptr); ret != ORBIS_OK) {
return ret;
}
} }
return ORBIS_OK; return ORBIS_OK;
} }
@ -546,40 +493,19 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
} }
std::scoped_lock lock(ports_mutex);
auto& port = ports_out.at(handle - 1); auto& port = ports_out.at(handle - 1);
if (!port.is_open) { {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; std::unique_lock lock{port.mutex};
} if (!port.IsOpen()) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
for (int i = 0; i < port.channels_num; i++, flag >>= 1u) {
auto bit = flag & 0x1u;
if (bit == 1) {
int src_index = i;
if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std ||
port.format == OrbisAudioOutParamFormat::S16_8CH_Std) {
switch (i) {
case 4:
src_index = 6;
break;
case 5:
src_index = 7;
break;
case 6:
src_index = 4;
break;
case 7:
src_index = 5;
break;
default:
break;
}
}
port.volume[i] = vol[src_index];
} }
for (int i = 0; i < port.format_info.num_channels; i++, flag >>= 1u) {
if (flag & 0x1u) {
port.volume[i] = vol[i];
}
}
port.impl->SetVolume(port.volume);
} }
audio->SetVolume(port.impl, port.volume);
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -3,20 +3,26 @@
#pragma once #pragma once
#include "common/bit_field.h" #include <condition_variable>
#include <memory>
#include <mutex>
#include "common/bit_field.h"
#include "core/libraries/kernel/threads.h"
#include "core/libraries/system/userservice.h" #include "core/libraries/system/userservice.h"
namespace Libraries::AudioOut { namespace Libraries::AudioOut {
class PortBackend;
// Main up to 8 ports, BGM 1 port, voice up to 4 ports, // Main up to 8 ports, BGM 1 port, voice up to 4 ports,
// personal up to 4 ports, padspk up to 5 ports, aux 1 port // personal up to 4 ports, padspk up to 5 ports, aux 1 port
constexpr int SCE_AUDIO_OUT_NUM_PORTS = 22; constexpr s32 SCE_AUDIO_OUT_NUM_PORTS = 22;
constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value constexpr s32 SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value
enum class OrbisAudioOutPort { Main = 0, Bgm = 1, Voice = 2, Personal = 3, Padspk = 4, Aux = 127 }; enum class OrbisAudioOutPort { Main = 0, Bgm = 1, Voice = 2, Personal = 3, Padspk = 4, Aux = 127 };
enum class OrbisAudioOutParamFormat { enum class OrbisAudioOutParamFormat : u32 {
S16Mono = 0, S16Mono = 0,
S16Stereo = 1, S16Stereo = 1,
S16_8CH = 2, S16_8CH = 2,
@ -27,7 +33,7 @@ enum class OrbisAudioOutParamFormat {
Float_8CH_Std = 7 Float_8CH_Std = 7
}; };
enum class OrbisAudioOutParamAttr { enum class OrbisAudioOutParamAttr : u32 {
None = 0, None = 0,
Restricted = 1, Restricted = 1,
MixToMain = 2, MixToMain = 2,
@ -43,7 +49,7 @@ union OrbisAudioOutParamExtendedInformation {
struct OrbisAudioOutOutputParam { struct OrbisAudioOutOutputParam {
s32 handle; s32 handle;
const void* ptr; void* ptr;
}; };
struct OrbisAudioOutPortState { struct OrbisAudioOutPortState {
@ -56,6 +62,43 @@ struct OrbisAudioOutPortState {
u64 reserved64[2]; u64 reserved64[2];
}; };
struct AudioFormatInfo {
bool is_float;
u8 sample_size;
u8 num_channels;
/// Layout array remapping channel indices, specified in this order:
/// FL, FR, FC, LFE, BL, BR, SL, SR
std::array<int, 8> channel_layout;
[[nodiscard]] u16 FrameSize() const {
return sample_size * num_channels;
}
};
struct PortOut {
std::mutex mutex;
std::unique_ptr<PortBackend> impl{};
void* output_buffer;
std::condition_variable_any output_cv;
bool output_ready;
Kernel::Thread output_thread{};
OrbisAudioOutPort type;
AudioFormatInfo format_info;
u32 sample_rate;
u32 buffer_frames;
std::array<s32, 8> volume;
[[nodiscard]] bool IsOpen() const {
return impl != nullptr;
}
[[nodiscard]] u32 BufferSize() const {
return buffer_frames * format_info.FrameSize();
}
};
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen(); int PS4_SYSV_ABI sceAudioOutDeviceIdOpen();
int PS4_SYSV_ABI sceAudioDeviceControlGet(); int PS4_SYSV_ABI sceAudioDeviceControlGet();
int PS4_SYSV_ABI sceAudioDeviceControlSet(); int PS4_SYSV_ABI sceAudioDeviceControlSet();
@ -94,7 +137,7 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
OrbisAudioOutPort port_type, s32 index, u32 length, OrbisAudioOutPort port_type, s32 index, u32 length,
u32 sample_rate, OrbisAudioOutParamExtendedInformation param_type); u32 sample_rate, OrbisAudioOutParamExtendedInformation param_type);
int PS4_SYSV_ABI sceAudioOutOpenEx(); int PS4_SYSV_ABI sceAudioOutOpenEx();
s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr); s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr);
s32 PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num); s32 PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num);
int PS4_SYSV_ABI sceAudioOutPtClose(); int PS4_SYSV_ABI sceAudioOutPtClose();
int PS4_SYSV_ABI sceAudioOutPtGetLastOutputTime(); int PS4_SYSV_ABI sceAudioOutPtGetLastOutputTime();

View File

@ -5,15 +5,30 @@
namespace Libraries::AudioOut { namespace Libraries::AudioOut {
struct PortOut;
class PortBackend {
public:
virtual ~PortBackend() = default;
/// Guaranteed to be called in intervals of at least port buffer time,
/// with size equal to port buffer size.
virtual void Output(void* ptr) = 0;
virtual void SetVolume(const std::array<int, 8>& ch_volumes) = 0;
};
class AudioOutBackend { class AudioOutBackend {
public: public:
AudioOutBackend() = default; AudioOutBackend() = default;
virtual ~AudioOutBackend() = default; virtual ~AudioOutBackend() = default;
virtual void* Open(bool is_float, int num_channels, u32 sample_rate) = 0; virtual std::unique_ptr<PortBackend> Open(PortOut& port) = 0;
virtual void Close(void* impl) = 0; };
virtual void Output(void* impl, const void* ptr, size_t size) = 0;
virtual void SetVolume(void* impl, std::array<int, 8> ch_volumes) = 0; class SDLAudioOut final : public AudioOutBackend {
public:
std::unique_ptr<PortBackend> Open(PortOut& port) override;
}; };
} // namespace Libraries::AudioOut } // namespace Libraries::AudioOut

View File

@ -1,44 +1,118 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include <SDL3/SDL_audio.h> #include <SDL3/SDL_audio.h>
#include <SDL3/SDL_init.h> #include <SDL3/SDL_hints.h>
#include <SDL3/SDL_timer.h>
#include "common/assert.h" #include "common/logging/log.h"
#include "core/libraries/audio/sdl_audio.h" #include "core/libraries/audio/audioout.h"
#include "core/libraries/audio/audioout_backend.h"
namespace Libraries::AudioOut { namespace Libraries::AudioOut {
constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold class SDLPortBackend : public PortBackend {
public:
void* SDLAudioOut::Open(bool is_float, int num_channels, u32 sample_rate) { explicit SDLPortBackend(const PortOut& port)
SDL_AudioSpec fmt; : frame_size(port.format_info.FrameSize()), guest_buffer_size(port.BufferSize()) {
SDL_zero(fmt); const SDL_AudioSpec fmt = {
fmt.format = is_float ? SDL_AUDIO_F32 : SDL_AUDIO_S16; .format = port.format_info.is_float ? SDL_AUDIO_F32LE : SDL_AUDIO_S16LE,
fmt.channels = num_channels; .channels = port.format_info.num_channels,
fmt.freq = sample_rate; .freq = static_cast<int>(port.sample_rate),
};
auto* stream = stream =
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr); SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr);
SDL_ResumeAudioStreamDevice(stream); if (stream == nullptr) {
return stream; LOG_ERROR(Lib_AudioOut, "Failed to create SDL audio stream: {}", SDL_GetError());
} return;
}
void SDLAudioOut::Close(void* impl) { CalculateQueueThreshold();
SDL_DestroyAudioStream(static_cast<SDL_AudioStream*>(impl)); if (!SDL_SetAudioStreamInputChannelMap(stream, port.format_info.channel_layout.data(),
} port.format_info.num_channels)) {
LOG_ERROR(Lib_AudioOut, "Failed to configure SDL audio stream channel map: {}",
void SDLAudioOut::Output(void* impl, const void* ptr, size_t size) { SDL_GetError());
auto* stream = static_cast<SDL_AudioStream*>(impl); SDL_DestroyAudioStream(stream);
SDL_PutAudioStreamData(stream, ptr, size); stream = nullptr;
while (SDL_GetAudioStreamAvailable(stream) > AUDIO_STREAM_BUFFER_THRESHOLD) { return;
SDL_Delay(0); }
if (!SDL_ResumeAudioStreamDevice(stream)) {
LOG_ERROR(Lib_AudioOut, "Failed to resume SDL audio stream: {}", SDL_GetError());
SDL_DestroyAudioStream(stream);
stream = nullptr;
return;
}
} }
}
void SDLAudioOut::SetVolume(void* impl, std::array<int, 8> ch_volumes) { ~SDLPortBackend() override {
// Not yet implemented if (!stream) {
return;
}
SDL_DestroyAudioStream(stream);
stream = nullptr;
}
void Output(void* ptr) override {
if (!stream) {
return;
}
// AudioOut library manages timing, but we still need to guard against the SDL
// audio queue stalling, which may happen during device changes, for example.
// Otherwise, latency may grow over time unbounded.
if (const auto queued = SDL_GetAudioStreamQueued(stream); queued >= queue_threshold) {
LOG_WARNING(Lib_AudioOut,
"SDL audio queue backed up ({} queued, {} threshold), clearing.", queued,
queue_threshold);
SDL_ClearAudioStream(stream);
// Recalculate the threshold in case this happened because of a device change.
CalculateQueueThreshold();
}
if (!SDL_PutAudioStreamData(stream, ptr, static_cast<int>(guest_buffer_size))) {
LOG_ERROR(Lib_AudioOut, "Failed to output to SDL audio stream: {}", SDL_GetError());
}
}
void SetVolume(const std::array<int, 8>& ch_volumes) override {
if (!stream) {
return;
}
// SDL does not have per-channel volumes, for now just take the maximum of the channels.
const auto vol = *std::ranges::max_element(ch_volumes);
if (!SDL_SetAudioStreamGain(stream, static_cast<float>(vol) / SCE_AUDIO_OUT_VOLUME_0DB)) {
LOG_WARNING(Lib_AudioOut, "Failed to change SDL audio stream volume: {}",
SDL_GetError());
}
}
private:
void CalculateQueueThreshold() {
SDL_AudioSpec discard;
int sdl_buffer_frames;
if (!SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(stream), &discard,
&sdl_buffer_frames)) {
LOG_WARNING(Lib_AudioOut, "Failed to get SDL audio stream buffer size: {}",
SDL_GetError());
sdl_buffer_frames = 0;
}
const auto sdl_buffer_size = sdl_buffer_frames * frame_size;
const auto new_threshold = std::max(guest_buffer_size, sdl_buffer_size) * 4;
if (host_buffer_size != sdl_buffer_size || queue_threshold != new_threshold) {
host_buffer_size = sdl_buffer_size;
queue_threshold = new_threshold;
LOG_INFO(Lib_AudioOut,
"SDL audio buffers: guest = {} bytes, host = {} bytes, threshold = {} bytes",
guest_buffer_size, host_buffer_size, queue_threshold);
}
}
u32 frame_size;
u32 guest_buffer_size;
u32 host_buffer_size{};
u32 queue_threshold{};
SDL_AudioStream* stream{};
};
std::unique_ptr<PortBackend> SDLAudioOut::Open(PortOut& port) {
return std::make_unique<SDLPortBackend>(port);
} }
} // namespace Libraries::AudioOut } // namespace Libraries::AudioOut

View File

@ -1,18 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/libraries/audio/audioout_backend.h"
namespace Libraries::AudioOut {
class SDLAudioOut final : public AudioOutBackend {
public:
void* Open(bool is_float, int num_channels, u32 sample_rate) override;
void Close(void* impl) override;
void Output(void* impl, const void* ptr, size_t size) override;
void SetVolume(void* impl, std::array<int, 8> ch_volumes) override;
};
} // namespace Libraries::AudioOut

View File

@ -3,6 +3,7 @@
#include "fiber.h" #include "fiber.h"
#include "common/elf_info.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/fiber/fiber_error.h" #include "core/libraries/fiber/fiber_error.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
@ -41,6 +42,41 @@ void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) {
} }
} }
s32 PS4_SYSV_ABI _sceFiberAttachContext(OrbisFiber* fiber, void* addr_context, u64 size_context) {
if (size_context && size_context < ORBIS_FIBER_CONTEXT_MINIMUM_SIZE) {
return ORBIS_FIBER_ERROR_RANGE;
}
if (size_context & 15) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (!addr_context || !size_context) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (fiber->addr_context) {
return ORBIS_FIBER_ERROR_INVALID;
}
fiber->addr_context = addr_context;
fiber->size_context = size_context;
fiber->context_start = addr_context;
fiber->context_end = reinterpret_cast<u8*>(addr_context) + size_context;
/* Apply signature to start of stack */
*(u64*)addr_context = kFiberStackSignature;
if (fiber->flags & FiberFlags::ContextSizeCheck) {
u64* stack_start = reinterpret_cast<u64*>(fiber->context_start);
u64* stack_end = reinterpret_cast<u64*>(fiber->context_end);
u64* stack_ptr = stack_start + 1;
while (stack_ptr < stack_end) {
*stack_ptr++ = kFiberStackSizeCheck;
}
}
return ORBIS_OK;
}
void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to, void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to,
OrbisFiberContext* ctx) { OrbisFiberContext* ctx) {
OrbisFiberContext* fiber_ctx = fiber->context; OrbisFiberContext* fiber_ctx = fiber->context;
@ -62,8 +98,7 @@ void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to,
data.entry = fiber->entry; data.entry = fiber->entry;
data.arg_on_initialize = fiber->arg_on_initialize; data.arg_on_initialize = fiber->arg_on_initialize;
data.arg_on_run_to = arg_on_run_to; data.arg_on_run_to = arg_on_run_to;
data.stack_addr = data.stack_addr = reinterpret_cast<u8*>(fiber->addr_context) + fiber->size_context;
reinterpret_cast<void*>(reinterpret_cast<u64>(fiber->addr_context) + fiber->size_context);
if (fiber->flags & FiberFlags::SetFpuRegs) { if (fiber->flags & FiberFlags::SetFpuRegs) {
data.fpucw = 0x037f; data.fpucw = 0x037f;
data.mxcsr = 0x9fc0; data.mxcsr = 0x9fc0;
@ -111,9 +146,10 @@ void PS4_SYSV_ABI _sceFiberTerminate(OrbisFiber* fiber, u64 arg_on_return, Orbis
__builtin_trap(); __builtin_trap();
} }
s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry, s32 PS4_SYSV_ABI sceFiberInitializeImpl(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry,
u64 arg_on_initialize, void* addr_context, u64 size_context, u64 arg_on_initialize, void* addr_context, u64 size_context,
const OrbisFiberOptParam* opt_param, u32 build_ver) { const OrbisFiberOptParam* opt_param, u32 flags,
u32 build_ver) {
if (!fiber || !name || !entry) { if (!fiber || !name || !entry) {
return ORBIS_FIBER_ERROR_NULL; return ORBIS_FIBER_ERROR_NULL;
} }
@ -139,12 +175,12 @@ s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFi
return ORBIS_FIBER_ERROR_INVALID; return ORBIS_FIBER_ERROR_INVALID;
} }
u32 flags = FiberFlags::None; u32 user_flags = flags;
if (build_ver >= 0x3500000) { if (build_ver >= Common::ElfInfo::FW_35) {
flags |= FiberFlags::SetFpuRegs; user_flags |= FiberFlags::SetFpuRegs;
} }
if (context_size_check) { if (context_size_check) {
flags |= FiberFlags::ContextSizeCheck; user_flags |= FiberFlags::ContextSizeCheck;
} }
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
@ -154,7 +190,7 @@ s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFi
fiber->addr_context = addr_context; fiber->addr_context = addr_context;
fiber->size_context = size_context; fiber->size_context = size_context;
fiber->context = nullptr; fiber->context = nullptr;
fiber->flags = flags; fiber->flags = user_flags;
/* /*
A low stack area is problematic, as we can easily A low stack area is problematic, as we can easily
@ -169,8 +205,7 @@ s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFi
if (addr_context != nullptr) { if (addr_context != nullptr) {
fiber->context_start = addr_context; fiber->context_start = addr_context;
fiber->context_end = fiber->context_end = reinterpret_cast<u8*>(addr_context) + size_context;
reinterpret_cast<void*>(reinterpret_cast<u64>(addr_context) + size_context);
/* Apply signature to start of stack */ /* Apply signature to start of stack */
*(u64*)addr_context = kFiberStackSignature; *(u64*)addr_context = kFiberStackSignature;
@ -221,11 +256,12 @@ s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber) {
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) { s32 PS4_SYSV_ABI sceFiberRunImpl(OrbisFiber* fiber, void* addr_context, u64 size_context,
u64 arg_on_run_to, u64* arg_on_return) {
if (!fiber) { if (!fiber) {
return ORBIS_FIBER_ERROR_NULL; return ORBIS_FIBER_ERROR_NULL;
} }
if ((u64)fiber & 7) { if ((u64)fiber & 7 || (u64)addr_context & 15) {
return ORBIS_FIBER_ERROR_ALIGNMENT; return ORBIS_FIBER_ERROR_ALIGNMENT;
} }
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
@ -237,6 +273,14 @@ s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_r
return ORBIS_FIBER_ERROR_PERMISSION; return ORBIS_FIBER_ERROR_PERMISSION;
} }
/* Caller wants to attach context and run. */
if (addr_context != nullptr || size_context != 0) {
s32 res = _sceFiberAttachContext(fiber, addr_context, size_context);
if (res < 0) {
return res;
}
}
FiberState expected = FiberState::Idle; FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) {
return ORBIS_FIBER_ERROR_STATE; return ORBIS_FIBER_ERROR_STATE;
@ -288,11 +332,12 @@ s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_r
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) { s32 PS4_SYSV_ABI sceFiberSwitchImpl(OrbisFiber* fiber, void* addr_context, u64 size_context,
u64 arg_on_run_to, u64* arg_on_run) {
if (!fiber) { if (!fiber) {
return ORBIS_FIBER_ERROR_NULL; return ORBIS_FIBER_ERROR_NULL;
} }
if ((u64)fiber & 7) { if ((u64)fiber & 7 || (u64)addr_context & 15) {
return ORBIS_FIBER_ERROR_ALIGNMENT; return ORBIS_FIBER_ERROR_ALIGNMENT;
} }
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
@ -304,6 +349,14 @@ s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_o
return ORBIS_FIBER_ERROR_PERMISSION; return ORBIS_FIBER_ERROR_PERMISSION;
} }
/* Caller wants to attach context and switch. */
if (addr_context != nullptr || size_context != 0) {
s32 res = _sceFiberAttachContext(fiber, addr_context, size_context);
if (res < 0) {
return res;
}
}
FiberState expected = FiberState::Idle; FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) {
return ORBIS_FIBER_ERROR_STATE; return ORBIS_FIBER_ERROR_STATE;
@ -462,9 +515,39 @@ s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name) {
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceFiberGetThreadFramePointerAddress(u64* addr_frame_pointer) {
if (!addr_frame_pointer) {
return ORBIS_FIBER_ERROR_NULL;
}
OrbisFiberContext* g_ctx = GetFiberContext();
if (!g_ctx) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
*addr_frame_pointer = g_ctx->rbp;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry,
u64 arg_on_initialize, void* addr_context, u64 size_context,
const OrbisFiberOptParam* opt_param, u32 build_ver) {
return sceFiberInitializeImpl(fiber, name, entry, arg_on_initialize, addr_context, size_context,
opt_param, 0, build_ver);
}
s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) {
return sceFiberRunImpl(fiber, nullptr, 0, arg_on_run_to, arg_on_return);
}
s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) {
return sceFiberSwitchImpl(fiber, nullptr, 0, arg_on_run_to, arg_on_run);
}
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberInitializeImpl); // _sceFiberInitializeWithInternalOptionImpl
LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize); LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize);
LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize); LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize);
@ -473,12 +556,20 @@ void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf); LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf);
LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread); LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread);
LIB_FUNCTION("avfGJ94g36Q", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberRunImpl); // _sceFiberAttachContextAndRun
LIB_FUNCTION("ZqhZFuzKT6U", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberSwitchImpl); // _sceFiberAttachContextAndSwitch
LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo); LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo);
LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1, LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberStartContextSizeCheck); sceFiberStartContextSizeCheck);
LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1, LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberStopContextSizeCheck); sceFiberStopContextSizeCheck);
LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename); LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename);
LIB_FUNCTION("0dy4JtMUcMQ", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberGetThreadFramePointerAddress);
} }
} // namespace Libraries::Fiber } // namespace Libraries::Fiber

View File

@ -114,5 +114,7 @@ s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void);
s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name); s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name);
s32 PS4_SYSV_ABI sceFiberGetThreadFramePointerAddress(u64* addr_frame_pointer);
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym); void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Fiber } // namespace Libraries::Fiber

View File

@ -12,6 +12,7 @@
#include "core/address_space.h" #include "core/address_space.h"
#include "core/debug_state.h" #include "core/debug_state.h"
#include "core/libraries/gnmdriver/gnm_error.h" #include "core/libraries/gnmdriver/gnm_error.h"
#include "core/libraries/gnmdriver/gnmdriver_init.h"
#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/kernel/process.h" #include "core/libraries/kernel/process.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
@ -29,7 +30,7 @@ namespace Libraries::GnmDriver {
using namespace AmdGpu; using namespace AmdGpu;
enum GnmEventIdents : u64 { enum GnmEventType : u64 {
Compute0RelMem = 0x00, Compute0RelMem = 0x00,
Compute1RelMem = 0x01, Compute1RelMem = 0x01,
Compute2RelMem = 0x02, Compute2RelMem = 0x02,
@ -54,244 +55,11 @@ enum ShaderStages : u32 {
static constexpr std::array indirect_sgpr_offsets{0u, 0u, 0x4cu, 0u, 0xccu, 0u, 0x14cu}; static constexpr std::array indirect_sgpr_offsets{0u, 0u, 0x4cu, 0u, 0xccu, 0u, 0x14cu};
static constexpr auto HwInitPacketSize = 0x100u; // Gates use of what appear to be the neo-mode init sequences but with the older
// IA_MULTI_VGT_PARAM register address. No idea what this is for as the ioctl
// clang-format off // that controls it is still a mystery, but leaving the sequences in gated behind
static constexpr std::array InitSequence{ // this flag in case we need it in the future.
// A fake preamble to mimic context reset sent by FW static constexpr bool UseNeoCompatSequences = false;
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1ffu,
0xc0017600u, 0x46u, 0x1ffu,
0xc0017600u, 0x87u, 0x1ffu,
0xc0017600u, 0xc7u, 0x1ffu,
0xc0017600u, 0x107u, 0u,
0xc0017600u, 0x147u, 0x1ffu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6000000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
};
static_assert(InitSequence.size() == 0x73 + 2);
static constexpr std::array InitSequence175{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1ffu,
0xc0017600u, 0x46u, 0x1ffu,
0xc0017600u, 0x87u, 0x1ffu,
0xc0017600u, 0xc7u, 0x1ffu,
0xc0017600u, 0x107u, 0u,
0xc0017600u, 0x147u, 0x1ffu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
};
static_assert(InitSequence175.size() == 0x73 + 2);
static constexpr std::array InitSequence200{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1701ffu,
0xc0017600u, 0x46u, 0x1701fdu,
0xc0017600u, 0x87u, 0x1701ffu,
0xc0017600u, 0xc7u, 0x1701fdu,
0xc0017600u, 0x107u, 0x17u,
0xc0017600u, 0x147u, 0x1701fdu,
0xc0017600u, 0x47u, 0x1cu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
};
static_assert(InitSequence200.size() == 0x76 + 2);
static constexpr std::array InitSequence350{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1701ffu,
0xc0017600u, 0x46u, 0x1701fdu,
0xc0017600u, 0x87u, 0x1701ffu,
0xc0017600u, 0xc7u, 0x1701fdu,
0xc0017600u, 0x107u, 0x17u,
0xc0017600u, 0x147u, 0x1701fdu,
0xc0017600u, 0x47u, 0x1cu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
0xc0016900u, 0x2aau, 0xffu,
};
static_assert(InitSequence350.size() == 0x7c + 2);
static constexpr std::array CtxInitSequence{
0xc0012800u, 0x80000000u, 0x80000000u,
0xc0001200u, 0u,
0xc0002f00u, 1u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0111000u, 0u
};
static_assert(CtxInitSequence.size() == 0x0f);
static constexpr std::array CtxInitSequence400{
0xc0012800u, 0x80000000u, 0x80000000u,
0xc0001200u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0016900u, 0x2aau, 0xffu,
0xc09e1000u,
};
static_assert(CtxInitSequence400.size() == 0x61);
// clang-format on
// In case if `submitDone` is issued we need to block submissions until GPU idle // In case if `submitDone` is issued we need to block submissions until GPU idle
static u32 submission_lock{}; static u32 submission_lock{};
@ -317,6 +85,14 @@ static void WaitGpuIdle() {
cv_lock.wait(lock, [] { return submission_lock == 0; }); cv_lock.wait(lock, [] { return submission_lock == 0; });
} }
// Write a special ending NOP packet with N DWs data block
static inline u32* WriteTrailingNop(u32* cmdbuf, u32 data_block_size) {
auto* nop = reinterpret_cast<PM4CmdNop*>(cmdbuf);
nop->header = PM4Type3Header{PM4ItOpcode::Nop, data_block_size - 1};
nop->data_block[0] = 0u; // only one out of `data_block_size` is initialized
return cmdbuf + data_block_size + 1 /* header */;
}
// Write a special ending NOP packet with N DWs data block // Write a special ending NOP packet with N DWs data block
template <u32 data_block_size> template <u32 data_block_size>
static inline u32* WriteTrailingNop(u32* cmdbuf) { static inline u32* WriteTrailingNop(u32* cmdbuf) {
@ -337,6 +113,12 @@ static inline u32* ClearContextState(u32* cmdbuf) {
return cmdbuf + ClearStateSequence.size(); return cmdbuf + ClearStateSequence.size();
} }
static inline bool IsValidEventType(Platform::InterruptId id) {
return (static_cast<u32>(id) >= static_cast<u32>(Platform::InterruptId::Compute0RelMem) &&
static_cast<u32>(id) <= static_cast<u32>(Platform::InterruptId::Compute6RelMem)) ||
static_cast<u32>(id) == static_cast<u32>(Platform::InterruptId::GfxEop);
}
s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
LOG_TRACE(Lib_GnmDriver, "called"); LOG_TRACE(Lib_GnmDriver, "called");
@ -347,8 +129,7 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
EqueueEvent kernel_event{}; EqueueEvent kernel_event{};
kernel_event.event.ident = id; kernel_event.event.ident = id;
kernel_event.event.filter = SceKernelEvent::Filter::GraphicsCore; kernel_event.event.filter = SceKernelEvent::Filter::GraphicsCore;
// The library only sets EV_ADD but it is suspected the kernel driver forces EV_CLEAR kernel_event.event.flags = SceKernelEvent::Flags::Add;
kernel_event.event.flags = SceKernelEvent::Flags::Clear;
kernel_event.event.fflags = 0; kernel_event.event.fflags = 0;
kernel_event.event.data = id; kernel_event.event.data = id;
kernel_event.event.udata = udata; kernel_event.event.udata = udata;
@ -357,11 +138,15 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
Platform::IrqC::Instance()->Register( Platform::IrqC::Instance()->Register(
static_cast<Platform::InterruptId>(id), static_cast<Platform::InterruptId>(id),
[=](Platform::InterruptId irq) { [=](Platform::InterruptId irq) {
ASSERT_MSG(irq == static_cast<Platform::InterruptId>(id), ASSERT_MSG(irq == static_cast<Platform::InterruptId>(id), "An unexpected IRQ occured");
"An unexpected IRQ occured"); // We need to convert IRQ# to event id and do
// proper filtering in trigger function // We need to convert IRQ# to event id
eq->TriggerEvent(static_cast<GnmEventIdents>(id), SceKernelEvent::Filter::GraphicsCore, if (!IsValidEventType(irq))
nullptr); return;
// Event data is expected to be an event type as per sceGnmGetEqEventType.
eq->TriggerEvent(static_cast<GnmEventType>(id), SceKernelEvent::Filter::GraphicsCore,
reinterpret_cast<void*>(id));
}, },
eq); eq);
return ORBIS_OK; return ORBIS_OK;
@ -476,7 +261,7 @@ s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) {
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
} }
eq->RemoveEvent(id); eq->RemoveEvent(id, SceKernelEvent::Filter::GraphicsCore);
Platform::IrqC::Instance()->Unregister(static_cast<Platform::InterruptId>(id), eq); Platform::IrqC::Instance()->Unregister(static_cast<Platform::InterruptId>(id), eq);
return ORBIS_OK; return ORBIS_OK;
@ -504,10 +289,14 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) {
auto vqid = gnm_vqid - 1; auto vqid = gnm_vqid - 1;
auto& asc_queue = liverpool->asc_queues[{vqid}]; auto& asc_queue = liverpool->asc_queues[{vqid}];
const auto& offs_dw = asc_next_offs_dw[vqid]; auto& offs_dw = asc_next_offs_dw[vqid];
if (next_offs_dw < offs_dw) { if (next_offs_dw < offs_dw && next_offs_dw != 0) {
ASSERT_MSG(next_offs_dw == 0, "ACB submission is split at the end of ring buffer"); // For cases if a submission is split at the end of the ring buffer, we need to submit it in
// two parts to handle the wrap
liverpool->SubmitAsc(gnm_vqid, {reinterpret_cast<const u32*>(asc_queue.map_addr) + offs_dw,
asc_queue.ring_size_dw - offs_dw});
offs_dw = 0;
} }
const auto* acb_ptr = reinterpret_cast<const u32*>(asc_queue.map_addr) + offs_dw; const auto* acb_ptr = reinterpret_cast<const u32*>(asc_queue.map_addr) + offs_dw;
@ -594,9 +383,16 @@ s32 PS4_SYSV_ABI sceGnmDispatchIndirect(u32* cmdbuf, u32 size, u32 data_offset,
return -1; return -1;
} }
int PS4_SYSV_ABI sceGnmDispatchIndirectOnMec() { s32 PS4_SYSV_ABI sceGnmDispatchIndirectOnMec(u32* cmdbuf, u32 size, VAddr args, u32 modifier) {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); if (cmdbuf != nullptr && size == 8 && args != 0 && ((args & 3u) == 0)) {
return ORBIS_OK; cmdbuf[0] = 0xc0021602 | (modifier & 1u);
*(VAddr*)(&cmdbuf[1]) = args;
cmdbuf[3] = (modifier & 0x18) | 1u;
cmdbuf[4] = 0xc0021000;
cmdbuf[5] = 0;
return ORBIS_OK;
}
return ORBIS_FAIL;
} }
u32 PS4_SYSV_ABI sceGnmDispatchInitDefaultHardwareState(u32* cmdbuf, u32 size) { u32 PS4_SYSV_ABI sceGnmDispatchInitDefaultHardwareState(u32* cmdbuf, u32 size) {
@ -606,17 +402,30 @@ u32 PS4_SYSV_ABI sceGnmDispatchInitDefaultHardwareState(u32* cmdbuf, u32 size) {
return 0; return 0;
} }
cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x216u, cmdbuf = PM4CmdSetData::SetShReg<PM4ShaderType::ShaderCompute>(
0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE0 cmdbuf, 0x216u,
cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x217u, 0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE0
0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE1 cmdbuf = PM4CmdSetData::SetShReg<PM4ShaderType::ShaderCompute>(
cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 0x215u, 0x170u); // COMPUTE_RESOURCE_LIMITS cmdbuf, 0x217u,
0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE1
if (sceKernelIsNeoMode()) {
cmdbuf = PM4CmdSetData::SetShReg<PM4ShaderType::ShaderCompute>(
cmdbuf, 0x219u,
0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE2
cmdbuf = PM4CmdSetData::SetShReg<PM4ShaderType::ShaderCompute>(
cmdbuf, 0x21au,
0xffffffffu); // COMPUTE_STATIC_THREAD_MGMT_SE3
}
cmdbuf = PM4CmdSetData::SetShReg<PM4ShaderType::ShaderCompute>(
cmdbuf, 0x215u, 0x170u); // COMPUTE_RESOURCE_LIMITS
cmdbuf = WriteHeader<PM4ItOpcode::AcquireMem>(cmdbuf, 6); cmdbuf = WriteHeader<PM4ItOpcode::AcquireMem>(cmdbuf, 6);
cmdbuf = WriteBody(cmdbuf, 0x28000000u, 0u, 0u, 0u, 0u, 0u); cmdbuf = WriteBody(cmdbuf, 0x28000000u, 0u, 0u, 0u, 0u, 0xau);
cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, 0xef); cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, sceKernelIsNeoMode() ? 0xe9 : 0xef);
cmdbuf = WriteBody(cmdbuf, 0xau, 0u); cmdbuf = WriteBody(cmdbuf, 0u);
return HwInitPacketSize; return HwInitPacketSize;
} }
@ -633,7 +442,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndex(u32* cmdbuf, u32 size, u32 index_count, uintptr
draw_index->index_base_lo = u32(index_addr); draw_index->index_base_lo = u32(index_addr);
draw_index->index_base_hi = u32(index_addr >> 32); draw_index->index_base_hi = u32(index_addr >> 32);
draw_index->index_count = index_count; draw_index->index_count = index_count;
draw_index->draw_initiator = 0; draw_index->draw_initiator = sceKernelIsNeoMode() ? flags & 0xe0000000u : 0;
WriteTrailingNop<3>(cmdbuf + 6); WriteTrailingNop<3>(cmdbuf + 6);
return ORBIS_OK; return ORBIS_OK;
@ -646,8 +455,9 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexAuto(u32* cmdbuf, u32 size, u32 index_count, u32
if (cmdbuf && (size == 7) && if (cmdbuf && (size == 7) &&
(flags & 0x1ffffffe) == 0) { // no predication will be set in the packet (flags & 0x1ffffffe) == 0) { // no predication will be set in the packet
cmdbuf = WritePacket<PM4ItOpcode::DrawIndexAuto>(cmdbuf, PM4ShaderType::ShaderGraphics, cmdbuf = WritePacket<PM4ItOpcode::DrawIndexAuto>(
index_count, 2u); cmdbuf, PM4ShaderType::ShaderGraphics, index_count,
sceKernelIsNeoMode() ? flags & 0xe0000000u | 2u : 2u);
WriteTrailingNop<3>(cmdbuf); WriteTrailingNop<3>(cmdbuf);
return ORBIS_OK; return ORBIS_OK;
} }
@ -671,7 +481,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirect(u32* cmdbuf, u32 size, u32 data_offset,
cmdbuf[0] = data_offset; cmdbuf[0] = data_offset;
cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset; cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset;
cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset; cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset;
cmdbuf[3] = 0; cmdbuf[3] = sceKernelIsNeoMode() ? flags & 0xe0000000u : 0u;
cmdbuf += 4; cmdbuf += 4;
WriteTrailingNop<3>(cmdbuf); WriteTrailingNop<3>(cmdbuf);
@ -686,8 +496,9 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da
u32 flags) { u32 flags) {
LOG_TRACE(Lib_GnmDriver, "called"); LOG_TRACE(Lib_GnmDriver, "called");
if (cmdbuf && (size == 16) && (shader_stage < ShaderStages::Max) && if ((!sceKernelIsNeoMode() || !UseNeoCompatSequences) && !cmdbuf && (size == 16) &&
(vertex_sgpr_offset < 0x10u) && (instance_sgpr_offset < 0x10u)) { (shader_stage < ShaderStages::Max) && (vertex_sgpr_offset < 0x10u) &&
(instance_sgpr_offset < 0x10u)) {
cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, 2); cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, 2);
cmdbuf = WriteBody(cmdbuf, 0u); cmdbuf = WriteBody(cmdbuf, 0u);
@ -706,7 +517,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexIndirectCountMulti(u32* cmdbuf, u32 size, u32 da
cmdbuf[4] = max_count; cmdbuf[4] = max_count;
*(u64*)(&cmdbuf[5]) = count_addr; *(u64*)(&cmdbuf[5]) = count_addr;
cmdbuf[7] = sizeof(DrawIndexedIndirectArgs); cmdbuf[7] = sizeof(DrawIndexedIndirectArgs);
cmdbuf[8] = 0; cmdbuf[8] = sceKernelIsNeoMode() ? flags & 0xe0000000u : 0;
cmdbuf += 9; cmdbuf += 9;
WriteTrailingNop<2>(cmdbuf); WriteTrailingNop<2>(cmdbuf);
@ -735,7 +546,8 @@ s32 PS4_SYSV_ABI sceGnmDrawIndexOffset(u32* cmdbuf, u32 size, u32 index_offset,
const auto predicate = flags & 1 ? PM4Predicate::PredEnable : PM4Predicate::PredDisable; const auto predicate = flags & 1 ? PM4Predicate::PredEnable : PM4Predicate::PredDisable;
cmdbuf = WriteHeader<PM4ItOpcode::DrawIndexOffset2>( cmdbuf = WriteHeader<PM4ItOpcode::DrawIndexOffset2>(
cmdbuf, 4, PM4ShaderType::ShaderGraphics, predicate); cmdbuf, 4, PM4ShaderType::ShaderGraphics, predicate);
cmdbuf = WriteBody(cmdbuf, index_count, index_offset, index_count, 0u); cmdbuf = WriteBody(cmdbuf, index_count, index_offset, index_count,
sceKernelIsNeoMode() ? flags & 0xe0000000u : 0u);
WriteTrailingNop<3>(cmdbuf); WriteTrailingNop<3>(cmdbuf);
return ORBIS_OK; return ORBIS_OK;
@ -759,7 +571,7 @@ s32 PS4_SYSV_ABI sceGnmDrawIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32
cmdbuf[0] = data_offset; cmdbuf[0] = data_offset;
cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset; cmdbuf[1] = vertex_sgpr_offset == 0 ? 0 : (vertex_sgpr_offset & 0xffffu) + sgpr_offset;
cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset; cmdbuf[2] = instance_sgpr_offset == 0 ? 0 : (instance_sgpr_offset & 0xffffu) + sgpr_offset;
cmdbuf[3] = 2; // auto index cmdbuf[3] = sceKernelIsNeoMode() ? flags & 0xe0000000u | 2u : 2u; // auto index
cmdbuf += 4; cmdbuf += 4;
WriteTrailingNop<3>(cmdbuf); WriteTrailingNop<3>(cmdbuf);
@ -788,6 +600,7 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState(u32* cmdbuf, u32 size) {
} }
const auto& SetupContext = [](u32* cmdbuf, u32 size, bool clear_state) { const auto& SetupContext = [](u32* cmdbuf, u32 size, bool clear_state) {
const auto* cmdbuf_end = cmdbuf + HwInitPacketSize;
if (clear_state) { if (clear_state) {
cmdbuf = ClearContextState(cmdbuf); cmdbuf = ClearContextState(cmdbuf);
} }
@ -795,10 +608,8 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState(u32* cmdbuf, u32 size) {
std::memcpy(cmdbuf, &InitSequence[2], (InitSequence.size() - 2) * 4); std::memcpy(cmdbuf, &InitSequence[2], (InitSequence.size() - 2) * 4);
cmdbuf += InitSequence.size() - 2; cmdbuf += InitSequence.size() - 2;
const auto cmdbuf_left = const auto cmdbuf_left = cmdbuf_end - cmdbuf - 1;
HwInitPacketSize - (InitSequence.size() - 2) - (clear_state ? 0xc : 0) - 1; WriteTrailingNop(cmdbuf, cmdbuf_left);
cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, cmdbuf_left);
cmdbuf = WriteBody(cmdbuf, 0u);
return HwInitPacketSize; return HwInitPacketSize;
}; };
@ -813,12 +624,13 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState175(u32* cmdbuf, u32 size) {
return 0; return 0;
} }
const auto* cmdbuf_end = cmdbuf + HwInitPacketSize;
cmdbuf = ClearContextState(cmdbuf); cmdbuf = ClearContextState(cmdbuf);
std::memcpy(cmdbuf, &InitSequence175[2], (InitSequence175.size() - 2) * 4); std::memcpy(cmdbuf, &InitSequence175[2], (InitSequence175.size() - 2) * 4);
cmdbuf += InitSequence175.size() - 2; cmdbuf += InitSequence175.size() - 2;
constexpr auto cmdbuf_left = HwInitPacketSize - (InitSequence175.size() - 2) - 0xc - 1; const auto cmdbuf_left = cmdbuf_end - cmdbuf - 1;
WriteTrailingNop<cmdbuf_left>(cmdbuf); WriteTrailingNop(cmdbuf, cmdbuf_left);
return HwInitPacketSize; return HwInitPacketSize;
} }
@ -831,17 +643,27 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState200(u32* cmdbuf, u32 size) {
} }
const auto& SetupContext200 = [](u32* cmdbuf, u32 size, bool clear_state) { const auto& SetupContext200 = [](u32* cmdbuf, u32 size, bool clear_state) {
const auto* cmdbuf_end = cmdbuf + HwInitPacketSize;
if (clear_state) { if (clear_state) {
cmdbuf = ClearContextState(cmdbuf); cmdbuf = ClearContextState(cmdbuf);
} }
std::memcpy(cmdbuf, &InitSequence200[2], (InitSequence200.size() - 2) * 4); if (sceKernelIsNeoMode()) {
cmdbuf += InitSequence200.size() - 2; if (!UseNeoCompatSequences) {
std::memcpy(cmdbuf, &InitSequence200Neo[2], (InitSequence200Neo.size() - 2) * 4);
cmdbuf += InitSequence200Neo.size() - 2;
} else {
std::memcpy(cmdbuf, &InitSequence200NeoCompat[2],
(InitSequence200NeoCompat.size() - 2) * 4);
cmdbuf += InitSequence200NeoCompat.size() - 2;
}
} else {
std::memcpy(cmdbuf, &InitSequence200[2], (InitSequence200.size() - 2) * 4);
cmdbuf += InitSequence200.size() - 2;
}
const auto cmdbuf_left = const auto cmdbuf_left = cmdbuf_end - cmdbuf - 1;
HwInitPacketSize - (InitSequence200.size() - 2) - (clear_state ? 0xc : 0) - 1; WriteTrailingNop(cmdbuf, cmdbuf_left);
cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, cmdbuf_left);
cmdbuf = WriteBody(cmdbuf, 0u);
return HwInitPacketSize; return HwInitPacketSize;
}; };
@ -857,17 +679,27 @@ u32 PS4_SYSV_ABI sceGnmDrawInitDefaultHardwareState350(u32* cmdbuf, u32 size) {
} }
const auto& SetupContext350 = [](u32* cmdbuf, u32 size, bool clear_state) { const auto& SetupContext350 = [](u32* cmdbuf, u32 size, bool clear_state) {
const auto* cmdbuf_end = cmdbuf + HwInitPacketSize;
if (clear_state) { if (clear_state) {
cmdbuf = ClearContextState(cmdbuf); cmdbuf = ClearContextState(cmdbuf);
} }
std::memcpy(cmdbuf, &InitSequence350[2], (InitSequence350.size() - 2) * 4); if (sceKernelIsNeoMode()) {
cmdbuf += InitSequence350.size() - 2; if (!UseNeoCompatSequences) {
std::memcpy(cmdbuf, &InitSequence350Neo[2], (InitSequence350Neo.size() - 2) * 4);
cmdbuf += InitSequence350Neo.size() - 2;
} else {
std::memcpy(cmdbuf, &InitSequence350NeoCompat[2],
(InitSequence350NeoCompat.size() - 2) * 4);
cmdbuf += InitSequence350NeoCompat.size() - 2;
}
} else {
std::memcpy(cmdbuf, &InitSequence350[2], (InitSequence350.size() - 2) * 4);
cmdbuf += InitSequence350.size() - 2;
}
const auto cmdbuf_left = const auto cmdbuf_left = cmdbuf_end - cmdbuf - 1;
HwInitPacketSize - (InitSequence350.size() - 2) - (clear_state ? 0xc : 0) - 1; WriteTrailingNop(cmdbuf, cmdbuf_left);
cmdbuf = WriteHeader<PM4ItOpcode::Nop>(cmdbuf, cmdbuf_left);
cmdbuf = WriteBody(cmdbuf, 0u);
return HwInitPacketSize; return HwInitPacketSize;
}; };
@ -883,7 +715,11 @@ u32 PS4_SYSV_ABI sceGnmDrawInitToDefaultContextState(u32* cmdbuf, u32 size) {
return 0; return 0;
} }
std::memcpy(cmdbuf, CtxInitSequence.data(), CtxInitSequence.size() * 4); if (sceKernelIsNeoMode()) {
std::memcpy(cmdbuf, CtxInitSequenceNeo.data(), CtxInitSequenceNeo.size() * 4);
} else {
std::memcpy(cmdbuf, CtxInitSequence.data(), CtxInitSequence.size() * 4);
}
return CtxInitPacketSize; return CtxInitPacketSize;
} }
@ -895,7 +731,16 @@ u32 PS4_SYSV_ABI sceGnmDrawInitToDefaultContextState400(u32* cmdbuf, u32 size) {
return 0; return 0;
} }
std::memcpy(cmdbuf, CtxInitSequence400.data(), CtxInitSequence400.size() * 4); if (sceKernelIsNeoMode()) {
if (!UseNeoCompatSequences) {
std::memcpy(cmdbuf, CtxInitSequence400Neo.data(), CtxInitSequence400Neo.size() * 4);
} else {
std::memcpy(cmdbuf, CtxInitSequence400NeoCompat.data(),
CtxInitSequence400NeoCompat.size() * 4);
}
} else {
std::memcpy(cmdbuf, CtxInitSequence400.data(), CtxInitSequence400.size() * 4);
}
return CtxInitPacketSize; return CtxInitPacketSize;
} }
@ -1000,9 +845,9 @@ int PS4_SYSV_ABI sceGnmGetDebugTimestamp() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceGnmGetEqEventType() { int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev) {
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); LOG_TRACE(Lib_GnmDriver, "called");
return ORBIS_OK; return sceKernelGetEventData(ev);
} }
int PS4_SYSV_ABI sceGnmGetEqTimeStamp() { int PS4_SYSV_ABI sceGnmGetEqTimeStamp() {
@ -1017,7 +862,8 @@ int PS4_SYSV_ABI sceGnmGetGpuBlockStatus() {
u32 PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency() { u32 PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency() {
LOG_TRACE(Lib_GnmDriver, "called"); LOG_TRACE(Lib_GnmDriver, "called");
return Config::isNeoMode() ? 911'000'000 : 800'000'000; // On console this uses an ioctl check, but we assume it is equal to just checking for neo mode.
return sceKernelIsNeoMode() ? 911'000'000 : 800'000'000;
} }
int PS4_SYSV_ABI sceGnmGetGpuInfoStatus() { int PS4_SYSV_ABI sceGnmGetGpuInfoStatus() {
@ -1356,7 +1202,15 @@ s32 PS4_SYSV_ABI sceGnmResetVgtControl(u32* cmdbuf, u32 size) {
if (cmdbuf == nullptr || size != 3) { if (cmdbuf == nullptr || size != 3) {
return -1; return -1;
} }
PM4CmdSetData::SetContextReg(cmdbuf, 0x2aau, 0xffu); // IA_MULTI_VGT_PARAM if (sceKernelIsNeoMode()) {
if (!UseNeoCompatSequences) {
PM4CmdSetData::SetUconfigReg(cmdbuf, 0x40000258u, 0x6d007fu); // IA_MULTI_VGT_PARAM
} else {
PM4CmdSetData::SetContextReg(cmdbuf, 0x100002aau, 0xd00ffu); // IA_MULTI_VGT_PARAM
}
} else {
PM4CmdSetData::SetContextReg(cmdbuf, 0x2aau, 0xffu); // IA_MULTI_VGT_PARAM
}
return ORBIS_OK; return ORBIS_OK;
} }
@ -1817,9 +1671,25 @@ s32 PS4_SYSV_ABI sceGnmSetVgtControl(u32* cmdbuf, u32 size, u32 prim_group_sz_mi
return -1; return -1;
} }
const u32 reg_value = if (sceKernelIsNeoMode()) {
((partial_vs_wave_mode & 1) << 0x10) | (prim_group_sz_minus_one & 0xffffu); const u32 wd_switch_on_eop = u32(wd_switch_only_on_eop_mode != 0) << 0x14;
PM4CmdSetData::SetContextReg(cmdbuf, 0x2aau, reg_value); // IA_MULTI_VGT_PARAM const u32 switch_on_eoi = u32(wd_switch_only_on_eop_mode == 0) << 0x13;
const u32 reg_value =
wd_switch_only_on_eop_mode != 0
? (partial_vs_wave_mode & 1) << 0x10 | prim_group_sz_minus_one | wd_switch_on_eop |
switch_on_eoi | 0x40000u
: prim_group_sz_minus_one & 0x1cffffu | wd_switch_on_eop | switch_on_eoi | 0x50000u;
if (!UseNeoCompatSequences) {
PM4CmdSetData::SetUconfigReg(cmdbuf, 0x40000258u,
reg_value | 0x600000u); // IA_MULTI_VGT_PARAM
} else {
PM4CmdSetData::SetContextReg(cmdbuf, 0x100002aau, reg_value); // IA_MULTI_VGT_PARAM
}
} else {
const u32 reg_value =
((partial_vs_wave_mode & 1) << 0x10) | (prim_group_sz_minus_one & 0xffffu);
PM4CmdSetData::SetContextReg(cmdbuf, 0x2aau, reg_value); // IA_MULTI_VGT_PARAM
}
return ORBIS_OK; return ORBIS_OK;
} }
@ -2202,9 +2072,25 @@ int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload(u32 workload, u32 count,
if (sdk_version <= 0x1ffffffu) { if (sdk_version <= 0x1ffffffu) {
liverpool->SubmitGfx(InitSequence, {}); liverpool->SubmitGfx(InitSequence, {});
} else if (sdk_version <= 0x3ffffffu) { } else if (sdk_version <= 0x3ffffffu) {
liverpool->SubmitGfx(InitSequence200, {}); if (sceKernelIsNeoMode()) {
if (!UseNeoCompatSequences) {
liverpool->SubmitGfx(InitSequence200Neo, {});
} else {
liverpool->SubmitGfx(InitSequence200NeoCompat, {});
}
} else {
liverpool->SubmitGfx(InitSequence200, {});
}
} else { } else {
liverpool->SubmitGfx(InitSequence350, {}); if (sceKernelIsNeoMode()) {
if (!UseNeoCompatSequences) {
liverpool->SubmitGfx(InitSequence350Neo, {});
} else {
liverpool->SubmitGfx(InitSequence350NeoCompat, {});
}
} else {
liverpool->SubmitGfx(InitSequence350, {});
}
} }
send_init_packet = false; send_init_packet = false;
} }

View File

@ -39,7 +39,7 @@ int PS4_SYSV_ABI sceGnmDisableMipStatsReport();
s32 PS4_SYSV_ABI sceGnmDispatchDirect(u32* cmdbuf, u32 size, u32 threads_x, u32 threads_y, s32 PS4_SYSV_ABI sceGnmDispatchDirect(u32* cmdbuf, u32 size, u32 threads_x, u32 threads_y,
u32 threads_z, u32 flags); u32 threads_z, u32 flags);
s32 PS4_SYSV_ABI sceGnmDispatchIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 flags); s32 PS4_SYSV_ABI sceGnmDispatchIndirect(u32* cmdbuf, u32 size, u32 data_offset, u32 flags);
int PS4_SYSV_ABI sceGnmDispatchIndirectOnMec(); s32 PS4_SYSV_ABI sceGnmDispatchIndirectOnMec(u32* cmdbuf, u32 size, VAddr args, u32 modifier);
u32 PS4_SYSV_ABI sceGnmDispatchInitDefaultHardwareState(u32* cmdbuf, u32 size); u32 PS4_SYSV_ABI sceGnmDispatchInitDefaultHardwareState(u32* cmdbuf, u32 size);
s32 PS4_SYSV_ABI sceGnmDrawIndex(u32* cmdbuf, u32 size, u32 index_count, uintptr_t index_addr, s32 PS4_SYSV_ABI sceGnmDrawIndex(u32* cmdbuf, u32 size, u32 index_count, uintptr_t index_addr,
u32 flags, u32 type); u32 flags, u32 type);
@ -85,7 +85,7 @@ int PS4_SYSV_ABI sceGnmGetCoredumpMode();
int PS4_SYSV_ABI sceGnmGetCoredumpProtectionFaultTimestamp(); int PS4_SYSV_ABI sceGnmGetCoredumpProtectionFaultTimestamp();
int PS4_SYSV_ABI sceGnmGetDbgGcHandle(); int PS4_SYSV_ABI sceGnmGetDbgGcHandle();
int PS4_SYSV_ABI sceGnmGetDebugTimestamp(); int PS4_SYSV_ABI sceGnmGetDebugTimestamp();
int PS4_SYSV_ABI sceGnmGetEqEventType(); int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev);
int PS4_SYSV_ABI sceGnmGetEqTimeStamp(); int PS4_SYSV_ABI sceGnmGetEqTimeStamp();
int PS4_SYSV_ABI sceGnmGetGpuBlockStatus(); int PS4_SYSV_ABI sceGnmGetGpuBlockStatus();
u32 PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency(); u32 PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency();

View File

@ -0,0 +1,542 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
namespace Libraries::GnmDriver {
constexpr auto HwInitPacketSize = 0x100u;
// clang-format off
constexpr std::array InitSequence{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1ffu,
0xc0017600u, 0x46u, 0x1ffu,
0xc0017600u, 0x87u, 0x1ffu,
0xc0017600u, 0xc7u, 0x1ffu,
0xc0017600u, 0x107u, 0u,
0xc0017600u, 0x147u, 0x1ffu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6000000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
};
static_assert(InitSequence.size() == 0x73 + 2);
constexpr std::array InitSequence175{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1ffu,
0xc0017600u, 0x46u, 0x1ffu,
0xc0017600u, 0x87u, 0x1ffu,
0xc0017600u, 0xc7u, 0x1ffu,
0xc0017600u, 0x107u, 0u,
0xc0017600u, 0x147u, 0x1ffu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
};
static_assert(InitSequence175.size() == 0x73 + 2);
constexpr std::array InitSequence200{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1701ffu,
0xc0017600u, 0x46u, 0x1701fdu,
0xc0017600u, 0x87u, 0x1701ffu,
0xc0017600u, 0xc7u, 0x1701fdu,
0xc0017600u, 0x107u, 0x17u,
0xc0017600u, 0x147u, 0x1701fdu,
0xc0017600u, 0x47u, 0x1cu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
};
static_assert(InitSequence200.size() == 0x76 + 2);
constexpr std::array InitSequence200Neo{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x219u, 0xffffffffu,
0xc0017600u, 0x21au, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1701ffu,
0xc0017600u, 0x46u, 0x1701fdu,
0xc0017600u, 0x87u, 0x1701ffu,
0xc0017600u, 0xc7u, 0x1701fdu,
0xc0017600u, 0x107u, 0x17u,
0xc0017600u, 0x147u, 0x1701fdu,
0xc0017600u, 0x47u, 0x1cu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
0xc0017900u, 0x40000258u, 0x6d007fu,
};
static_assert(InitSequence200Neo.size() == 0x83 + 2);
constexpr std::array InitSequence200NeoCompat{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x219u, 0xffffffffu,
0xc0017600u, 0x21au, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1701ffu,
0xc0017600u, 0x46u, 0x1701fdu,
0xc0017600u, 0x87u, 0x1701ffu,
0xc0017600u, 0xc7u, 0x1701fdu,
0xc0017600u, 0x107u, 0x17u,
0xc0017600u, 0x147u, 0x1701fdu,
0xc0017600u, 0x47u, 0x1cu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
0xc0016900u, 0x100002aau, 0xd00ffu,
};
static_assert(InitSequence200NeoCompat.size() == 0x83 + 2);
constexpr std::array InitSequence350{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1701ffu,
0xc0017600u, 0x46u, 0x1701fdu,
0xc0017600u, 0x87u, 0x1701ffu,
0xc0017600u, 0xc7u, 0x1701fdu,
0xc0017600u, 0x107u, 0x17u,
0xc0017600u, 0x147u, 0x1701fdu,
0xc0017600u, 0x47u, 0x1cu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
0xc0016900u, 0x2aau, 0xffu,
};
static_assert(InitSequence350.size() == 0x7c + 2);
constexpr std::array InitSequence350Neo{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x219u, 0xffffffffu,
0xc0017600u, 0x21au, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1701ffu,
0xc0017600u, 0x46u, 0x1701fdu,
0xc0017600u, 0x87u, 0x1701ffu,
0xc0017600u, 0xc7u, 0x1701fdu,
0xc0017600u, 0x107u, 0x17u,
0xc0017600u, 0x147u, 0x1701fdu,
0xc0017600u, 0x47u, 0x1cu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
0xc0017900u, 0x40000258u, 0x6d007fu,
};
static_assert(InitSequence350Neo.size() == 0x86 + 2);
constexpr std::array InitSequence350NeoCompat{
// A fake preamble to mimic context reset sent by FW
0xc0001200u, 0u, // IT_CLEAR_STATE
// Actual init state sequence
0xc0017600u, 0x216u, 0xffffffffu,
0xc0017600u, 0x217u, 0xffffffffu,
0xc0017600u, 0x219u, 0xffffffffu,
0xc0017600u, 0x21au, 0xffffffffu,
0xc0017600u, 0x215u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0017600u, 7u, 0x1701ffu,
0xc0017600u, 0x46u, 0x1701fdu,
0xc0017600u, 0x87u, 0x1701ffu,
0xc0017600u, 0xc7u, 0x1701fdu,
0xc0017600u, 0x107u, 0x17u,
0xc0017600u, 0x147u, 0x1701fdu,
0xc0017600u, 0x47u, 0x1cu,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x200u, 0xe0000000u,
0xc0016900u, 0x100002aau, 0xd00ffu,
};
static_assert(InitSequence350NeoCompat.size() == 0x86 + 2);
constexpr std::array CtxInitSequence{
0xc0012800u, 0x80000000u, 0x80000000u,
0xc0001200u, 0u,
0xc0002f00u, 1u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0111000u, 0u
};
static_assert(CtxInitSequence.size() == 0x0f);
constexpr std::array CtxInitSequenceNeo{
0xc0012800u, 0x80000000u, 0x80000000u,
0xc0001200u, 0u,
0xc0002f00u, 1u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u,
0xc00d1000, 0u
};
static_assert(CtxInitSequenceNeo.size() == 0x13);
constexpr std::array CtxInitSequence400{
0xc0012800u, 0x80000000u, 0x80000000u,
0xc0001200u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0016900u, 0x2aau, 0xffu,
0xc09e1000u,
};
static_assert(CtxInitSequence400.size() == 0x61);
constexpr std::array CtxInitSequence400Neo{
0xc0012800u, 0x80000000u, 0x80000000u,
0xc0001200u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0017900u, 0x40000258u, 0x6d007fu,
0xc09a1000u,
};
static_assert(CtxInitSequence400Neo.size() == 0x65);
constexpr std::array CtxInitSequence400NeoCompat{
0xc0012800u, 0x80000000u, 0x80000000u,
0xc0001200u, 0u,
0xc0016900u, 0x2f9u, 0x2du,
0xc0016900u, 0x282u, 8u,
0xc0016900u, 0x280u, 0x80008u,
0xc0016900u, 0x281u, 0xffff0000u,
0xc0016900u, 0x204u, 0u,
0xc0016900u, 0x206u, 0x43fu,
0xc0016900u, 0x83u, 0xffffu,
0xc0016900u, 0x317u, 0x10u,
0xc0016900u, 0x2fau, 0x3f800000u,
0xc0016900u, 0x2fcu, 0x3f800000u,
0xc0016900u, 0x2fbu, 0x3f800000u,
0xc0016900u, 0x2fdu, 0x3f800000u,
0xc0016900u, 0x202u, 0xcc0010u,
0xc0016900u, 0x30eu, 0xffffffffu,
0xc0016900u, 0x30fu, 0xffffffffu,
0xc0002f00u, 1u,
0xc0016900u, 0x1b1u, 2u,
0xc0016900u, 0x101u, 0u,
0xc0016900u, 0x100u, 0xffffffffu,
0xc0016900u, 0x103u, 0u,
0xc0016900u, 0x284u, 0u,
0xc0016900u, 0x290u, 0u,
0xc0016900u, 0x2aeu, 0u,
0xc0016900u, 0x102u, 0u,
0xc0016900u, 0x292u, 0u,
0xc0016900u, 0x293u, 0x6020000u,
0xc0016900u, 0x2f8u, 0u,
0xc0016900u, 0x2deu, 0x1e9u,
0xc0026900u, 0xebu, 0xff00ff00u, 0xff00u,
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
0xc0016900u, 0x100002aau, 0xd00ffu,
0xc09a1000u,
};
static_assert(CtxInitSequence400Neo.size() == 0x65);
// clang-format on
} // namespace Libraries::GnmDriver

View File

@ -12,6 +12,8 @@
namespace Libraries::Kernel { namespace Libraries::Kernel {
// Events are uniquely identified by id and filter.
bool EqueueInternal::AddEvent(EqueueEvent& event) { bool EqueueInternal::AddEvent(EqueueEvent& event) {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
@ -27,12 +29,13 @@ bool EqueueInternal::AddEvent(EqueueEvent& event) {
return true; return true;
} }
bool EqueueInternal::RemoveEvent(u64 id) { bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
bool has_found = false; bool has_found = false;
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
const auto& it = const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) {
std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; }); return ev.event.ident == id && ev.event.filter == filter;
});
if (it != m_events.cend()) { if (it != m_events.cend()) {
m_events.erase(it); m_events.erase(it);
has_found = true; has_found = true;
@ -68,7 +71,7 @@ int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) {
if (ev->flags & SceKernelEvent::Flags::OneShot) { if (ev->flags & SceKernelEvent::Flags::OneShot) {
for (auto ev_id = 0u; ev_id < count; ++ev_id) { for (auto ev_id = 0u; ev_id < count; ++ev_id) {
RemoveEvent(ev->ident); RemoveEvent(ev->ident, ev->filter);
} }
} }
@ -94,8 +97,11 @@ int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) {
int count = 0; int count = 0;
for (auto& event : m_events) { for (auto& event : m_events) {
if (event.IsTriggered()) { if (event.IsTriggered()) {
// Event should not trigger again
event.ResetTriggerState();
if (event.event.flags & SceKernelEvent::Flags::Clear) { if (event.event.flags & SceKernelEvent::Flags::Clear) {
event.Reset(); event.Clear();
} }
ev[count++] = event.event; ev[count++] = event.event;
if (count == num) { if (count == num) {
@ -277,6 +283,19 @@ s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec*
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceKernelDeleteHRTimerEvent(SceKernelEqueue eq, int id) {
if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
if (eq->HasSmallTimer()) {
return eq->RemoveSmallTimer(id) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOENT;
} else {
return eq->RemoveEvent(id, SceKernelEvent::Filter::HrTimer) ? ORBIS_OK
: ORBIS_KERNEL_ERROR_ENOENT;
}
}
int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) { int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) {
if (eq == nullptr) { if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
@ -334,16 +353,20 @@ int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) {
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
} }
if (!eq->RemoveEvent(id)) { if (!eq->RemoveEvent(id, SceKernelEvent::Filter::User)) {
return ORBIS_KERNEL_ERROR_ENOENT; return ORBIS_KERNEL_ERROR_ENOENT;
} }
return ORBIS_OK; return ORBIS_OK;
} }
s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) { int PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) {
return ev->filter; return ev->filter;
} }
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev) {
return ev->data;
}
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) { void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue); LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue);
LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue); LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue);
@ -352,10 +375,12 @@ void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent); LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent);
LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge); LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge);
LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent); LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent);
LIB_FUNCTION("J+LF6LwObXU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteHRTimerEvent);
LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent); LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent);
LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent);
LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId);
LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter); LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter);
LIB_FUNCTION("kwGyyjohI50", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventData);
} }
} // namespace Libraries::Kernel } // namespace Libraries::Kernel

View File

@ -66,8 +66,11 @@ struct EqueueEvent {
std::chrono::steady_clock::time_point time_added; std::chrono::steady_clock::time_point time_added;
std::unique_ptr<boost::asio::steady_timer> timer; std::unique_ptr<boost::asio::steady_timer> timer;
void Reset() { void ResetTriggerState() {
is_triggered = false; is_triggered = false;
}
void Clear() {
event.fflags = 0; event.fflags = 0;
event.data = 0; event.data = 0;
} }
@ -83,7 +86,7 @@ struct EqueueEvent {
} }
bool operator==(const EqueueEvent& ev) const { bool operator==(const EqueueEvent& ev) const {
return ev.event.ident == event.ident; return ev.event.ident == event.ident && ev.event.filter == event.filter;
} }
private: private:
@ -99,7 +102,7 @@ public:
} }
bool AddEvent(EqueueEvent& event); bool AddEvent(EqueueEvent& event);
bool RemoveEvent(u64 id); bool RemoveEvent(u64 id, s16 filter);
int WaitForEvents(SceKernelEvent* ev, int num, u32 micros); int WaitForEvents(SceKernelEvent* ev, int num, u32 micros);
bool TriggerEvent(u64 ident, s16 filter, void* trigger_data); bool TriggerEvent(u64 ident, s16 filter, void* trigger_data);
int GetTriggeredEvents(SceKernelEvent* ev, int num); int GetTriggeredEvents(SceKernelEvent* ev, int num);
@ -108,6 +111,13 @@ public:
bool HasSmallTimer() const { bool HasSmallTimer() const {
return small_timer_event.event.data != 0; return small_timer_event.event.data != 0;
} }
bool RemoveSmallTimer(u64 id) {
if (HasSmallTimer() && small_timer_event.event.ident == id) {
small_timer_event = {};
return true;
}
return false;
}
int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros); int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros);
@ -122,6 +132,8 @@ private:
using SceKernelUseconds = u32; using SceKernelUseconds = u32;
using SceKernelEqueue = EqueueInternal*; using SceKernelEqueue = EqueueInternal*;
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev);
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym); void RegisterEventQueue(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Kernel } // namespace Libraries::Kernel

View File

@ -46,17 +46,6 @@ static std::map<std::string, FactoryDevice> available_device = {
namespace Libraries::Kernel { namespace Libraries::Kernel {
auto GetDirectoryEntries(const std::filesystem::path& path) {
std::vector<Core::FileSys::DirEntry> files;
for (const auto& entry : std::filesystem::directory_iterator(path)) {
auto& dir_entry = files.emplace_back();
dir_entry.name = entry.path().filename().string();
dir_entry.isFile = !std::filesystem::is_directory(entry.path().string());
}
return files;
}
int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode); LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode);
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance(); auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
@ -115,7 +104,12 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
if (create) { if (create) {
return handle; // dir already exists return handle; // dir already exists
} else { } else {
file->dirents = GetDirectoryEntries(file->m_host_name); mnt->IterateDirectory(file->m_guest_name,
[&file](const auto& ent_path, const auto ent_is_file) {
auto& dir_entry = file->dirents.emplace_back();
dir_entry.name = ent_path.filename().string();
dir_entry.isFile = ent_is_file;
});
file->dirents_index = 0; file->dirents_index = 0;
} }
} }
@ -695,66 +689,12 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) {
return sizeof(OrbisKernelDirent); return sizeof(OrbisKernelDirent);
} }
static int HandleSeparateUpdateDents(int fd, char* buf, int nbytes, s64* basep) {
int dir_entries = 0;
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
auto* file = h->GetFile(fd);
auto update_dir_name = std::string{fmt::UTF(file->m_host_name.u8string()).data};
auto mount = mnt->GetMountFromHostPath(update_dir_name);
auto suffix = std::string{fmt::UTF(mount->host_path.u8string()).data};
size_t pos = update_dir_name.find("-UPDATE");
if (pos != std::string::npos) {
update_dir_name.erase(pos, 7);
auto guest_name = mount->mount + "/" + update_dir_name.substr(suffix.size() + 1);
int descriptor;
auto existent_folder = h->GetFile(update_dir_name);
if (!existent_folder) {
u32 handle = h->CreateHandle();
auto* new_file = h->GetFile(handle);
new_file->type = Core::FileSys::FileType::Directory;
new_file->m_guest_name = guest_name;
new_file->m_host_name = update_dir_name;
if (!std::filesystem::is_directory(new_file->m_host_name)) {
h->DeleteHandle(handle);
return dir_entries;
} else {
new_file->dirents = GetDirectoryEntries(new_file->m_host_name);
new_file->dirents_index = 0;
}
new_file->is_opened = true;
descriptor = h->GetFileDescriptor(new_file);
} else {
descriptor = h->GetFileDescriptor(existent_folder);
}
dir_entries = GetDents(descriptor, buf, nbytes, basep);
if (dir_entries == ORBIS_OK && existent_folder) {
existent_folder->dirents_index = 0;
file->dirents_index = 0;
}
}
return dir_entries;
}
int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) { int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) {
int a = GetDents(fd, buf, nbytes, nullptr); return GetDents(fd, buf, nbytes, nullptr);
if (a == ORBIS_OK) {
return HandleSeparateUpdateDents(fd, buf, nbytes, nullptr);
}
return a;
} }
int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) { int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) {
int a = GetDents(fd, buf, nbytes, basep); return GetDents(fd, buf, nbytes, basep);
if (a == ORBIS_OK) {
return HandleSeparateUpdateDents(fd, buf, nbytes, basep);
}
return a;
} }
s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) { s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) {

View File

@ -505,6 +505,41 @@ int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) {
return result; return result;
} }
static constexpr int MAX_PRT_APERTURES = 3;
static constexpr VAddr PRT_AREA_START_ADDR = 0x1000000000;
static constexpr size_t PRT_AREA_SIZE = 0xec00000000;
static std::array<std::pair<VAddr, size_t>, MAX_PRT_APERTURES> PrtApertures{};
int PS4_SYSV_ABI sceKernelSetPrtAperture(int id, VAddr address, size_t size) {
if (id < 0 || id >= MAX_PRT_APERTURES) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (address < PRT_AREA_START_ADDR || address + size > PRT_AREA_START_ADDR + PRT_AREA_SIZE) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (address % 4096 != 0) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
LOG_WARNING(Kernel_Vmm,
"PRT aperture id = {}, address = {:#x}, size = {:#x} is set but not used", id,
address, size);
PrtApertures[id] = {address, size};
return ORBIS_OK;
}
int PS4_SYSV_ABI sceKernelGetPrtAperture(int id, VAddr* address, size_t* size) {
if (id < 0 || id >= MAX_PRT_APERTURES) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
std::tie(*address, *size) = PrtApertures[id];
return ORBIS_OK;
}
void RegisterMemory(Core::Loader::SymbolsResolver* sym) { void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory); LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory);
LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1, LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1,
@ -551,6 +586,10 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap); LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap);
LIB_FUNCTION("UqDGjXA5yUM", "libkernel", 1, "libkernel", 1, 1, posix_munmap); LIB_FUNCTION("UqDGjXA5yUM", "libkernel", 1, "libkernel", 1, 1, posix_munmap);
LIB_FUNCTION("UqDGjXA5yUM", "libScePosix", 1, "libkernel", 1, 1, posix_munmap); LIB_FUNCTION("UqDGjXA5yUM", "libScePosix", 1, "libkernel", 1, 1, posix_munmap);
// PRT memory management
LIB_FUNCTION("BohYr-F7-is", "libkernel", 1, "libkernel", 1, 1, sceKernelSetPrtAperture);
LIB_FUNCTION("L0v2Go5jOuM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetPrtAperture);
} }
} // namespace Libraries::Kernel } // namespace Libraries::Kernel

View File

@ -6,9 +6,11 @@
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/types.h" #include "common/types.h"
constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5056_MB; // ~ 5GB constexpr u64 SCE_KERNEL_TOTAL_MEM = 5248_MB;
// TODO: Confirm this value on hardware. constexpr u64 SCE_KERNEL_TOTAL_MEM_PRO = 5888_MB;
constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE_PRO = 5568_MB; // ~ 5.5GB
constexpr u64 SCE_FLEXIBLE_MEMORY_BASE = 64_MB;
constexpr u64 SCE_FLEXIBLE_MEMORY_SIZE = 512_MB;
namespace Core::Loader { namespace Core::Loader {
class SymbolsResolver; class SymbolsResolver;
@ -129,10 +131,6 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags);
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len); int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len);
void* Malloc(size_t size);
void Free(void* ptr);
void RegisterMemory(Core::Loader::SymbolsResolver* sym); void RegisterMemory(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Kernel } // namespace Libraries::Kernel

View File

@ -14,7 +14,8 @@ namespace Libraries::Kernel {
int PS4_SYSV_ABI sceKernelIsNeoMode() { int PS4_SYSV_ABI sceKernelIsNeoMode() {
LOG_DEBUG(Kernel_Sce, "called"); LOG_DEBUG(Kernel_Sce, "called");
return Config::isNeoMode(); return Config::isNeoModeConsole() &&
Common::ElfInfo::Instance().GetPSFAttributes().support_neo_mode;
} }
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {

View File

@ -55,6 +55,9 @@ public:
stop.request_stop(); stop.request_stop();
Join(); Join();
} }
thread = nullptr;
func = nullptr;
stop = std::stop_source{};
} }
static void* PS4_SYSV_ABI RunWrapper(void* arg) { static void* PS4_SYSV_ABI RunWrapper(void* arg) {

View File

@ -244,10 +244,9 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
new_thread->tid = ++TidCounter; new_thread->tid = ++TidCounter;
if (new_thread->attr.stackaddr_attr == 0) { if (new_thread->attr.stackaddr_attr == 0) {
/* Enforce minimum stack size of 64 KB */ /* Add additional stack space for HLE */
static constexpr size_t MinimumStack = 64_KB; static constexpr size_t AdditionalStack = 128_KB;
auto& stacksize = new_thread->attr.stacksize_attr; new_thread->attr.stacksize_attr += AdditionalStack;
stacksize = std::max(stacksize, MinimumStack);
} }
if (thread_state->CreateStack(&new_thread->attr) != 0) { if (thread_state->CreateStack(&new_thread->attr) != 0) {

View File

@ -18,6 +18,8 @@
#include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libc_internal/libc_internal.h"
#include "core/libraries/libpng/pngdec.h" #include "core/libraries/libpng/pngdec.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/libraries/mouse/mouse.h"
#include "core/libraries/move/move.h"
#include "core/libraries/network/http.h" #include "core/libraries/network/http.h"
#include "core/libraries/network/net.h" #include "core/libraries/network/net.h"
#include "core/libraries/network/netctl.h" #include "core/libraries/network/netctl.h"
@ -46,6 +48,8 @@
#include "core/libraries/videodec/videodec.h" #include "core/libraries/videodec/videodec.h"
#include "core/libraries/videodec/videodec2.h" #include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videoout/video_out.h" #include "core/libraries/videoout/video_out.h"
#include "fiber/fiber.h"
#include "jpeg/jpegenc.h"
namespace Libraries { namespace Libraries {
@ -91,6 +95,10 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::Remoteplay::RegisterlibSceRemoteplay(sym); Libraries::Remoteplay::RegisterlibSceRemoteplay(sym);
Libraries::Videodec::RegisterlibSceVideodec(sym); Libraries::Videodec::RegisterlibSceVideodec(sym);
Libraries::RazorCpu::RegisterlibSceRazorCpu(sym); Libraries::RazorCpu::RegisterlibSceRazorCpu(sym);
Libraries::Move::RegisterlibSceMove(sym);
Libraries::Fiber::RegisterlibSceFiber(sym);
Libraries::JpegEnc::RegisterlibSceJpegEnc(sym);
Libraries::Mouse::RegisterlibSceMouse(sym);
} }
} // namespace Libraries } // namespace Libraries

View File

@ -0,0 +1,99 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Generated By moduleGenerator
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "mouse.h"
namespace Libraries::Mouse {
int PS4_SYSV_ABI sceMouseClose() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseConnectPort() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseDebugGetDeviceId() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseDeviceOpen() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseDisconnectDevice() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseDisconnectPort() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseGetDeviceInfo() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseInit() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseMbusInit() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseOpen() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseRead() {
LOG_DEBUG(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseSetHandType() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseSetPointerSpeed() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMouseSetProcessPrivilege() {
LOG_ERROR(Lib_Mouse, "(STUBBED) called");
return ORBIS_OK;
}
void RegisterlibSceMouse(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("cAnT0Rw-IwU", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseClose);
LIB_FUNCTION("Ymyy1HSSJLQ", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseConnectPort);
LIB_FUNCTION("BRXOoXQtb+k", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseDebugGetDeviceId);
LIB_FUNCTION("WiGKINCZWkc", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseDeviceOpen);
LIB_FUNCTION("eDQTFHbgeTU", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseDisconnectDevice);
LIB_FUNCTION("jJP1vYMEPd4", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseDisconnectPort);
LIB_FUNCTION("QA9Qupz3Zjw", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseGetDeviceInfo);
LIB_FUNCTION("Qs0wWulgl7U", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseInit);
LIB_FUNCTION("1FeceR5YhAo", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseMbusInit);
LIB_FUNCTION("RaqxZIf6DvE", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseOpen);
LIB_FUNCTION("x8qnXqh-tiM", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseRead);
LIB_FUNCTION("crkFfp-cmFo", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseSetHandType);
LIB_FUNCTION("ghLUU2Z5Lcg", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseSetPointerSpeed);
LIB_FUNCTION("6aANndpS0Wo", "libSceMouse", 1, "libSceMouse", 1, 1, sceMouseSetProcessPrivilege);
};
} // namespace Libraries::Mouse

View File

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Mouse {
int PS4_SYSV_ABI sceMouseClose();
int PS4_SYSV_ABI sceMouseConnectPort();
int PS4_SYSV_ABI sceMouseDebugGetDeviceId();
int PS4_SYSV_ABI sceMouseDeviceOpen();
int PS4_SYSV_ABI sceMouseDisconnectDevice();
int PS4_SYSV_ABI sceMouseDisconnectPort();
int PS4_SYSV_ABI sceMouseGetDeviceInfo();
int PS4_SYSV_ABI sceMouseInit();
int PS4_SYSV_ABI sceMouseMbusInit();
int PS4_SYSV_ABI sceMouseOpen();
int PS4_SYSV_ABI sceMouseRead();
int PS4_SYSV_ABI sceMouseSetHandType();
int PS4_SYSV_ABI sceMouseSetPointerSpeed();
int PS4_SYSV_ABI sceMouseSetProcessPrivilege();
void RegisterlibSceMouse(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Mouse

View File

@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "move.h"
namespace Libraries::Move {
int PS4_SYSV_ABI sceMoveOpen() {
LOG_ERROR(Lib_Move, "(STUBBED) called");
return ORBIS_FAIL;
}
int PS4_SYSV_ABI sceMoveGetDeviceInfo() {
LOG_ERROR(Lib_Move, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMoveReadStateRecent() {
LOG_TRACE(Lib_Move, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMoveTerm() {
LOG_ERROR(Lib_Move, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMoveInit() {
LOG_ERROR(Lib_Move, "(STUBBED) called");
return ORBIS_OK;
}
void RegisterlibSceMove(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("HzC60MfjJxU", "libSceMove", 1, "libSceMove", 1, 1, sceMoveOpen);
LIB_FUNCTION("GWXTyxs4QbE", "libSceMove", 1, "libSceMove", 1, 1, sceMoveGetDeviceInfo);
LIB_FUNCTION("f2bcpK6kJfg", "libSceMove", 1, "libSceMove", 1, 1, sceMoveReadStateRecent);
LIB_FUNCTION("tsZi60H4ypY", "libSceMove", 1, "libSceMove", 1, 1, sceMoveTerm);
LIB_FUNCTION("j1ITE-EoJmE", "libSceMove", 1, "libSceMove", 1, 1, sceMoveInit);
};
} // namespace Libraries::Move

View File

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Move {
int PS4_SYSV_ABI sceMoveOpen();
int PS4_SYSV_ABI sceMoveGetDeviceInfo();
int PS4_SYSV_ABI sceMoveReadStateRecent();
int PS4_SYSV_ABI sceMoveTerm();
int PS4_SYSV_ABI sceMoveInit();
void RegisterlibSceMove(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Move

View File

@ -972,11 +972,8 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatusA() {
} }
int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) { int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) {
LOG_INFO(Lib_NpManager, "user_id {}", user_id); LOG_DEBUG(Lib_NpManager, "user_id {}", user_id);
const auto name = Config::getUserName(); return ORBIS_NP_ERROR_SIGNED_OUT;
std::memset(np_id, 0, sizeof(OrbisNpId));
name.copy(np_id->handle.data, sizeof(np_id->handle.data));
return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNpGetNpReachabilityState() { int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
@ -986,10 +983,7 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
int PS4_SYSV_ABI sceNpGetOnlineId(s32 user_id, OrbisNpOnlineId* online_id) { int PS4_SYSV_ABI sceNpGetOnlineId(s32 user_id, OrbisNpOnlineId* online_id) {
LOG_DEBUG(Lib_NpManager, "user_id {}", user_id); LOG_DEBUG(Lib_NpManager, "user_id {}", user_id);
const auto name = Config::getUserName(); return ORBIS_NP_ERROR_SIGNED_OUT;
std::memset(online_id, 0, sizeof(OrbisNpOnlineId));
name.copy(online_id->data, sizeof(online_id->data));
return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNpGetParentalControlInfo() { int PS4_SYSV_ABI sceNpGetParentalControlInfo() {

View File

@ -498,7 +498,7 @@ int PS4_SYSV_ABI sceNpTrophyGetTrophyInfo(OrbisNpTrophyContext context, OrbisNpT
s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context, s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context,
OrbisNpTrophyHandle handle, OrbisNpTrophyHandle handle,
OrbisNpTrophyFlagArray* flags, u32* count) { OrbisNpTrophyFlagArray* flags, u32* count) {
LOG_INFO(Lib_NpTrophy, "GetTrophyUnlockState called"); LOG_INFO(Lib_NpTrophy, "called");
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT) if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT; return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
@ -519,8 +519,9 @@ s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context,
pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str()); pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str());
if (!result) { if (!result) {
LOG_ERROR(Lib_NpTrophy, "Failed to open trophy xml : {}", result.description()); LOG_ERROR(Lib_NpTrophy, "Failed to open trophy XML: {}", result.description());
return -1; *count = 0;
return ORBIS_OK;
} }
int num_trophies = 0; int num_trophies = 0;

View File

@ -286,6 +286,7 @@ int PS4_SYSV_ABI scePadOutputReport() {
} }
int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
LOG_TRACE(Lib_Pad, "called");
int connected_count = 0; int connected_count = 0;
bool connected = false; bool connected = false;
Input::State states[64]; Input::State states[64];
@ -304,16 +305,15 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
pData[i].rightStick.y = states[i].axes[static_cast<int>(Input::Axis::RightY)]; pData[i].rightStick.y = states[i].axes[static_cast<int>(Input::Axis::RightY)];
pData[i].analogButtons.l2 = states[i].axes[static_cast<int>(Input::Axis::TriggerLeft)]; pData[i].analogButtons.l2 = states[i].axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData[i].analogButtons.r2 = states[i].axes[static_cast<int>(Input::Axis::TriggerRight)]; pData[i].analogButtons.r2 = states[i].axes[static_cast<int>(Input::Axis::TriggerRight)];
pData[i].orientation.x = 0.0f; pData[i].acceleration.x = states[i].acceleration.x;
pData[i].orientation.y = 0.0f; pData[i].acceleration.y = states[i].acceleration.y;
pData[i].orientation.z = 0.0f; pData[i].acceleration.z = states[i].acceleration.z;
pData[i].orientation.w = 1.0f; pData[i].angularVelocity.x = states[i].angularVelocity.x;
pData[i].acceleration.x = 0.0f; pData[i].angularVelocity.y = states[i].angularVelocity.y;
pData[i].acceleration.y = 0.0f; pData[i].angularVelocity.z = states[i].angularVelocity.z;
pData[i].acceleration.z = 0.0f; Input::GameController::CalculateOrientation(pData[i].acceleration, pData[i].angularVelocity,
pData[i].angularVelocity.x = 0.0f; 1.0f / controller->accel_poll_rate,
pData[i].angularVelocity.y = 0.0f; pData[i].orientation);
pData[i].angularVelocity.z = 0.0f;
pData[i].touchData.touchNum = pData[i].touchData.touchNum =
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); (states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0);
pData[i].touchData.touch[0].x = states[i].touchpad[0].x; pData[i].touchData.touch[0].x = states[i].touchpad[0].x;
@ -352,6 +352,7 @@ int PS4_SYSV_ABI scePadReadHistory() {
} }
int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
LOG_TRACE(Lib_Pad, "called");
if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) { if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) {
return ORBIS_PAD_ERROR_INVALID_HANDLE; return ORBIS_PAD_ERROR_INVALID_HANDLE;
} }
@ -367,16 +368,15 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)]; pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)];
pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)]; pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData->analogButtons.r2 = state.axes[static_cast<int>(Input::Axis::TriggerRight)]; pData->analogButtons.r2 = state.axes[static_cast<int>(Input::Axis::TriggerRight)];
pData->orientation.x = 0; pData->acceleration.x = state.acceleration.x;
pData->orientation.y = 0; pData->acceleration.y = state.acceleration.y;
pData->orientation.z = 0; pData->acceleration.z = state.acceleration.z;
pData->orientation.w = 1; pData->angularVelocity.x = state.angularVelocity.x;
pData->acceleration.x = 0.0f; pData->angularVelocity.y = state.angularVelocity.y;
pData->acceleration.y = 0.0f; pData->angularVelocity.z = state.angularVelocity.z;
pData->acceleration.z = 0.0f; Input::GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
pData->angularVelocity.x = 0.0f; 1.0f / controller->accel_poll_rate,
pData->angularVelocity.y = 0.0f; pData->orientation);
pData->angularVelocity.z = 0.0f;
pData->touchData.touchNum = pData->touchData.touchNum =
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0); (state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
pData->touchData.touch[0].x = state.touchpad[0].x; pData->touchData.touch[0].x = state.touchpad[0].x;
@ -498,6 +498,8 @@ int PS4_SYSV_ABI scePadSetLoginUserNumber() {
int PS4_SYSV_ABI scePadSetMotionSensorState(s32 handle, bool bEnable) { int PS4_SYSV_ABI scePadSetMotionSensorState(s32 handle, bool bEnable) {
LOG_ERROR(Lib_Pad, "(STUBBED) called"); LOG_ERROR(Lib_Pad, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
// it's already handled by the SDL backend and will be on no matter what
// (assuming the controller supports it)
} }
int PS4_SYSV_ABI scePadSetProcessFocus() { int PS4_SYSV_ABI scePadSetProcessFocus() {

View File

@ -157,7 +157,7 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh
} }
for (int i = 0; i < numberOfEntries; i++) { for (int i = 0; i < numberOfEntries; i++) {
if (chunkIds[i] <= playgo->chunks.size()) { if (chunkIds[i] < playgo->chunks.size()) {
outLoci[i] = OrbisPlayGoLocus::LocalFast; outLoci[i] = OrbisPlayGoLocus::LocalFast;
} else { } else {
outLoci[i] = OrbisPlayGoLocus::NotDownloaded; outLoci[i] = OrbisPlayGoLocus::NotDownloaded;

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <span> #include <span>
#include <thread>
#include <vector> #include <vector>
#include <core/libraries/system/msgdialog_ui.h> #include <core/libraries/system/msgdialog_ui.h>
@ -1139,10 +1140,6 @@ Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getPar
LOG_INFO(Lib_SaveData, "called without save memory initialized"); LOG_INFO(Lib_SaveData, "called without save memory initialized");
return Error::MEMORY_NOT_READY; return Error::MEMORY_NOT_READY;
} }
if (SaveMemory::IsSaving()) {
LOG_TRACE(Lib_SaveData, "called while saving");
return Error::BUSY_FOR_SAVING;
}
LOG_DEBUG(Lib_SaveData, "called"); LOG_DEBUG(Lib_SaveData, "called");
auto data = getParam->data; auto data = getParam->data;
if (data != nullptr) { if (data != nullptr) {
@ -1502,8 +1499,14 @@ Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2*
return Error::MEMORY_NOT_READY; return Error::MEMORY_NOT_READY;
} }
if (SaveMemory::IsSaving()) { if (SaveMemory::IsSaving()) {
LOG_TRACE(Lib_SaveData, "called while saving"); int count = 0;
return Error::BUSY_FOR_SAVING; while (++count < 100 && SaveMemory::IsSaving()) { // try for more 10 seconds
std::this_thread::sleep_for(chrono::milliseconds(100));
}
if (SaveMemory::IsSaving()) {
LOG_TRACE(Lib_SaveData, "called while saving");
return Error::BUSY_FOR_SAVING;
}
} }
LOG_DEBUG(Lib_SaveData, "called"); LOG_DEBUG(Lib_SaveData, "called");
auto data = setParam->data; auto data = setParam->data;
@ -1584,8 +1587,8 @@ Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetu
} else { } else {
SaveMemory::SetIcon(nullptr, 0); SaveMemory::SetIcon(nullptr, 0);
} }
SaveMemory::TriggerSaveWithoutEvent();
} }
SaveMemory::TriggerSaveWithoutEvent();
if (g_fw_ver >= ElfInfo::FW_45 && result != nullptr) { if (g_fw_ver >= ElfInfo::FW_45 && result != nullptr) {
result->existedMemorySize = existed_size; result->existedMemorySize = existed_size;
} }

View File

@ -10,327 +10,327 @@
namespace Libraries::Usbd { namespace Libraries::Usbd {
int PS4_SYSV_ABI sceUsbdAllocTransfer() { int PS4_SYSV_ABI sceUsbdAllocTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdAttachKernelDriver() { int PS4_SYSV_ABI sceUsbdAttachKernelDriver() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdBulkTransfer() { int PS4_SYSV_ABI sceUsbdBulkTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdCancelTransfer() { int PS4_SYSV_ABI sceUsbdCancelTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdCheckConnected() { int PS4_SYSV_ABI sceUsbdCheckConnected() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdClaimInterface() { int PS4_SYSV_ABI sceUsbdClaimInterface() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdClearHalt() { int PS4_SYSV_ABI sceUsbdClearHalt() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdClose() { int PS4_SYSV_ABI sceUsbdClose() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdControlTransfer() { int PS4_SYSV_ABI sceUsbdControlTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdControlTransferGetData() { int PS4_SYSV_ABI sceUsbdControlTransferGetData() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdControlTransferGetSetup() { int PS4_SYSV_ABI sceUsbdControlTransferGetSetup() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdDetachKernelDriver() { int PS4_SYSV_ABI sceUsbdDetachKernelDriver() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdEventHandlerActive() { int PS4_SYSV_ABI sceUsbdEventHandlerActive() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdEventHandlingOk() { int PS4_SYSV_ABI sceUsbdEventHandlingOk() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdExit() { int PS4_SYSV_ABI sceUsbdExit() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdFillBulkTransfer() { int PS4_SYSV_ABI sceUsbdFillBulkTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdFillControlSetup() { int PS4_SYSV_ABI sceUsbdFillControlSetup() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdFillControlTransfer() { int PS4_SYSV_ABI sceUsbdFillControlTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdFillInterruptTransfer() { int PS4_SYSV_ABI sceUsbdFillInterruptTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdFillIsoTransfer() { int PS4_SYSV_ABI sceUsbdFillIsoTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdFreeConfigDescriptor() { int PS4_SYSV_ABI sceUsbdFreeConfigDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdFreeDeviceList() { int PS4_SYSV_ABI sceUsbdFreeDeviceList() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdFreeTransfer() { int PS4_SYSV_ABI sceUsbdFreeTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetActiveConfigDescriptor() { int PS4_SYSV_ABI sceUsbdGetActiveConfigDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetBusNumber() { int PS4_SYSV_ABI sceUsbdGetBusNumber() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetConfigDescriptor() { int PS4_SYSV_ABI sceUsbdGetConfigDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetConfigDescriptorByValue() { int PS4_SYSV_ABI sceUsbdGetConfigDescriptorByValue() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetConfiguration() { int PS4_SYSV_ABI sceUsbdGetConfiguration() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetDescriptor() { int PS4_SYSV_ABI sceUsbdGetDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetDevice() { int PS4_SYSV_ABI sceUsbdGetDevice() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetDeviceAddress() { int PS4_SYSV_ABI sceUsbdGetDeviceAddress() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetDeviceDescriptor() { int PS4_SYSV_ABI sceUsbdGetDeviceDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetDeviceList() { int PS4_SYSV_ABI sceUsbdGetDeviceList() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetDeviceSpeed() { int PS4_SYSV_ABI sceUsbdGetDeviceSpeed() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetIsoPacketBuffer() { int PS4_SYSV_ABI sceUsbdGetIsoPacketBuffer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetMaxIsoPacketSize() { int PS4_SYSV_ABI sceUsbdGetMaxIsoPacketSize() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetMaxPacketSize() { int PS4_SYSV_ABI sceUsbdGetMaxPacketSize() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetStringDescriptor() { int PS4_SYSV_ABI sceUsbdGetStringDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdGetStringDescriptorAscii() { int PS4_SYSV_ABI sceUsbdGetStringDescriptorAscii() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdHandleEvents() { int PS4_SYSV_ABI sceUsbdHandleEvents() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdHandleEventsLocked() { int PS4_SYSV_ABI sceUsbdHandleEventsLocked() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdHandleEventsTimeout() { int PS4_SYSV_ABI sceUsbdHandleEventsTimeout() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_DEBUG(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdInit() { int PS4_SYSV_ABI sceUsbdInit() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return 0x80240005; // Skip return 0x80240005; // Skip
} }
int PS4_SYSV_ABI sceUsbdInterruptTransfer() { int PS4_SYSV_ABI sceUsbdInterruptTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdKernelDriverActive() { int PS4_SYSV_ABI sceUsbdKernelDriverActive() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdLockEvents() { int PS4_SYSV_ABI sceUsbdLockEvents() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdLockEventWaiters() { int PS4_SYSV_ABI sceUsbdLockEventWaiters() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdOpen() { int PS4_SYSV_ABI sceUsbdOpen() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdOpenDeviceWithVidPid() { int PS4_SYSV_ABI sceUsbdOpenDeviceWithVidPid() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdRefDevice() { int PS4_SYSV_ABI sceUsbdRefDevice() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdReleaseInterface() { int PS4_SYSV_ABI sceUsbdReleaseInterface() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdResetDevice() { int PS4_SYSV_ABI sceUsbdResetDevice() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdSetConfiguration() { int PS4_SYSV_ABI sceUsbdSetConfiguration() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdSetInterfaceAltSetting() { int PS4_SYSV_ABI sceUsbdSetInterfaceAltSetting() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdSetIsoPacketLengths() { int PS4_SYSV_ABI sceUsbdSetIsoPacketLengths() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdSubmitTransfer() { int PS4_SYSV_ABI sceUsbdSubmitTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdTryLockEvents() { int PS4_SYSV_ABI sceUsbdTryLockEvents() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdUnlockEvents() { int PS4_SYSV_ABI sceUsbdUnlockEvents() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdUnlockEventWaiters() { int PS4_SYSV_ABI sceUsbdUnlockEventWaiters() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdUnrefDevice() { int PS4_SYSV_ABI sceUsbdUnrefDevice() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceUsbdWaitForEvent() { int PS4_SYSV_ABI sceUsbdWaitForEvent() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI Func_65F6EF33E38FFF50() { int PS4_SYSV_ABI Func_65F6EF33E38FFF50() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI Func_97F056BAD90AADE7() { int PS4_SYSV_ABI Func_97F056BAD90AADE7() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI Func_C55104A33B35B264() { int PS4_SYSV_ABI Func_C55104A33B35B264() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI Func_D56B43060720B1E0() { int PS4_SYSV_ABI Func_D56B43060720B1E0() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called"); LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -52,8 +52,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle,
Kernel::EqueueEvent event{}; Kernel::EqueueEvent event{};
event.event.ident = u64(OrbisVideoOutEventId::Flip); event.event.ident = u64(OrbisVideoOutEventId::Flip);
event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut; event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut;
// The library only sets EV_ADD but kernel driver forces EV_CLEAR event.event.flags = Kernel::SceKernelEvent::Flags::Add;
event.event.flags = Kernel::SceKernelEvent::Flags::Clear;
event.event.udata = udata; event.event.udata = udata;
event.event.fflags = 0; event.event.fflags = 0;
event.event.data = 0; event.event.data = 0;
@ -79,8 +78,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl
Kernel::EqueueEvent event{}; Kernel::EqueueEvent event{};
event.event.ident = u64(OrbisVideoOutEventId::Vblank); event.event.ident = u64(OrbisVideoOutEventId::Vblank);
event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut; event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut;
// The library only sets EV_ADD but kernel driver forces EV_CLEAR event.event.flags = Kernel::SceKernelEvent::Flags::Add;
event.event.flags = Kernel::SceKernelEvent::Flags::Clear;
event.event.udata = udata; event.event.udata = udata;
event.event.fflags = 0; event.event.fflags = 0;
event.event.data = 0; event.event.data = 0;

View File

@ -5,6 +5,7 @@
#include "common/arch.h" #include "common/arch.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/config.h" #include "common/config.h"
#include "common/elf_info.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "common/string_util.h" #include "common/string_util.h"
@ -65,21 +66,41 @@ void Linker::Execute() {
Relocate(m.get()); Relocate(m.get());
} }
// Configure used flexible memory size. // Configure the direct and flexible memory regions.
if (const auto* proc_param = GetProcParam()) { u64 fmem_size = SCE_FLEXIBLE_MEMORY_SIZE;
if (proc_param->size >= bool use_extended_mem1 = true, use_extended_mem2 = true;
offsetof(OrbisProcParam, mem_param) + sizeof(OrbisKernelMemParam*)) {
if (const auto* mem_param = proc_param->mem_param) { const auto* proc_param = GetProcParam();
if (mem_param->size >= ASSERT(proc_param);
offsetof(OrbisKernelMemParam, flexible_memory_size) + sizeof(u64*)) {
if (const auto* flexible_size = mem_param->flexible_memory_size) { Core::OrbisKernelMemParam mem_param{};
memory->SetupMemoryRegions(*flexible_size); if (proc_param->size >= offsetof(OrbisProcParam, mem_param) + sizeof(OrbisKernelMemParam*)) {
} if (proc_param->mem_param) {
mem_param = *proc_param->mem_param;
if (mem_param.size >=
offsetof(OrbisKernelMemParam, flexible_memory_size) + sizeof(u64*)) {
if (const auto* flexible_size = mem_param.flexible_memory_size) {
fmem_size = *flexible_size + SCE_FLEXIBLE_MEMORY_BASE;
} }
} }
} }
} }
if (mem_param.size < offsetof(OrbisKernelMemParam, extended_memory_1) + sizeof(u64*)) {
mem_param.extended_memory_1 = nullptr;
}
if (mem_param.size < offsetof(OrbisKernelMemParam, extended_memory_2) + sizeof(u64*)) {
mem_param.extended_memory_2 = nullptr;
}
const u64 sdk_ver = proc_param->sdk_version;
if (sdk_ver < Common::ElfInfo::FW_50) {
use_extended_mem1 = mem_param.extended_memory_1 ? *mem_param.extended_memory_1 : false;
use_extended_mem2 = mem_param.extended_memory_2 ? *mem_param.extended_memory_2 : false;
}
memory->SetupMemoryRegions(fmem_size, use_extended_mem1, use_extended_mem2);
main_thread.Run([this, module](std::stop_token) { main_thread.Run([this, module](std::stop_token) {
Common::SetCurrentThreadName("GAME_MainThread"); Common::SetCurrentThreadName("GAME_MainThread");
LoadSharedLibraries(); LoadSharedLibraries();

View File

@ -22,8 +22,9 @@ struct OrbisKernelMemParam {
u8* extended_memory_1; u8* extended_memory_1;
u64* extended_gpu_page_table; u64* extended_gpu_page_table;
u8* extended_memory_2; u8* extended_memory_2;
u64* exnteded_cpu_page_table; u64* extended_cpu_page_table;
}; };
static_assert(sizeof(OrbisKernelMemParam) == 0x38);
struct OrbisProcParam { struct OrbisProcParam {
u64 size; u64 size;

View File

@ -7,17 +7,13 @@
#include "common/debug.h" #include "common/debug.h"
#include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/kernel/process.h"
#include "core/memory.h" #include "core/memory.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_rasterizer.h"
namespace Core { namespace Core {
constexpr u64 SCE_DEFAULT_FLEXIBLE_MEMORY_SIZE = 448_MB;
MemoryManager::MemoryManager() { MemoryManager::MemoryManager() {
// Set up the direct and flexible memory regions.
SetupMemoryRegions(SCE_DEFAULT_FLEXIBLE_MEMORY_SIZE);
// Insert a virtual memory area that covers the entire area we manage. // Insert a virtual memory area that covers the entire area we manage.
const VAddr system_managed_base = impl.SystemManagedVirtualBase(); const VAddr system_managed_base = impl.SystemManagedVirtualBase();
const size_t system_managed_size = impl.SystemManagedVirtualSize(); const size_t system_managed_size = impl.SystemManagedVirtualSize();
@ -38,10 +34,17 @@ MemoryManager::MemoryManager() {
MemoryManager::~MemoryManager() = default; MemoryManager::~MemoryManager() = default;
void MemoryManager::SetupMemoryRegions(u64 flexible_size) { void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1,
const auto total_size = bool use_extended_mem2) {
Config::isNeoMode() ? SCE_KERNEL_MAIN_DMEM_SIZE_PRO : SCE_KERNEL_MAIN_DMEM_SIZE; const bool is_neo = ::Libraries::Kernel::sceKernelIsNeoMode();
total_flexible_size = flexible_size; auto total_size = is_neo ? SCE_KERNEL_TOTAL_MEM_PRO : SCE_KERNEL_TOTAL_MEM;
if (!use_extended_mem1 && is_neo) {
total_size -= 256_MB;
}
if (!use_extended_mem2 && !is_neo) {
total_size -= 128_MB;
}
total_flexible_size = flexible_size - SCE_FLEXIBLE_MEMORY_BASE;
total_direct_size = total_size - flexible_size; total_direct_size = total_size - flexible_size;
// Insert an area that covers direct memory physical block. // Insert an area that covers direct memory physical block.
@ -101,13 +104,17 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
auto dmem_area = FindDmemArea(search_start); auto dmem_area = FindDmemArea(search_start);
const auto is_suitable = [&] { const auto is_suitable = [&] {
if (dmem_area == dmem_map.end()) {
return false;
}
const auto aligned_base = Common::AlignUp(dmem_area->second.base, alignment); const auto aligned_base = Common::AlignUp(dmem_area->second.base, alignment);
const auto alignment_size = aligned_base - dmem_area->second.base; const auto alignment_size = aligned_base - dmem_area->second.base;
const auto remaining_size = const auto remaining_size =
dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0; dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0;
return dmem_area->second.is_free && remaining_size >= size; return dmem_area->second.is_free && remaining_size >= size;
}; };
while (!is_suitable() && dmem_area->second.GetEnd() <= search_end) { while (dmem_area != dmem_map.end() && !is_suitable() &&
dmem_area->second.GetEnd() <= search_end) {
++dmem_area; ++dmem_area;
} }
ASSERT_MSG(is_suitable(), "Unable to find free direct memory area: size = {:#x}", size); ASSERT_MSG(is_suitable(), "Unable to find free direct memory area: size = {:#x}", size);
@ -164,10 +171,11 @@ int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size,
// Fixed mapping means the virtual address must exactly match the provided one. // Fixed mapping means the virtual address must exactly match the provided one.
if (True(flags & MemoryMapFlags::Fixed)) { if (True(flags & MemoryMapFlags::Fixed)) {
const auto& vma = FindVMA(mapped_addr)->second; auto& vma = FindVMA(mapped_addr)->second;
// If the VMA is mapped, unmap the region first. // If the VMA is mapped, unmap the region first.
if (vma.IsMapped()) { if (vma.IsMapped()) {
UnmapMemoryImpl(mapped_addr, size); UnmapMemoryImpl(mapped_addr, size);
vma = FindVMA(mapped_addr)->second;
} }
const size_t remaining_size = vma.base + vma.size - mapped_addr; const size_t remaining_size = vma.base + vma.size - mapped_addr;
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size);
@ -201,10 +209,11 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem
// Fixed mapping means the virtual address must exactly match the provided one. // Fixed mapping means the virtual address must exactly match the provided one.
if (True(flags & MemoryMapFlags::Fixed)) { if (True(flags & MemoryMapFlags::Fixed)) {
const auto& vma = FindVMA(mapped_addr)->second; auto& vma = FindVMA(mapped_addr)->second;
// If the VMA is mapped, unmap the region first. // If the VMA is mapped, unmap the region first.
if (vma.IsMapped()) { if (vma.IsMapped()) {
UnmapMemoryImpl(mapped_addr, size); UnmapMemoryImpl(mapped_addr, size);
vma = FindVMA(mapped_addr)->second;
} }
const size_t remaining_size = vma.base + vma.size - mapped_addr; const size_t remaining_size = vma.base + vma.size - mapped_addr;
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size); ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size);
@ -386,14 +395,18 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) {
ASSERT_MSG(vma_base.Contains(virtual_addr, size), ASSERT_MSG(vma_base.Contains(virtual_addr, size),
"Existing mapping does not contain requested unmap range"); "Existing mapping does not contain requested unmap range");
const auto type = vma_base.type;
if (type == VMAType::Free) {
return ORBIS_OK;
}
const auto vma_base_addr = vma_base.base; const auto vma_base_addr = vma_base.base;
const auto vma_base_size = vma_base.size; const auto vma_base_size = vma_base.size;
const auto phys_base = vma_base.phys_base; const auto phys_base = vma_base.phys_base;
const bool is_exec = vma_base.is_exec; const bool is_exec = vma_base.is_exec;
const auto start_in_vma = virtual_addr - vma_base_addr; const auto start_in_vma = virtual_addr - vma_base_addr;
const auto type = vma_base.type;
const bool has_backing = type == VMAType::Direct || type == VMAType::File; const bool has_backing = type == VMAType::Direct || type == VMAType::File;
if (type == VMAType::Direct) { if (type == VMAType::Direct || type == VMAType::Pooled) {
rasterizer->UnmapMemory(virtual_addr, size); rasterizer->UnmapMemory(virtual_addr, size);
} }
if (type == VMAType::Flexible) { if (type == VMAType::Flexible) {
@ -411,10 +424,12 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) {
MergeAdjacent(vma_map, new_it); MergeAdjacent(vma_map, new_it);
bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File; bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File;
// Unmap the memory region. if (type != VMAType::Reserved && type != VMAType::PoolReserved) {
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec, // Unmap the memory region.
has_backing, readonly_file); impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base,
TRACK_FREE(virtual_addr, "VMEM"); is_exec, has_backing, readonly_file);
TRACK_FREE(virtual_addr, "VMEM");
}
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -166,7 +166,7 @@ public:
bool TryWriteBacking(void* address, const void* data, u32 num_bytes); bool TryWriteBacking(void* address, const void* data, u32 num_bytes);
void SetupMemoryRegions(u64 flexible_size); void SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1, bool use_extended_mem2);
PAddr PoolExpand(PAddr search_start, PAddr search_end, size_t size, u64 alignment); PAddr PoolExpand(PAddr search_start, PAddr search_end, size_t size, u64 alignment);

View File

@ -28,8 +28,6 @@
#include "core/file_format/trp.h" #include "core/file_format/trp.h"
#include "core/file_sys/fs.h" #include "core/file_sys/fs.h"
#include "core/libraries/disc_map/disc_map.h" #include "core/libraries/disc_map/disc_map.h"
#include "core/libraries/fiber/fiber.h"
#include "core/libraries/jpeg/jpegenc.h"
#include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libc_internal/libc_internal.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/libraries/ngs2/ngs2.h" #include "core/libraries/ngs2/ngs2.h"
@ -45,10 +43,6 @@ Frontend::WindowSDL* g_window = nullptr;
namespace Core { namespace Core {
Emulator::Emulator() { Emulator::Emulator() {
// Read configuration file.
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
Config::load(config_dir / "config.toml");
// Initialize NT API functions and set high priority // Initialize NT API functions and set high priority
#ifdef _WIN32 #ifdef _WIN32
Common::NtApi::Initialize(); Common::NtApi::Initialize();
@ -63,8 +57,8 @@ Emulator::Emulator() {
LOG_INFO(Loader, "Branch {}", Common::g_scm_branch); LOG_INFO(Loader, "Branch {}", Common::g_scm_branch);
LOG_INFO(Loader, "Description {}", Common::g_scm_desc); LOG_INFO(Loader, "Description {}", Common::g_scm_desc);
LOG_INFO(Config, "General Logtype: {}", Config::getLogType()); LOG_INFO(Config, "General LogType: {}", Config::getLogType());
LOG_INFO(Config, "General isNeo: {}", Config::isNeoMode()); LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole());
LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu()); LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu());
LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders()); LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders());
LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv()); LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv());
@ -105,19 +99,12 @@ Emulator::~Emulator() {
} }
void Emulator::Run(const std::filesystem::path& file) { void Emulator::Run(const std::filesystem::path& file) {
// Use the eboot from the separated updates folder if it's there
std::filesystem::path game_patch_folder = file.parent_path();
game_patch_folder += "-UPDATE";
std::filesystem::path eboot_path = std::filesystem::exists(game_patch_folder / file.filename())
? game_patch_folder / file.filename()
: file;
// Applications expect to be run from /app0 so mount the file's parent path as app0. // Applications expect to be run from /app0 so mount the file's parent path as app0.
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance(); auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
mnt->Mount(file.parent_path(), "/app0"); const auto game_folder = file.parent_path();
mnt->Mount(game_folder, "/app0");
// Certain games may use /hostapp as well such as CUSA001100 // Certain games may use /hostapp as well such as CUSA001100
mnt->Mount(file.parent_path(), "/hostapp"); mnt->Mount(game_folder, "/hostapp");
auto& game_info = Common::ElfInfo::Instance(); auto& game_info = Common::ElfInfo::Instance();
@ -126,50 +113,52 @@ void Emulator::Run(const std::filesystem::path& file) {
std::string title; std::string title;
std::string app_version; std::string app_version;
u32 fw_version; u32 fw_version;
Common::PSFAttributes psf_attributes{};
std::filesystem::path sce_sys_folder = eboot_path.parent_path() / "sce_sys"; const auto param_sfo_path = mnt->GetHostPath("/app0/sce_sys/param.sfo");
if (std::filesystem::is_directory(sce_sys_folder)) { if (std::filesystem::exists(param_sfo_path)) {
for (const auto& entry : std::filesystem::directory_iterator(sce_sys_folder)) { auto* param_sfo = Common::Singleton<PSF>::Instance();
if (entry.path().filename() == "param.sfo") { const bool success = param_sfo->Open(param_sfo_path);
auto* param_sfo = Common::Singleton<PSF>::Instance(); ASSERT_MSG(success, "Failed to open param.sfo");
const bool success = param_sfo->Open(sce_sys_folder / "param.sfo"); const auto content_id = param_sfo->GetString("CONTENT_ID");
ASSERT_MSG(success, "Failed to open param.sfo"); ASSERT_MSG(content_id.has_value(), "Failed to get CONTENT_ID");
const auto content_id = param_sfo->GetString("CONTENT_ID"); id = std::string(*content_id, 7, 9);
ASSERT_MSG(content_id.has_value(), "Failed to get CONTENT_ID"); Libraries::NpTrophy::game_serial = id;
id = std::string(*content_id, 7, 9); const auto trophyDir =
Libraries::NpTrophy::game_serial = id; Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles";
const auto trophyDir = if (!std::filesystem::exists(trophyDir)) {
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles"; TRP trp;
if (!std::filesystem::exists(trophyDir)) { if (!trp.Extract(game_folder, id)) {
TRP trp; LOG_ERROR(Loader, "Couldn't extract trophies");
if (!trp.Extract(eboot_path.parent_path(), id)) { }
LOG_ERROR(Loader, "Couldn't extract trophies"); }
}
}
#ifdef ENABLE_QT_GUI #ifdef ENABLE_QT_GUI
MemoryPatcher::g_game_serial = id; MemoryPatcher::g_game_serial = id;
// Timer for 'Play Time' // Timer for 'Play Time'
QTimer* timer = new QTimer(); QTimer* timer = new QTimer();
QObject::connect(timer, &QTimer::timeout, [this, id]() { QObject::connect(timer, &QTimer::timeout, [this, id]() {
UpdatePlayTime(id); UpdatePlayTime(id);
start_time = std::chrono::steady_clock::now(); start_time = std::chrono::steady_clock::now();
}); });
timer->start(60000); // 60000 ms = 1 minute timer->start(60000); // 60000 ms = 1 minute
#endif #endif
title = param_sfo->GetString("TITLE").value_or("Unknown title"); title = param_sfo->GetString("TITLE").value_or("Unknown title");
LOG_INFO(Loader, "Game id: {} Title: {}", id, title); LOG_INFO(Loader, "Game id: {} Title: {}", id, title);
fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000); fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000);
app_version = param_sfo->GetString("APP_VER").value_or("Unknown version"); app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version); LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version);
} else if (entry.path().filename() == "pic1.png") { if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) {
auto* splash = Common::Singleton<Splash>::Instance(); psf_attributes.raw = *raw_attributes;
if (splash->IsLoaded()) { }
continue; }
}
if (!splash->Open(entry.path())) { const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png");
LOG_ERROR(Loader, "Game splash: unable to open file"); if (std::filesystem::exists(pic1_path)) {
} auto* splash = Common::Singleton<Splash>::Instance();
if (!splash->IsLoaded()) {
if (!splash->Open(pic1_path)) {
LOG_ERROR(Loader, "Game splash: unable to open file");
} }
} }
} }
@ -180,6 +169,7 @@ void Emulator::Run(const std::filesystem::path& file) {
game_info.app_ver = app_version; game_info.app_ver = app_version;
game_info.firmware_ver = fw_version & 0xFFF00000; game_info.firmware_ver = fw_version & 0xFFF00000;
game_info.raw_firmware_ver = fw_version; game_info.raw_firmware_ver = fw_version;
game_info.psf_attributes = psf_attributes;
std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version); std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version);
std::string window_title = ""; std::string window_title = "";
@ -223,42 +213,19 @@ void Emulator::Run(const std::filesystem::path& file) {
Libraries::InitHLELibs(&linker->GetHLESymbols()); Libraries::InitHLELibs(&linker->GetHLESymbols());
// Load the module with the linker // Load the module with the linker
const auto eboot_path = mnt->GetHostPath("/app0/" + file.filename().string());
linker->LoadModule(eboot_path); linker->LoadModule(eboot_path);
// check if we have system modules to load // check if we have system modules to load
LoadSystemModules(eboot_path, game_info.game_serial); LoadSystemModules(game_info.game_serial);
// Load all prx from game's sce_module folder // Load all prx from game's sce_module folder
std::vector<std::filesystem::path> modules_to_load; mnt->IterateDirectory("/app0/sce_module", [this](const auto& path, const auto is_file) {
std::filesystem::path game_module_folder = file.parent_path() / "sce_module"; if (is_file) {
if (std::filesystem::is_directory(game_module_folder)) { LOG_INFO(Loader, "Loading {}", fmt::UTF(path.u8string()));
for (const auto& entry : std::filesystem::directory_iterator(game_module_folder)) { linker->LoadModule(path);
if (entry.is_regular_file()) {
modules_to_load.push_back(entry.path());
}
} }
} });
// Load all prx from separate update's sce_module folder
std::filesystem::path update_module_folder = game_patch_folder / "sce_module";
if (std::filesystem::is_directory(update_module_folder)) {
for (const auto& entry : std::filesystem::directory_iterator(update_module_folder)) {
auto it = std::find_if(modules_to_load.begin(), modules_to_load.end(),
[&entry](const std::filesystem::path& p) {
return p.filename() == entry.path().filename();
});
if (it != modules_to_load.end()) {
*it = entry.path();
} else {
modules_to_load.push_back(entry.path());
}
}
}
for (const auto& module_path : modules_to_load) {
LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string()));
linker->LoadModule(module_path);
}
#ifdef ENABLE_DISCORD_RPC #ifdef ENABLE_DISCORD_RPC
// Discord RPC // Discord RPC
@ -285,17 +252,15 @@ void Emulator::Run(const std::filesystem::path& file) {
std::exit(0); std::exit(0);
} }
void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) { void Emulator::LoadSystemModules(const std::string& game_serial) {
constexpr std::array<SysModules, 13> ModulesToLoad{ constexpr std::array<SysModules, 11> ModulesToLoad{
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
{"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber},
{"libSceUlt.sprx", nullptr}, {"libSceUlt.sprx", nullptr},
{"libSceJson.sprx", nullptr}, {"libSceJson.sprx", nullptr},
{"libSceJson2.sprx", nullptr}, {"libSceJson2.sprx", nullptr},
{"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal}, {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal},
{"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap},
{"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc},
{"libSceJpegEnc.sprx", &Libraries::JpegEnc::RegisterlibSceJpegEnc},
{"libSceCesCs.sprx", nullptr}, {"libSceCesCs.sprx", nullptr},
{"libSceFont.sprx", nullptr}, {"libSceFont.sprx", nullptr},
{"libSceFontFt.sprx", nullptr}, {"libSceFontFt.sprx", nullptr},
@ -311,8 +276,9 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string
found_modules, [&](const auto& path) { return path.filename() == module_name; }); found_modules, [&](const auto& path) { return path.filename() == module_name; });
if (it != found_modules.end()) { if (it != found_modules.end()) {
LOG_INFO(Loader, "Loading {}", it->string()); LOG_INFO(Loader, "Loading {}", it->string());
linker->LoadModule(*it); if (linker->LoadModule(*it) != -1) {
continue; continue;
}
} }
if (init_func) { if (init_func) {
LOG_INFO(Loader, "Can't Load {} switching to HLE", module_name); LOG_INFO(Loader, "Can't Load {} switching to HLE", module_name);
@ -321,7 +287,7 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string
LOG_INFO(Loader, "No HLE available for {} module", module_name); LOG_INFO(Loader, "No HLE available for {} module", module_name);
} }
} }
if (std::filesystem::exists(sys_module_path / game_serial)) { if (!game_serial.empty() && std::filesystem::exists(sys_module_path / game_serial)) {
for (const auto& entry : for (const auto& entry :
std::filesystem::directory_iterator(sys_module_path / game_serial)) { std::filesystem::directory_iterator(sys_module_path / game_serial)) {
LOG_INFO(Loader, "Loading {} from game serial file {}", entry.path().string(), LOG_INFO(Loader, "Loading {} from game serial file {}", entry.path().string(),

View File

@ -29,7 +29,7 @@ public:
void UpdatePlayTime(const std::string& serial); void UpdatePlayTime(const std::string& serial);
private: private:
void LoadSystemModules(const std::filesystem::path& file, std::string game_serial); void LoadSystemModules(const std::string& game_serial);
Core::MemoryManager* memory; Core::MemoryManager* memory;
Input::GameController* controller; Input::GameController* controller;

View File

@ -2,6 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include "common/config.h"
#include "common/logging/log.h"
#include "core/libraries/kernel/time.h" #include "core/libraries/kernel/time.h"
#include "core/libraries/pad/pad.h" #include "core/libraries/pad/pad.h"
#include "input/controller.h" #include "input/controller.h"
@ -116,6 +118,98 @@ void GameController::Axis(int id, Input::Axis axis, int value) {
AddState(state); AddState(state);
} }
void GameController::Gyro(int id, const float gyro[3]) {
std::scoped_lock lock{m_mutex};
auto state = GetLastState();
state.time = Libraries::Kernel::sceKernelGetProcessTime();
// Update the angular velocity (gyro data)
state.angularVelocity.x = gyro[0]; // X-axis
state.angularVelocity.y = gyro[1]; // Y-axis
state.angularVelocity.z = gyro[2]; // Z-axis
AddState(state);
}
void GameController::Acceleration(int id, const float acceleration[3]) {
std::scoped_lock lock{m_mutex};
auto state = GetLastState();
state.time = Libraries::Kernel::sceKernelGetProcessTime();
// Update the acceleration values
state.acceleration.x = acceleration[0]; // X-axis
state.acceleration.y = acceleration[1]; // Y-axis
state.acceleration.z = acceleration[2]; // Z-axis
AddState(state);
}
// Stolen from
// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
float eInt[3] = {0.0f, 0.0f, 0.0f}; // Integral error terms
const float Kp = 50.0f; // Proportional gain
const float Ki = 1.0f; // Integral gain
Libraries::Pad::OrbisFQuaternion o = {1, 0, 0, 0};
void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
Libraries::Pad::OrbisFQuaternion& orientation) {
float ax = acceleration.x, ay = acceleration.y, az = acceleration.z;
float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z;
float q1 = o.w, q2 = o.x, q3 = o.y, q4 = o.z;
// Normalize accelerometer measurement
float norm = std::sqrt(ax * ax + ay * ay + az * az);
if (norm == 0.0f)
return; // Handle NaN
norm = 1.0f / norm;
ax *= norm;
ay *= norm;
az *= norm;
// Estimated direction of gravity
float vx = 2.0f * (q2 * q4 - q1 * q3);
float vy = 2.0f * (q1 * q2 + q3 * q4);
float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
// Error is cross product between estimated direction and measured direction of gravity
float ex = (ay * vz - az * vy);
float ey = (az * vx - ax * vz);
float ez = (ax * vy - ay * vx);
if (Ki > 0.0f) {
eInt[0] += ex * deltaTime; // Accumulate integral error
eInt[1] += ey * deltaTime;
eInt[2] += ez * deltaTime;
} else {
eInt[0] = eInt[1] = eInt[2] = 0.0f; // Prevent integral wind-up
}
// Apply feedback terms
gx += Kp * ex + Ki * eInt[0];
gy += Kp * ey + Ki * eInt[1];
gz += Kp * ez + Ki * eInt[2];
//// Integrate rate of change of quaternion
q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime);
q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime);
q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime);
q4 += (q1 * gz + q2 * gy - q3 * gx) * (0.5f * deltaTime);
// Normalize quaternion
norm = std::sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
norm = 1.0f / norm;
orientation.w = q1 * norm;
orientation.x = q2 * norm;
orientation.y = q3 * norm;
orientation.z = q4 * norm;
o.w = q1 * norm;
o.x = q2 * norm;
o.y = q3 * norm;
o.z = q4 * norm;
LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x,
orientation.y, orientation.z, orientation.w);
}
void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) {
if (m_sdl_gamepad != nullptr) { if (m_sdl_gamepad != nullptr) {
SDL_SetGamepadLED(m_sdl_gamepad, r, g, b); SDL_SetGamepadLED(m_sdl_gamepad, r, g, b);
@ -149,6 +243,21 @@ void GameController::TryOpenSDLController() {
int gamepad_count; int gamepad_count;
SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count); SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count);
m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr; m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr;
if (Config::getIsMotionControlsEnabled()) {
if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_GYRO, true)) {
gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_GYRO);
LOG_INFO(Input, "Gyro initialized, poll rate: {}", gyro_poll_rate);
} else {
LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad");
}
if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_ACCEL, true)) {
accel_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_ACCEL);
LOG_INFO(Input, "Accel initialized, poll rate: {}", accel_poll_rate);
} else {
LOG_ERROR(Input, "Failed to initialize accel controls for gamepad");
}
}
SDL_free(gamepads); SDL_free(gamepads);
SetLightBarRGB(0, 0, 255); SetLightBarRGB(0, 0, 255);
@ -156,6 +265,7 @@ void GameController::TryOpenSDLController() {
} }
u32 GameController::Poll() { u32 GameController::Poll() {
std::scoped_lock lock{m_mutex};
if (m_connected) { if (m_connected) {
auto time = Libraries::Kernel::sceKernelGetProcessTime(); auto time = Libraries::Kernel::sceKernelGetProcessTime();
if (m_states_num == 0) { if (m_states_num == 0) {

View File

@ -33,6 +33,9 @@ struct State {
u64 time = 0; u64 time = 0;
int axes[static_cast<int>(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0}; int axes[static_cast<int>(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0};
TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}}; TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}};
Libraries::Pad::OrbisFVector3 acceleration = {0.0f, 0.0f, 0.0f};
Libraries::Pad::OrbisFVector3 angularVelocity = {0.0f, 0.0f, 0.0f};
Libraries::Pad::OrbisFQuaternion orientation = {0.0f, 0.0f, 0.0f, 1.0f};
}; };
inline int GetAxis(int min, int max, int value) { inline int GetAxis(int min, int max, int value) {
@ -53,12 +56,21 @@ public:
void CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed); void CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed);
void AddState(const State& state); void AddState(const State& state);
void Axis(int id, Input::Axis axis, int value); void Axis(int id, Input::Axis axis, int value);
void Gyro(int id, const float gyro[3]);
void Acceleration(int id, const float acceleration[3]);
void SetLightBarRGB(u8 r, u8 g, u8 b); void SetLightBarRGB(u8 r, u8 g, u8 b);
bool SetVibration(u8 smallMotor, u8 largeMotor); bool SetVibration(u8 smallMotor, u8 largeMotor);
void SetTouchpadState(int touchIndex, bool touchDown, float x, float y); void SetTouchpadState(int touchIndex, bool touchDown, float x, float y);
void TryOpenSDLController(); void TryOpenSDLController();
u32 Poll(); u32 Poll();
float gyro_poll_rate;
float accel_poll_rate;
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
Libraries::Pad::OrbisFQuaternion& orientation);
private: private:
struct StateInternal { struct StateInternal {
bool obtained = false; bool obtained = false;

View File

@ -4,11 +4,14 @@
#include "functional" #include "functional"
#include "iostream" #include "iostream"
#include "string" #include "string"
#include "system_error"
#include "unordered_map" #include "unordered_map"
#include <fmt/core.h> #include <fmt/core.h>
#include "common/config.h" #include "common/config.h"
#include "common/memory_patcher.h" #include "common/memory_patcher.h"
#include "common/path_util.h"
#include "core/file_sys/fs.h"
#include "emulator.h" #include "emulator.h"
#ifdef _WIN32 #ifdef _WIN32
@ -20,6 +23,10 @@ int main(int argc, char* argv[]) {
SetConsoleOutputCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8);
#endif #endif
// Load configurations
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
Config::load(user_dir / "config.toml");
bool has_game_argument = false; bool has_game_argument = false;
std::string game_path; std::string game_path;
@ -33,6 +40,7 @@ int main(int argc, char* argv[]) {
" -p, --patch <patch_file> Apply specified patch file\n" " -p, --patch <patch_file> Apply specified patch file\n"
" -f, --fullscreen <true|false> Specify window initial fullscreen " " -f, --fullscreen <true|false> Specify window initial fullscreen "
"state. Does not overwrite the config file.\n" "state. Does not overwrite the config file.\n"
" --add-game-folder <folder> Adds a new game folder to the config.\n"
" -h, --help Display this help message\n"; " -h, --help Display this help message\n";
exit(0); exit(0);
}}, }},
@ -78,9 +86,28 @@ int main(int argc, char* argv[]) {
exit(1); exit(1);
} }
// Set fullscreen mode without saving it to config file // Set fullscreen mode without saving it to config file
Config::setFullscreenMode(is_fullscreen); Config::setIsFullscreen(is_fullscreen);
}}, }},
{"--fullscreen", [&](int& i) { arg_map["-f"](i); }}, {"--fullscreen", [&](int& i) { arg_map["-f"](i); }},
{"--add-game-folder",
[&](int& i) {
if (++i >= argc) {
std::cerr << "Error: Missing argument for --add-game-folder\n";
exit(1);
}
std::string config_dir(argv[i]);
std::filesystem::path config_path = std::filesystem::path(config_dir);
std::error_code discard;
if (!std::filesystem::exists(config_path, discard)) {
std::cerr << "Error: File does not exist: " << config_path << "\n";
exit(1);
}
Config::addGameInstallDir(config_path);
Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
std::cout << "Game folder successfully saved.\n";
exit(0);
}},
}; };
if (argc == 1) { if (argc == 1) {
@ -105,20 +132,41 @@ int main(int argc, char* argv[]) {
} }
} }
// If no game directory is set and no command line argument, prompt for it
if (Config::getGameInstallDirs().empty()) {
std::cout << "Warning: No game folder set, please set it by calling shadps4"
" with the --add-game-folder <folder_name> argument";
}
if (!has_game_argument) { if (!has_game_argument) {
std::cerr << "Error: Please provide a game path or ID.\n"; std::cerr << "Error: Please provide a game path or ID.\n";
exit(1); exit(1);
} }
// Check if the game path or ID exists // Check if the game path or ID exists
if (!std::filesystem::exists(game_path)) { std::filesystem::path eboot_path(game_path);
std::cerr << "Error: Game file not found\n";
return -1; // Check if the provided path is a valid file
if (!std::filesystem::exists(eboot_path)) {
// If not a file, treat it as a game ID and search in install directories
bool game_found = false;
for (const auto& install_dir : Config::getGameInstallDirs()) {
const auto candidate_path = install_dir / game_path / "eboot.bin";
if (std::filesystem::exists(candidate_path)) {
eboot_path = candidate_path;
game_found = true;
break;
}
}
if (!game_found) {
std::cerr << "Error: Game ID or file path not found: " << game_path << std::endl;
return 1;
}
} }
// Run the emulator with the specified game // Run the emulator with the resolved eboot path
Core::Emulator emulator; Core::Emulator emulator;
emulator.Run(game_path); emulator.Run(eboot_path);
return 0; return 0;
} }

View File

@ -14,6 +14,7 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QProcess> #include <QProcess>
#include <QProgressBar>
#include <QPushButton> #include <QPushButton>
#include <QStandardPaths> #include <QStandardPaths>
#include <QString> #include <QString>
@ -24,11 +25,9 @@
#include <common/path_util.h> #include <common/path_util.h>
#include <common/scm_rev.h> #include <common/scm_rev.h>
#include <common/version.h> #include <common/version.h>
#include <qprogressbar.h>
#include "check_update.h" #include "check_update.h"
using namespace Common::FS; using namespace Common::FS;
namespace fs = std::filesystem;
CheckUpdate::CheckUpdate(const bool showMessage, QWidget* parent) CheckUpdate::CheckUpdate(const bool showMessage, QWidget* parent)
: QDialog(parent), networkManager(new QNetworkAccessManager(this)) { : QDialog(parent), networkManager(new QNetworkAccessManager(this)) {
@ -254,7 +253,11 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
connect(noButton, &QPushButton::clicked, this, [this]() { close(); }); connect(noButton, &QPushButton::clicked, this, [this]() { close(); });
autoUpdateCheckBox->setChecked(Config::autoUpdate()); autoUpdateCheckBox->setChecked(Config::autoUpdate());
#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
connect(autoUpdateCheckBox, &QCheckBox::stateChanged, this, [](int state) { connect(autoUpdateCheckBox, &QCheckBox::stateChanged, this, [](int state) {
#else
connect(autoUpdateCheckBox, &QCheckBox::checkStateChanged, this, [](Qt::CheckState state) {
#endif
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
Config::setAutoUpdate(state == Qt::Checked); Config::setAutoUpdate(state == Qt::Checked);
Config::save(user_dir / "config.toml"); Config::save(user_dir / "config.toml");

View File

@ -133,16 +133,16 @@ void GameListFrame::PopulateGameList() {
QString formattedPlayTime; QString formattedPlayTime;
if (hours > 0) { if (hours > 0) {
formattedPlayTime += QString("%1h ").arg(hours); formattedPlayTime += QString("%1").arg(hours) + tr("h");
} }
if (minutes > 0) { if (minutes > 0) {
formattedPlayTime += QString("%1m ").arg(minutes); formattedPlayTime += QString("%1").arg(minutes) + tr("m");
} }
formattedPlayTime = formattedPlayTime.trimmed(); formattedPlayTime = formattedPlayTime.trimmed();
m_game_info->m_games[i].play_time = playTime.toStdString(); m_game_info->m_games[i].play_time = playTime.toStdString();
if (formattedPlayTime.isEmpty()) { if (formattedPlayTime.isEmpty()) {
SetTableItem(i, 8, QString("%1s").arg(seconds)); SetTableItem(i, 8, QString("%1").arg(seconds) + tr("s"));
} else { } else {
SetTableItem(i, 8, formattedPlayTime); SetTableItem(i, 8, formattedPlayTime);
} }

View File

@ -30,10 +30,11 @@ struct GameInfo {
CompatibilityEntry compatibility = CompatibilityEntry{CompatibilityStatus::Unknown}; CompatibilityEntry compatibility = CompatibilityEntry{CompatibilityStatus::Unknown};
}; };
class GameListUtils { class GameListUtils : public QObject {
Q_OBJECT
public: public:
static QString FormatSize(qint64 size) { static QString FormatSize(qint64 size) {
static const QStringList suffixes = {"B", "KB", "MB", "GB", "TB"}; static const QStringList suffixes = {tr("B"), tr("KB"), tr("MB"), tr("GB"), tr("TB")};
int suffixIndex = 0; int suffixIndex = 0;
double gameSize = static_cast<double>(size); double gameSize = static_cast<double>(size);

View File

@ -283,15 +283,15 @@ public:
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (createShortcutWin(linkPath, ebootPath, icoPath, exePath)) { if (createShortcutWin(linkPath, ebootPath, icoPath, exePath)) {
#else #else
if (createShortcutLinux(linkPath, ebootPath, iconPath)) { if (createShortcutLinux(linkPath, m_games[itemID].name, ebootPath, iconPath)) {
#endif #endif
QMessageBox::information( QMessageBox::information(
nullptr, tr("Shortcut creation"), nullptr, tr("Shortcut creation"),
QString(tr("Shortcut created successfully!\n %1")).arg(linkPath)); QString(tr("Shortcut created successfully!") + "\n%1").arg(linkPath));
} else { } else {
QMessageBox::critical( QMessageBox::critical(
nullptr, tr("Error"), nullptr, tr("Error"),
QString(tr("Error creating shortcut!\n %1")).arg(linkPath)); QString(tr("Error creating shortcut!") + "\n%1").arg(linkPath));
} }
} else { } else {
QMessageBox::critical(nullptr, tr("Error"), tr("Failed to convert icon.")); QMessageBox::critical(nullptr, tr("Error"), tr("Failed to convert icon."));
@ -301,15 +301,15 @@ public:
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
if (createShortcutWin(linkPath, ebootPath, iconPath, exePath)) { if (createShortcutWin(linkPath, ebootPath, iconPath, exePath)) {
#else #else
if (createShortcutLinux(linkPath, ebootPath, iconPath)) { if (createShortcutLinux(linkPath, m_games[itemID].name, ebootPath, iconPath)) {
#endif #endif
QMessageBox::information( QMessageBox::information(
nullptr, tr("Shortcut creation"), nullptr, tr("Shortcut creation"),
QString(tr("Shortcut created successfully!\n %1")).arg(linkPath)); QString(tr("Shortcut created successfully!") + "\n%1").arg(linkPath));
} else { } else {
QMessageBox::critical( QMessageBox::critical(
nullptr, tr("Error"), nullptr, tr("Error"),
QString(tr("Error creating shortcut!\n %1")).arg(linkPath)); QString(tr("Error creating shortcut!") + "\n%1").arg(linkPath));
} }
} }
} }
@ -347,9 +347,8 @@ public:
if (selected == deleteUpdate) { if (selected == deleteUpdate) {
if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) { if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) {
QMessageBox::critical( QMessageBox::critical(nullptr, tr("Error"),
nullptr, tr("Error"), QString(tr("This game has no update to delete!")));
QString(tr("This game has no separate update to delete!")));
error = true; error = true;
} else { } else {
folder_path = game_update_path; folder_path = game_update_path;
@ -511,8 +510,8 @@ private:
return SUCCEEDED(hres); return SUCCEEDED(hres);
} }
#else #else
bool createShortcutLinux(const QString& linkPath, const QString& targetPath, bool createShortcutLinux(const QString& linkPath, const std::string& name,
const QString& iconPath) { const QString& targetPath, const QString& iconPath) {
QFile shortcutFile(linkPath); QFile shortcutFile(linkPath);
if (!shortcutFile.open(QIODevice::WriteOnly | QIODevice::Text)) { if (!shortcutFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(nullptr, "Error", QMessageBox::critical(nullptr, "Error",
@ -523,7 +522,7 @@ private:
QTextStream out(&shortcutFile); QTextStream out(&shortcutFile);
out << "[Desktop Entry]\n"; out << "[Desktop Entry]\n";
out << "Version=1.0\n"; out << "Version=1.0\n";
out << "Name=" << QFileInfo(linkPath).baseName() << "\n"; out << "Name=" << QString::fromStdString(name) << "\n";
out << "Exec=" << QCoreApplication::applicationFilePath() << " \"" << targetPath << "\"\n"; out << "Exec=" << QCoreApplication::applicationFilePath() << " \"" << targetPath << "\"\n";
out << "Icon=" << iconPath << "\n"; out << "Icon=" << iconPath << "\n";
out << "Terminal=false\n"; out << "Terminal=false\n";

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "iostream" #include "iostream"
#include "system_error"
#include "unordered_map" #include "unordered_map"
#include "common/config.h" #include "common/config.h"
@ -31,7 +32,7 @@ int main(int argc, char* argv[]) {
bool has_command_line_argument = argc > 1; bool has_command_line_argument = argc > 1;
bool show_gui = false, has_game_argument = false; bool show_gui = false, has_game_argument = false;
std::string gamePath; std::string game_path;
// Map of argument strings to lambda functions // Map of argument strings to lambda functions
std::unordered_map<std::string, std::function<void(int&)>> arg_map = { std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
@ -46,6 +47,7 @@ int main(int argc, char* argv[]) {
" -s, --show-gui Show the GUI\n" " -s, --show-gui Show the GUI\n"
" -f, --fullscreen <true|false> Specify window initial fullscreen " " -f, --fullscreen <true|false> Specify window initial fullscreen "
"state. Does not overwrite the config file.\n" "state. Does not overwrite the config file.\n"
" --add-game-folder <folder> Adds a new game folder to the config.\n"
" -h, --help Display this help message\n"; " -h, --help Display this help message\n";
exit(0); exit(0);
}}, }},
@ -57,7 +59,7 @@ int main(int argc, char* argv[]) {
{"-g", {"-g",
[&](int& i) { [&](int& i) {
if (i + 1 < argc) { if (i + 1 < argc) {
gamePath = argv[++i]; game_path = argv[++i];
has_game_argument = true; has_game_argument = true;
} else { } else {
std::cerr << "Error: Missing argument for -g/--game\n"; std::cerr << "Error: Missing argument for -g/--game\n";
@ -95,9 +97,28 @@ int main(int argc, char* argv[]) {
exit(1); exit(1);
} }
// Set fullscreen mode without saving it to config file // Set fullscreen mode without saving it to config file
Config::setFullscreenMode(is_fullscreen); Config::setIsFullscreen(is_fullscreen);
}}, }},
{"--fullscreen", [&](int& i) { arg_map["-f"](i); }}, {"--fullscreen", [&](int& i) { arg_map["-f"](i); }},
{"--add-game-folder",
[&](int& i) {
if (++i >= argc) {
std::cerr << "Error: Missing argument for --add-game-folder\n";
exit(1);
}
std::string config_dir(argv[i]);
std::filesystem::path config_path = std::filesystem::path(config_dir);
std::error_code discard;
if (!std::filesystem::is_directory(config_path, discard)) {
std::cerr << "Error: Directory does not exist: " << config_path << "\n";
exit(1);
}
Config::addGameInstallDir(config_path);
Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
std::cout << "Game folder successfully saved.\n";
exit(0);
}},
}; };
// Parse command-line arguments using the map // Parse command-line arguments using the map
@ -106,6 +127,10 @@ int main(int argc, char* argv[]) {
auto it = arg_map.find(cur_arg); auto it = arg_map.find(cur_arg);
if (it != arg_map.end()) { if (it != arg_map.end()) {
it->second(i); // Call the associated lambda function it->second(i); // Call the associated lambda function
} else if (i == argc - 1 && !has_game_argument) {
// Assume the last argument is the game file if not specified via -g/--game
game_path = argv[i];
has_game_argument = true;
} else { } else {
std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n"; std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n";
return 1; return 1;
@ -134,14 +159,14 @@ int main(int argc, char* argv[]) {
// Process game path or ID if provided // Process game path or ID if provided
if (has_game_argument) { if (has_game_argument) {
std::filesystem::path game_file_path(gamePath); std::filesystem::path game_file_path(game_path);
// Check if the provided path is a valid file // Check if the provided path is a valid file
if (!std::filesystem::exists(game_file_path)) { if (!std::filesystem::exists(game_file_path)) {
// If not a file, treat it as a game ID and search in install directories // If not a file, treat it as a game ID and search in install directories
bool game_found = false; bool game_found = false;
for (const auto& install_dir : Config::getGameInstallDirs()) { for (const auto& install_dir : Config::getGameInstallDirs()) {
auto potential_game_path = install_dir / gamePath / "eboot.bin"; auto potential_game_path = install_dir / game_path / "eboot.bin";
if (std::filesystem::exists(potential_game_path)) { if (std::filesystem::exists(potential_game_path)) {
game_file_path = potential_game_path; game_file_path = potential_game_path;
game_found = true; game_found = true;
@ -149,7 +174,7 @@ int main(int argc, char* argv[]) {
} }
} }
if (!game_found) { if (!game_found) {
std::cerr << "Error: Game ID or file path not found: " << gamePath << std::endl; std::cerr << "Error: Game ID or file path not found: " << game_path << std::endl;
return 1; return 1;
} }
} }
@ -165,4 +190,4 @@ int main(int argc, char* argv[]) {
// Show the main window and run the Qt application // Show the main window and run the Qt application
m_main_window->show(); m_main_window->show();
return a.exec(); return a.exec();
} }

View File

@ -79,8 +79,8 @@ bool MainWindow::Init() {
this->setStatusBar(statusBar.data()); this->setStatusBar(statusBar.data());
// Update status bar // Update status bar
int numGames = m_game_info->m_games.size(); int numGames = m_game_info->m_games.size();
QString statusMessage = QString statusMessage = tr("Games: ") + QString::number(numGames) + " (" +
"Games: " + QString::number(numGames) + " (" + QString::number(duration.count()) + "ms)"; QString::number(duration.count()) + "ms)";
statusBar->showMessage(statusMessage); statusBar->showMessage(statusMessage);
#ifdef ENABLE_DISCORD_RPC #ifdef ENABLE_DISCORD_RPC
@ -116,6 +116,7 @@ void MainWindow::CreateActions() {
m_theme_act_group->addAction(ui->setThemeBlue); m_theme_act_group->addAction(ui->setThemeBlue);
m_theme_act_group->addAction(ui->setThemeViolet); m_theme_act_group->addAction(ui->setThemeViolet);
m_theme_act_group->addAction(ui->setThemeGruvbox); m_theme_act_group->addAction(ui->setThemeGruvbox);
m_theme_act_group->addAction(ui->setThemeTokyoNight);
} }
void MainWindow::AddUiWidgets() { void MainWindow::AddUiWidgets() {
@ -569,6 +570,14 @@ void MainWindow::CreateConnects() {
isIconBlack = false; isIconBlack = false;
} }
}); });
connect(ui->setThemeTokyoNight, &QAction::triggered, &m_window_themes, [this]() {
m_window_themes.SetWindowTheme(Theme::TokyoNight, ui->mw_searchbar);
Config::setMainWindowTheme(static_cast<int>(Theme::TokyoNight));
if (isIconBlack) {
SetUiIcons(false);
isIconBlack = false;
}
});
} }
void MainWindow::StartGame() { void MainWindow::StartGame() {
@ -944,6 +953,11 @@ void MainWindow::SetLastUsedTheme() {
isIconBlack = false; isIconBlack = false;
SetUiIcons(false); SetUiIcons(false);
break; break;
case Theme::TokyoNight:
ui->setThemeTokyoNight->setChecked(true);
isIconBlack = false;
SetUiIcons(false);
break;
} }
} }

View File

@ -143,5 +143,27 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
themePalette.setColor(QPalette::HighlightedText, Qt::black); themePalette.setColor(QPalette::HighlightedText, Qt::black);
qApp->setPalette(themePalette); qApp->setPalette(themePalette);
break; break;
case Theme::TokyoNight:
mw_searchbar->setStyleSheet(
"QLineEdit {"
"background-color: #1a1b26; color: #9d7cd8; border: 1px solid #9d7cd8; "
"border-radius: 4px; padding: 5px; }"
"QLineEdit:focus {"
"border: 1px solid #7aa2f7; }");
themePalette.setColor(QPalette::Window, QColor(31, 35, 53));
themePalette.setColor(QPalette::WindowText, QColor(192, 202, 245));
themePalette.setColor(QPalette::Base, QColor(25, 28, 39));
themePalette.setColor(QPalette::AlternateBase, QColor(36, 40, 59));
themePalette.setColor(QPalette::ToolTipBase, QColor(192, 202, 245));
themePalette.setColor(QPalette::ToolTipText, QColor(192, 202, 245));
themePalette.setColor(QPalette::Text, QColor(192, 202, 245));
themePalette.setColor(QPalette::Button, QColor(30, 30, 41));
themePalette.setColor(QPalette::ButtonText, QColor(192, 202, 245));
themePalette.setColor(QPalette::BrightText, QColor(197, 59, 83));
themePalette.setColor(QPalette::Link, QColor(79, 214, 190));
themePalette.setColor(QPalette::Highlight, QColor(79, 214, 190));
themePalette.setColor(QPalette::HighlightedText, Qt::black);
qApp->setPalette(themePalette);
break;
} }
} }

View File

@ -7,7 +7,7 @@
#include <QLineEdit> #include <QLineEdit>
#include <QWidget> #include <QWidget>
enum class Theme : int { Dark, Light, Green, Blue, Violet, Gruvbox }; enum class Theme : int { Dark, Light, Green, Blue, Violet, Gruvbox, TokyoNight };
class WindowThemes : public QObject { class WindowThemes : public QObject {
Q_OBJECT Q_OBJECT

View File

@ -37,6 +37,7 @@ public:
QAction* setThemeBlue; QAction* setThemeBlue;
QAction* setThemeViolet; QAction* setThemeViolet;
QAction* setThemeGruvbox; QAction* setThemeGruvbox;
QAction* setThemeTokyoNight;
QWidget* centralWidget; QWidget* centralWidget;
QLineEdit* mw_searchbar; QLineEdit* mw_searchbar;
QPushButton* playButton; QPushButton* playButton;
@ -162,6 +163,9 @@ public:
setThemeGruvbox = new QAction(MainWindow); setThemeGruvbox = new QAction(MainWindow);
setThemeGruvbox->setObjectName("setThemeGruvbox"); setThemeGruvbox->setObjectName("setThemeGruvbox");
setThemeGruvbox->setCheckable(true); setThemeGruvbox->setCheckable(true);
setThemeTokyoNight = new QAction(MainWindow);
setThemeTokyoNight->setObjectName("setThemeTokyoNight");
setThemeTokyoNight->setCheckable(true);
centralWidget = new QWidget(MainWindow); centralWidget = new QWidget(MainWindow);
centralWidget->setObjectName("centralWidget"); centralWidget->setObjectName("centralWidget");
sizePolicy.setHeightForWidth(centralWidget->sizePolicy().hasHeightForWidth()); sizePolicy.setHeightForWidth(centralWidget->sizePolicy().hasHeightForWidth());
@ -287,6 +291,7 @@ public:
menuThemes->addAction(setThemeBlue); menuThemes->addAction(setThemeBlue);
menuThemes->addAction(setThemeViolet); menuThemes->addAction(setThemeViolet);
menuThemes->addAction(setThemeGruvbox); menuThemes->addAction(setThemeGruvbox);
menuThemes->addAction(setThemeTokyoNight);
menuGame_List_Icons->addAction(setIconSizeTinyAct); menuGame_List_Icons->addAction(setIconSizeTinyAct);
menuGame_List_Icons->addAction(setIconSizeSmallAct); menuGame_List_Icons->addAction(setIconSizeSmallAct);
menuGame_List_Icons->addAction(setIconSizeMediumAct); menuGame_List_Icons->addAction(setIconSizeMediumAct);
@ -374,6 +379,7 @@ public:
setThemeBlue->setText(QCoreApplication::translate("MainWindow", "Blue", nullptr)); setThemeBlue->setText(QCoreApplication::translate("MainWindow", "Blue", nullptr));
setThemeViolet->setText(QCoreApplication::translate("MainWindow", "Violet", nullptr)); setThemeViolet->setText(QCoreApplication::translate("MainWindow", "Violet", nullptr));
setThemeGruvbox->setText("Gruvbox"); setThemeGruvbox->setText("Gruvbox");
setThemeTokyoNight->setText("Tokyo Night");
toolBar->setWindowTitle(QCoreApplication::translate("MainWindow", "toolBar", nullptr)); toolBar->setWindowTitle(QCoreApplication::translate("MainWindow", "toolBar", nullptr));
} // retranslateUi } // retranslateUi
}; };

View File

@ -47,6 +47,9 @@ PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get, QWidget* pare
connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, connect(treeWidget, &QTreeWidget::customContextMenuRequested, this,
[=, this](const QPoint& pos) { [=, this](const QPoint& pos) {
if (treeWidget->selectedItems().isEmpty()) {
return;
}
m_gui_context_menus.RequestGameMenuPKGViewer(pos, m_full_pkg_list, treeWidget, m_gui_context_menus.RequestGameMenuPKGViewer(pos, m_full_pkg_list, treeWidget,
InstallDragDropPkg); InstallDragDropPkg);
}); });

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