Merge branch 'main' into qupdate

This commit is contained in:
Marek Ledworowski 2025-11-03 21:32:55 +01:00
commit e6662e303e
351 changed files with 10167 additions and 109297 deletions

View File

@ -35,7 +35,7 @@ body:
required: true
- label: I have disabled all patches and cheats and the issue is still present.
required: true
- label: I have all the required [system modules](https://github.com/shadps4-emu/shadPS4/wiki/I.-Quick-start-%5BUsers%5D#4-adding-modules) installed.
- label: I have all the required [system modules](https://github.com/shadps4-emu/shadPS4/wiki/I.-Quick-start-%5BUsers%5D#4-dumping-firmware-modules) installed.
required: true
- type: textarea
id: desc

View File

@ -1,33 +0,0 @@
# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
#!/bin/bash
if [[ -z $GITHUB_WORKSPACE ]]; then
GITHUB_WORKSPACE="${PWD%/*}"
fi
export Qt6_DIR="/usr/lib/qt6"
export PATH="$Qt6_DIR/bin:$PATH"
export EXTRA_QT_PLUGINS="waylandcompositor"
export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"
# Prepare Tools for building the AppImage
wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh
chmod a+x linuxdeploy-x86_64.AppImage
chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage
chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh
# Build AppImage
./linuxdeploy-x86_64.AppImage --appdir AppDir
./linuxdeploy-plugin-checkrt-x86_64.sh --appdir AppDir
cp -a "$GITHUB_WORKSPACE/build/translations" AppDir/usr/bin
./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/dist/net.shadps4.shadPS4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/src/images/net.shadps4.shadPS4.svg --plugin qt
rm AppDir/usr/plugins/multimedia/libgstreamermediaplugin.so
./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage
mv shadPS4-x86_64.AppImage Shadps4-qt.AppImage

View File

@ -17,14 +17,14 @@ jobs:
runs-on: ubuntu-24.04
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: fsfe/reuse-action@v5
clang-format:
runs-on: ubuntu-24.04
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Install
@ -45,7 +45,7 @@ jobs:
shorthash: ${{ steps.vars.outputs.shorthash }}
fullhash: ${{ steps.vars.outputs.fullhash }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Get date and git hash
id: vars
run: |
@ -60,7 +60,7 @@ jobs:
runs-on: windows-2025
needs: get-info
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: recursive
@ -76,7 +76,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.18
uses: hendrikmuhs/ccache-action@v1.2.19
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with:
@ -95,69 +95,11 @@ jobs:
name: shadps4-win64-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: ${{github.workspace}}/build/shadPS4.exe
windows-qt:
runs-on: windows-2025
needs: get-info
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Qt
uses: jurplel/install-qt-action@v4
with:
version: 6.9.2
host: windows
target: desktop
arch: win64_msvc2022_64
archives: qtbase qttools
modules: qtmultimedia
- name: Cache CMake Configuration
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-qt-ninja-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.18
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 -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS
- name: Deploy and Package
run: |
mkdir upload
mkdir upload/qtplugins
move build/shadPS4.exe upload
cp dist/qt.conf upload/qt.conf
windeployqt --plugindir upload/qtplugins --no-compiler-runtime --no-system-d3d-compiler --no-system-dxc-compiler --dir upload upload/shadPS4.exe
Compress-Archive -Path upload/* -DestinationPath shadps4-win64-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}.zip
- name: Upload Windows Qt artifact
uses: actions/upload-artifact@v4
with:
name: shadps4-win64-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: upload/
macos-sdl:
runs-on: macos-15
needs: get-info
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: recursive
@ -178,7 +120,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.18
uses: hendrikmuhs/ccache-action@v1.2.19
env:
cache-name: ${{runner.os}}-sdl-cache-cmake-build
with:
@ -204,72 +146,11 @@ jobs:
name: shadps4-macos-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: upload/
macos-qt:
runs-on: macos-15
needs: get-info
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup latest Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest
- name: Setup Qt
uses: jurplel/install-qt-action@v4
with:
version: 6.9.2
host: mac
target: desktop
arch: clang_64
archives: qtbase qttools
modules: qtmultimedia
- 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.18
env:
cache-name: ${{runner.os}}-qt-cache-cmake-build
with:
append-timestamp: false
create-symlink: true
key: ${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
variant: sccache
- name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)
- name: Package and Upload macOS Qt artifact
run: |
mkdir upload
mv ${{github.workspace}}/build/shadps4.app upload
macdeployqt upload/shadps4.app
tar cf shadps4-macos-qt.tar.gz -C upload .
- uses: actions/upload-artifact@v4
with:
name: shadps4-macos-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: shadps4-macos-qt.tar.gz
linux-sdl:
runs-on: ubuntu-24.04
needs: get-info
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: recursive
@ -293,7 +174,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.18
uses: hendrikmuhs/ccache-action@v1.2.19
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with:
@ -326,63 +207,11 @@ jobs:
name: shadps4-linux-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: Shadps4-sdl.AppImage
linux-qt:
runs-on: ubuntu-24.04
needs: get-info
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Add LLVM repository
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main'
- 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 clang-19 mold 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.18
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=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -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)
- name: Run AppImage packaging script
run: ./.github/linux-appimage-qt.sh
- name: Package and Upload Linux Qt artifact
run: |
tar cf shadps4-linux-qt.tar.gz -C ${{github.workspace}}/build shadps4
- uses: actions/upload-artifact@v4
with:
name: shadps4-linux-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: Shadps4-qt.AppImage
linux-sdl-gcc:
runs-on: ubuntu-24.04
needs: get-info
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: recursive
@ -401,7 +230,7 @@ jobs:
${{ env.cache-name }}-
- name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.18
uses: hendrikmuhs/ccache-action@v1.2.19
env:
cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-build
with:
@ -414,51 +243,20 @@ jobs:
- 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 mold 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-gcc-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.18
env:
cache-name: ${{ runner.os }}-qt-gcc-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_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -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:
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, macos-sdl, linux-sdl]
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
path: ./artifacts
- name: Make SDL artifacts executable
run: |
chmod -R a+x ./artifacts/shadps4-linux-sdl-*
chmod -R a+x ./artifacts/shadps4-macos-sdl-*
- name: Compress individual directories (without parent directory)
run: |

View File

@ -1,11 +0,0 @@
#!/bin/bash
set -e
sudo apt-get -y install qt6-l10n-tools python3
SCRIPT_PATH="src/qt_gui/translations/update_translation.sh"
chmod +x "$SCRIPT_PATH"
PATH=/usr/lib/qt6/bin:$PATH "$SCRIPT_PATH"

View File

@ -1,30 +0,0 @@
name: Update Translation
on:
schedule:
- cron: "0 0 * * *" # Every day at 12am UTC.
workflow_dispatch: # As well as manually.
jobs:
update:
if: github.repository == 'shadps4-emu/shadPS4'
name: "Update Translation"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set execution permissions for the script
run: chmod +x ./.github/workflows/scripts/update_translation.sh
- name: Update Base Translation
run: ./.github/workflows/scripts/update_translation.sh
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.SHADPS4_TOKEN_REPO }}
title: "Qt GUI: Update Translation"
commit-message: "[ci skip] Qt GUI: Update Translation."
body: "Daily update of translation sources."
branch: update-translation
delete-branch: true

24
.gitmodules vendored
View File

@ -91,18 +91,6 @@
path = externals/libpng
url = https://github.com/pnggroup/libpng
shallow = true
[submodule "externals/MoltenVK/SPIRV-Cross"]
path = externals/MoltenVK/SPIRV-Cross
url = https://github.com/KhronosGroup/SPIRV-Cross
shallow = true
[submodule "externals/MoltenVK/MoltenVK"]
path = externals/MoltenVK/MoltenVK
url = https://github.com/KhronosGroup/MoltenVK
shallow = true
[submodule "externals/MoltenVK/cereal"]
path = externals/MoltenVK/cereal
url = https://github.com/USCiLab/cereal
shallow = true
[submodule "externals/ext-libusb"]
path = externals/ext-libusb
url = https://github.com/shadps4-emu/ext-libusb.git
@ -113,3 +101,15 @@
path = externals/hwinfo
url = https://github.com/shadps4-emu/ext-hwinfo
shallow = true
[submodule "externals/ext-wepoll"]
path = externals/ext-wepoll
url = https://github.com/shadps4-emu/ext-wepoll.git
shallow = true
branch = dist
[submodule "externals/MoltenVK"]
path = externals/MoltenVK
url = https://github.com/KhronosGroup/MoltenVK.git
shallow = true
[submodule "externals/json"]
path = externals/json
url = https://github.com/nlohmann/json.git

22
CMakeDarwinPresets.json Normal file
View File

@ -0,0 +1,22 @@
{
"version": 9,
"cmakeMinimumRequired": {
"major": 3,
"minor": 30,
"patch": 0
},
"configurePresets": [
{
"name": "x64-Clang-Base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/Build/${presetName}",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/Build/${presetName}",
"CMAKE_OSX_ARCHITECTURES": "x86_64"
}
}
]
}

View File

@ -31,7 +31,6 @@ if(UNIX AND NOT APPLE)
endif()
endif()
option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF)
option(ENABLE_DISCORD_RPC "Enable the Discord RPC integration" ON)
option(ENABLE_UPDATER "Enables the options to updater" ON)
@ -203,27 +202,23 @@ execute_process(
# Set Version
set(EMULATOR_VERSION_MAJOR "0")
set(EMULATOR_VERSION_MINOR "11")
set(EMULATOR_VERSION_PATCH "0")
set(EMULATOR_VERSION_MINOR "12")
set(EMULATOR_VERSION_PATCH "1")
set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}")
set(APP_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH}")
set(APP_IS_RELEASE true)
set(APP_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH} WIP")
set(APP_IS_RELEASE false)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY)
message("-- end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}, link: ${GIT_REMOTE_URL}")
string(TOLOWER "${GIT_REMOTE_URL}" GIT_REMOTE_URL_LOWER)
if(NOT GIT_REMOTE_URL_LOWER MATCHES "shadps4-emu/shadps4" OR NOT GIT_BRANCH STREQUAL "main")
if(NOT (GIT_REMOTE_URL_LOWER MATCHES "shadps4-emu/shadps4" AND (GIT_BRANCH STREQUAL "main" OR "$ENV{GITHUB_REF}" MATCHES "refs/tags/")))
message(STATUS "not main, disabling auto update")
set(ENABLE_UPDATER OFF)
endif()
if(WIN32 AND ENABLE_QT_GUI AND NOT CMAKE_PREFIX_PATH)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/DetectQtInstallation.cmake")
endif ()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
find_package(Boost 1.84.0 CONFIG)
find_package(FFmpeg 5.1.2 MODULE)
@ -237,7 +232,7 @@ find_package(SDL3 3.1.2 CONFIG)
find_package(stb MODULE)
find_package(toml11 4.2.0 CONFIG)
find_package(tsl-robin-map 1.3.0 CONFIG)
find_package(VulkanHeaders 1.4.324 CONFIG)
find_package(VulkanHeaders 1.4.329 CONFIG)
find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE)
@ -262,30 +257,6 @@ endif()
add_subdirectory(externals)
include_directories(src)
if(ENABLE_QT_GUI)
find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent LinguistTools Network Multimedia)
qt_standard_project_setup()
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(QT_TRANSLATIONS "${PROJECT_SOURCE_DIR}/src/qt_gui/translations")
file(GLOB_RECURSE TRANSLATIONS_TS ${QT_TRANSLATIONS}/*.ts)
set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS})
set(TRANSLATIONS_QRC ${CMAKE_CURRENT_BINARY_DIR}/translations/translations.qrc)
file(WRITE ${TRANSLATIONS_QRC} "<RCC><qresource prefix=\"translations\">\n")
foreach (QM ${TRANSLATIONS_QM})
get_filename_component(QM_FILE ${QM} NAME)
file(APPEND ${TRANSLATIONS_QRC} "<file>${QM_FILE}</file>\n")
endforeach (QM)
file(APPEND ${TRANSLATIONS_QRC} "</qresource></RCC>")
qt_add_resources(TRANSLATIONS ${TRANSLATIONS_QRC})
endif()
set(AJM_LIB src/core/libraries/ajm/ajm.cpp
src/core/libraries/ajm/ajm.h
src/core/libraries/ajm/ajm_at9.cpp
@ -380,6 +351,10 @@ set(NETWORK_LIBS src/core/libraries/network/http.cpp
src/core/libraries/network/net_ctl_codes.h
src/core/libraries/network/net_util.cpp
src/core/libraries/network/net_util.h
src/core/libraries/network/net_epoll.cpp
src/core/libraries/network/net_epoll.h
src/core/libraries/network/net_resolver.cpp
src/core/libraries/network/net_resolver.h
src/core/libraries/network/net_error.h
src/core/libraries/network/net.h
src/core/libraries/network/ssl.cpp
@ -557,6 +532,9 @@ set(RANDOM_LIB src/core/libraries/random/random.cpp
set(USBD_LIB src/core/libraries/usbd/usbd.cpp
src/core/libraries/usbd/usbd.h
src/core/libraries/usbd/usb_backend.h
src/core/libraries/usbd/emulated/skylander.cpp
src/core/libraries/usbd/emulated/skylander.h
)
set(FIBER_LIB src/core/libraries/fiber/fiber_context.s
@ -596,6 +574,8 @@ set(NP_LIBS src/core/libraries/np/np_error.h
src/core/libraries/np/np_auth.h
src/core/libraries/np/np_profile_dialog.cpp
src/core/libraries/np/np_profile_dialog.h
src/core/libraries/np/np_sns_facebook_dialog.cpp
src/core/libraries/np/np_sns_facebook_dialog.h
)
set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp
@ -637,6 +617,7 @@ set(COMPANION_LIBS src/core/libraries/companion/companion_httpd.cpp
)
set(DEV_TOOLS src/core/devtools/layer.cpp
src/core/devtools/layer.h
src/core/devtools/layer_extra.cpp
src/core/devtools/options.cpp
src/core/devtools/options.h
src/core/devtools/gcn/gcn_context_regs.cpp
@ -844,6 +825,8 @@ set(CORE src/core/aerolib/stubs.cpp
${DEV_TOOLS}
src/core/debug_state.cpp
src/core/debug_state.h
src/core/debugger.cpp
src/core/debugger.h
src/core/linker.cpp
src/core/linker.h
src/core/memory.cpp
@ -865,10 +848,10 @@ if (ARCHITECTURE STREQUAL "x86_64")
src/core/cpu_patches.h)
endif()
set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/profile.h
set(SHADER_RECOMPILER src/shader_recompiler/profile.h
src/shader_recompiler/recompiler.cpp
src/shader_recompiler/recompiler.h
src/shader_recompiler/resource.h
src/shader_recompiler/info.h
src/shader_recompiler/params.h
src/shader_recompiler/runtime_info.h
@ -966,17 +949,24 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/ir/value.h
)
set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
set(VIDEO_CORE src/video_core/amdgpu/cb_db_extent.h
src/video_core/amdgpu/liverpool.cpp
src/video_core/amdgpu/liverpool.h
src/video_core/amdgpu/pixel_format.cpp
src/video_core/amdgpu/pixel_format.h
src/video_core/amdgpu/pm4_cmds.h
src/video_core/amdgpu/pm4_opcodes.h
src/video_core/amdgpu/regs_color.h
src/video_core/amdgpu/regs_depth.h
src/video_core/amdgpu/regs.cpp
src/video_core/amdgpu/regs.h
src/video_core/amdgpu/regs_primitive.h
src/video_core/amdgpu/regs_shader.h
src/video_core/amdgpu/regs_texture.h
src/video_core/amdgpu/regs_vertex.h
src/video_core/amdgpu/resource.h
src/video_core/amdgpu/tiling.cpp
src/video_core/amdgpu/tiling.h
src/video_core/amdgpu/types.h
src/video_core/amdgpu/default_context.cpp
src/video_core/buffer_cache/buffer.cpp
src/video_core/buffer_cache/buffer.h
src/video_core/buffer_cache/buffer_cache.cpp
@ -1073,116 +1063,28 @@ set(EMULATOR src/emulator.cpp
src/sdl_window.cpp
)
# The above is shared in SDL and Qt version (TODO share them all)
if(ENABLE_QT_GUI)
qt_add_resources(RESOURCE_FILES src/shadps4.qrc)
if (ENABLE_UPDATER)
set(UPDATER src/qt_gui/check_update.cpp
src/qt_gui/check_update.h
)
endif()
set(QT_GUI src/qt_gui/about_dialog.cpp
src/qt_gui/about_dialog.h
src/qt_gui/about_dialog.ui
src/qt_gui/background_music_player.cpp
src/qt_gui/background_music_player.h
src/qt_gui/cheats_patches.cpp
src/qt_gui/cheats_patches.h
src/qt_gui/compatibility_info.cpp
src/qt_gui/compatibility_info.h
src/qt_gui/control_settings.cpp
src/qt_gui/control_settings.h
src/qt_gui/control_settings.ui
src/qt_gui/kbm_gui.cpp
src/qt_gui/kbm_gui.h
src/qt_gui/kbm_gui.ui
src/qt_gui/main_window_ui.h
src/qt_gui/main_window.cpp
src/qt_gui/main_window.h
src/qt_gui/gui_context_menus.h
src/qt_gui/game_list_utils.h
src/qt_gui/game_info.cpp
src/qt_gui/game_info.h
src/qt_gui/game_list_frame.cpp
src/qt_gui/game_list_frame.h
src/qt_gui/game_grid_frame.cpp
src/qt_gui/game_grid_frame.h
src/qt_gui/game_install_dialog.cpp
src/qt_gui/game_install_dialog.h
src/qt_gui/trophy_viewer.cpp
src/qt_gui/trophy_viewer.h
src/qt_gui/elf_viewer.cpp
src/qt_gui/elf_viewer.h
src/qt_gui/kbm_config_dialog.cpp
src/qt_gui/kbm_config_dialog.h
src/qt_gui/kbm_help_dialog.cpp
src/qt_gui/kbm_help_dialog.h
src/qt_gui/main_window_themes.cpp
src/qt_gui/main_window_themes.h
src/qt_gui/log_presets_dialog.cpp
src/qt_gui/log_presets_dialog.h
src/qt_gui/settings_dialog.cpp
src/qt_gui/settings_dialog.h
src/qt_gui/settings_dialog.ui
src/qt_gui/main.cpp
src/qt_gui/gui_settings.cpp
src/qt_gui/gui_settings.h
src/qt_gui/settings.cpp
src/qt_gui/settings.h
src/qt_gui/sdl_event_wrapper.cpp
src/qt_gui/sdl_event_wrapper.h
src/qt_gui/hotkeys.h
src/qt_gui/hotkeys.cpp
src/qt_gui/hotkeys.ui
${EMULATOR}
${RESOURCE_FILES}
${TRANSLATIONS}
${UPDATER}
add_executable(shadps4
${AUDIO_CORE}
${IMGUI}
${INPUT}
${COMMON}
${CORE}
${SHADER_RECOMPILER}
${VIDEO_CORE}
${EMULATOR}
${QUASIFS}
${HOSTIO}
src/main.cpp
src/emulator.cpp
src/emulator.h
src/sdl_window.h
src/sdl_window.cpp
)
endif()
if (ENABLE_QT_GUI)
qt_add_executable(shadps4
${AUDIO_CORE}
${IMGUI}
${INPUT}
${QT_GUI}
${COMMON}
${CORE}
${SHADER_RECOMPILER}
${VIDEO_CORE}
${EMULATOR}
${QUASIFS}
${HOSTIO}
src/images/shadPS4.icns
)
else()
add_executable(shadps4
${AUDIO_CORE}
${IMGUI}
${INPUT}
${COMMON}
${CORE}
${SHADER_RECOMPILER}
${VIDEO_CORE}
${EMULATOR}
${QUASIFS}
${HOSTIO}
src/main.cpp
src/emulator.cpp
src/emulator.h
src/sdl_window.h
src/sdl_window.cpp
)
endif()
create_target_directory_groups(shadps4)
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb lfreist-hwinfo::hwinfo)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json)
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
@ -1202,22 +1104,11 @@ endif()
if (APPLE)
# Include MoltenVK, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers.
if (ENABLE_QT_GUI)
set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d")
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}")
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH})
add_custom_command(
OUTPUT ${MVK_DST}
COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST})
else()
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path")
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR})
endif()
set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib)
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path")
set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR})
set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/MoltenVK/libMoltenVK.dylib)
set(MVK_DYLIB_DST ${MVK_DST}/libMoltenVK.dylib)
set(MVK_ICD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json)
set(MVK_ICD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK/icd/MoltenVK_icd.json)
set(MVK_ICD_DST ${MVK_DST}/MoltenVK_icd.json)
add_custom_command(
@ -1234,23 +1125,15 @@ if (APPLE)
if (ARCHITECTURE STREQUAL "x86_64")
# Reserve system-managed memory space.
target_link_options(shadps4 PRIVATE -Wl,-ld_classic,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000)
target_link_options(shadps4 PRIVATE -Wl,-ld_classic,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-segaddr,USER_AREA,0x7000000000,-image_base,0x700000000000)
endif()
# Replacement for std::chrono::time_zone
target_link_libraries(shadps4 PRIVATE date::date-tz epoll-shim)
endif()
if (ENABLE_QT_GUI)
target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
add_definitions(-DENABLE_QT_GUI)
if (ENABLE_UPDATER)
add_definitions(-DENABLE_UPDATER)
endif()
endif()
if (WIN32)
target_link_libraries(shadps4 PRIVATE mincore)
target_link_libraries(shadps4 PRIVATE mincore wepoll)
if (MSVC)
# MSVC likes putting opinions on what people can use, disable:
@ -1284,6 +1167,13 @@ if (WIN32)
else()
target_link_options(shadps4 PRIVATE -Wl,--stack,2097152)
endif()
# Change base image address
if (MSVC)
target_link_options(shadps4 PRIVATE /BASE:0x700000000000)
else()
target_link_options(shadps4 PRIVATE -Wl,--image-base=0x700000000000)
endif()
endif()
if (WIN32)
@ -1319,25 +1209,6 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/imgui/renderer)
add_dependencies(shadps4 ImGui_Resources)
target_include_directories(shadps4 PRIVATE ${IMGUI_RESOURCES_INCLUDE})
if (ENABLE_QT_GUI)
set_target_properties(shadps4 PROPERTIES
# WIN32_EXECUTABLE ON
MACOSX_BUNDLE ON
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in"
MACOSX_BUNDLE_ICON_FILE "shadPS4.icns"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APP_VERSION}"
)
set_source_files_properties(src/images/shadPS4.icns PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
endif()
if (UNIX AND NOT APPLE)
if (ENABLE_QT_GUI)
find_package(OpenSSL REQUIRED)
target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES})
endif()
endif()
# Discord RPC
if (ENABLE_DISCORD_RPC)
@ -1346,10 +1217,3 @@ endif()
# Install rules
install(TARGETS shadps4 BUNDLE DESTINATION .)
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.metainfo.xml" DESTINATION "share/metainfo")
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")
endif()

View File

@ -15,15 +15,6 @@
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "x64-Clang-Debug-Qt",
"displayName": "Clang x64 Debug with Qt",
"inherits": ["x64-Clang-Base"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"ENABLE_QT_GUI": "ON"
}
},
{
"name": "x64-Clang-Release",
"displayName": "Clang x64 Release",
@ -32,15 +23,6 @@
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "x64-Clang-Release-Qt",
"displayName": "Clang x64 Release with Qt",
"inherits": ["x64-Clang-Base"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"ENABLE_QT_GUI": "ON"
}
},
{
"name": "x64-Clang-RelWithDebInfo",
"displayName": "Clang x64 RelWithDebInfo",
@ -48,15 +30,6 @@
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
},
{
"name": "x64-Clang-RelWithDebInfo-Qt",
"displayName": "Clang x64 RelWithDebInfo with Qt",
"inherits": ["x64-Clang-Base"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"ENABLE_QT_GUI": "ON"
}
}
]
}

View File

@ -12,18 +12,6 @@
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
},
{
"name": "x64-Clang-Release-Qt",
"generator": "Ninja",
"configurationType": "Release",
"buildRoot": "${projectDir}\\Build\\${name}",
"installRoot": "${projectDir}\\Install\\${name}",
"cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
},
{
"name": "x64-Clang-Debug",
"generator": "Ninja",
@ -36,18 +24,6 @@
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
},
{
"name": "x64-Clang-Debug-Qt",
"generator": "Ninja",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\Build\\${name}",
"installRoot": "${projectDir}\\Install\\${name}",
"cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
},
{
"name": "x64-Clang-RelWithDebInfo",
"generator": "Ninja",
@ -59,18 +35,6 @@
"ctestCommandArgs": "",
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
},
{
"name": "x64-Clang-RelWithDebInfo-Qt",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\Build\\${name}",
"installRoot": "${projectDir}\\Install\\${name}",
"cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
}
]
}

View File

@ -36,6 +36,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
**shadPS4** is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++.
> [!IMPORTANT]
> This is the emulator core, which does not include a GUI. If you just want to use the emulator as an end user, download the [**QtLauncher**](https://github.com/shadps4-emu/shadps4-qtlauncher/releases) instead.
If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/wiki/I.-Quick-start-%5BUsers%5D).\
To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-compatibility/shadps4-game-compatibility).\
To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).\
@ -55,9 +58,6 @@ This project began for fun. Given our limited free time, it may take some time b
# Building
> [!IMPORTANT]
> If you want to use shadPS4 to play your games, you don't have to follow the build instructions, you can simply download the emulator from either the [**release tab**](https://github.com/shadps4-emu/shadPS4/releases) or the [**action tab**](https://github.com/shadps4-emu/shadPS4/actions).
## Windows
Check the build instructions for [**Windows**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-windows.md).
@ -73,6 +73,22 @@ Check the build instructions for [**macOS**](https://github.com/shadps4-emu/shad
> [!IMPORTANT]
> macOS users need at least macOS 15.4 to run shadPS4. Due to GPU issues there are currently heavy bugs on Intel Macs.
# Usage examples
> [!IMPORTANT]
> For a user-friendly GUI, download the [**QtLauncher**](https://github.com/shadps4-emu/shadps4-qtlauncher/releases).
To get the list of all available commands and also a more detailed description of what each command does, please refer to the `--help` flag's output.
Below is a list of commonly used command patterns:
```sh
shadPS4 CUSA00001 # Searches for a game folder called CUSA00001 in the list of game install folders, and boots it.
shadPS4 --fullscreen true --config-clean CUSA00001 # the game argument is always the last one,
shadPS4 -g CUSA00001 --fullscreen true --config-clean # ...unless manually specified otherwise.
shadPS4 /path/to/game.elf # Boots a PS4 ELF file directly. Useful if you want to boot an executable that is not named eboot.bin.
shadPS4 CUSA00001 -- -flag1 -flag2 # Passes '-flag1' and '-flag2' to the game executable in argv.
```
# Debugging and reporting issues
For more information on how to test, debug and report issues with the emulator or games, read the [**Debugging documentation**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md).
@ -160,15 +176,6 @@ Logo is done by [**Xphalnos**](https://github.com/Xphalnos)
If you want to contribute, please read the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.\
Open a PR and we'll check it :)
# Translations
If you want to translate shadPS4 to your language we use [**Crowdin**](https://crowdin.com/project/shadps4-emulator).
# Contributors
<a href="https://github.com/shadps4-emu/shadPS4/graphs/contributors">
<img src="https://contrib.rocks/image?repo=shadps4-emu/shadPS4&max=24">
</a>
# Special Thanks

View File

@ -5,13 +5,12 @@ path = [
"REUSE.toml",
"crowdin.yml",
"CMakeSettings.json",
"CMakeDarwinPresets.json",
"CMakeLinuxPresets.json",
"CMakeWindowsPresets.json",
"CMakePresets.json",
".github/FUNDING.yml",
".github/shadps4.png",
".github/workflows/scripts/update_translation.sh",
".github/workflows/update_translation.yml",
".gitmodules",
"dist/MacOSBundleInfo.plist.in",
"dist/net.shadps4.shadPS4.desktop",
@ -75,9 +74,7 @@ path = [
"src/images/trophy.wav",
"src/images/hotkey.png",
"src/images/game_settings.png",
"src/shadps4.qrc",
"src/shadps4.rc",
"src/qt_gui/translations/update_translation.sh",
]
precedence = "aggregate"
SPDX-FileCopyrightText = "shadPS4 Emulator Project"
@ -132,9 +129,4 @@ SPDX-License-Identifier = "MIT"
[[annotations]]
path = "src/video_core/host_shaders/fsr/*"
SPDX-FileCopyrightText = "Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved."
SPDX-License-Identifier = "MIT"
[[annotations]]
path = "dist/qt.conf"
SPDX-FileCopyrightText = "shadPS4 Emulator Project"
SPDX-License-Identifier = "GPL-2.0-or-later"
SPDX-License-Identifier = "MIT"

View File

@ -1,28 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
set(highest_version "0")
set(CANDIDATE_DRIVES A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
foreach(drive ${CANDIDATE_DRIVES})
file(GLOB kits LIST_DIRECTORIES true CONFIGURE_DEPENDS "${drive}:/Qt/*/msvc*_64")
foreach(kit IN LISTS kits)
get_filename_component(version_dir "${kit}" DIRECTORY)
get_filename_component(kit_version "${version_dir}" NAME)
message(STATUS "DetectQtInstallation.cmake: Detected Qt: ${kit}")
if (kit_version VERSION_GREATER highest_version)
set(highest_version "${kit_version}")
set(QT_PREFIX "${kit}")
endif()
endforeach()
endforeach()
if(QT_PREFIX)
set(CMAKE_PREFIX_PATH "${QT_PREFIX}" CACHE PATH "Qt prefix autodetected" FORCE)
message(STATUS "DetectQtInstallation.cmake: Choose newest Qt: ${QT_PREFIX}")
else()
message(STATUS "DetectQtInstallation.cmake: No QtDirectory found in <drive>:/Qt please set CMAKE_PREFIX_PATH manually")
endif()

View File

@ -1,3 +0,0 @@
files:
- source: /src/qt_gui/translations/en_US.ts
translation: /%original_path%/%locale_with_underscore%.ts

View File

@ -37,6 +37,9 @@
<category translate="no">Game</category>
</categories>
<releases>
<release version="0.12.0" date="2025-10-31">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.0</url>
</release>
<release version="0.11.0" date="2025-09-18">
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.11.0</url>
</release>

2
dist/qt.conf vendored
View File

@ -1,2 +0,0 @@
[Paths]
plugins = "./qtplugins"

View File

@ -11,6 +11,13 @@ This document covers information about debugging, troubleshooting and reporting
This section will guide you through setting up tools for debugging the emulator. This list will likely expand as more tools and platforms receive consistent setups.
<details>
<summary>Linux</summary>
RenderDoc doesn't work with Wayland, so to use it you have to run the emulator with `SDL_VIDEODRIVER=x11` set.
</details>
<details>
<summary>Windows and Visual Studio</summary>

View File

@ -17,8 +17,7 @@ First and foremost, Clang 18 is the **recommended compiler** as it is used for o
sudo apt install build-essential clang git cmake libasound2-dev \
libpulse-dev libopenal-dev libssl-dev zlib1g-dev libedit-dev \
libudev-dev libevdev-dev libsdl2-dev libjack-dev libsndio-dev \
qt6-base-dev qt6-tools-dev qt6-multimedia-dev libvulkan-dev \
vulkan-validationlayers libpng-dev
libvulkan-dev vulkan-validationlayers libpng-dev
```
#### Fedora
@ -27,8 +26,6 @@ sudo apt install build-essential clang git cmake libasound2-dev \
sudo dnf install clang git cmake libatomic alsa-lib-devel \
pipewire-jack-audio-connection-kit-devel openal-soft-devel \
openssl-devel libevdev-devel libudev-devel libXext-devel \
qt6-qtbase-devel qt6-qtbase-private-devel \
qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \
vulkan-devel vulkan-validation-layers libpng-devel libuuid-devel
```
@ -36,8 +33,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel \
```bash
sudo pacman -S base-devel clang git cmake sndio jack2 openal \
qt6-base qt6-declarative qt6-multimedia qt6-tools sdl2 \
vulkan-validation-layers libpng
sdl2 vulkan-validation-layers libpng
```
**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.
@ -48,9 +44,7 @@ sudo pacman -S base-devel clang git cmake sndio jack2 openal \
sudo zypper install clang git cmake libasound2 libpulse-devel \
libsndio7 libjack-devel openal-soft-devel libopenssl-devel \
zlib-devel libedit-devel systemd-devel libevdev-devel \
qt6-base-devel qt6-multimedia-devel qt6-svg-devel \
qt6-linguist-devel qt6-gui-private-devel vulkan-devel \
vulkan-validationlayers libpng-devel
vulkan-devel vulkan-validationlayers libpng-devel
```
#### NixOS
@ -90,12 +84,12 @@ There are 3 options you can choose from. Option 1 is **highly recommended**.
1. Generate the build directory in the shadPS4 directory.
```bash
cmake -S . -B build/ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
cmake -S . -B build/ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
```
To disable the Qt GUI, remove the `-DENABLE_QT_GUI=ON` flag. To change the build type (for debugging), add `-DCMAKE_BUILD_TYPE=Debug`.
To change the build type (for debugging), add `-DCMAKE_BUILD_TYPE=Debug`.
2. Use CMake to build the project:
1. Use CMake to build the project:
```bash
cmake --build ./build --parallel$(nproc)
@ -103,18 +97,12 @@ cmake --build ./build --parallel$(nproc)
If your computer freezes during this step, this could be caused by excessive system resource usage. In that case, remove `--parallel$(nproc)`.
Now run the emulator. If Qt was enabled at configure time:
Now run the emulator to get the list of options:
```bash
./build/shadps4
```
Otherwise, specify the path to your game's boot file:
```bash
./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin
```
You can also specify the Game ID as an argument for which game to boot, as long as the folder containing the games is specified in config.toml (example: Bloodborne (US) is CUSA00900).
#### Option 2: Configuring with cmake-gui
@ -142,10 +130,6 @@ Go to Settings, filter by `@ext:ms-vscode.cmake-tools configure` and disable thi
![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/1.png)
If you wish to build with the Qt GUI, add `-DENABLE_QT_GUI=ON` to the configure arguments:
![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/2.png)
On the CMake tab, change the options as you wish, but make sure that it looks similar to or exactly like this:
![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/4.png)

View File

@ -24,21 +24,6 @@ eval $(/opt/homebrew/bin/brew shellenv)
brew install clang-format cmake
```
Next, install x86_64 Qt. You can skip these steps and move on to **Cloning and compiling** if you do not intend to build the Qt GUI.
**If you are on an ARM Mac:**
```
# Installs x86_64 Homebrew to /usr/local
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Installs libraries.
arch -x86_64 /usr/local/bin/brew install qt@6
```
**If you are on an x86_64 Mac:**
```
brew install qt@6
```
### Cloning and compiling:
Clone the repository recursively:
@ -52,8 +37,6 @@ Generate the build directory in the shadPS4 directory:
cmake -S . -B build/ -DCMAKE_OSX_ARCHITECTURES=x86_64
```
If you want to build the Qt GUI, add `-DENABLE_QT_GUI=ON` to the end of this command as well.
Enter the directory:
```
cd build/

View File

@ -1,12 +1,14 @@
<!--
SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
SPDX-FileCopyrightText: 2025 shadPS4 Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later
-->
# Build shadPS4 for Windows
This tutorial reads as if you have none of the prerequisites already installed. If you do, just ignore the steps regarding installation.
If you are building to contribute to the project, please omit `--depth 1` from the git invocations.
> [!WARNING]
> If you are trying to compile older builds for testing, do not provide the `--depth 1` flag in `git clone`.
> This flag omits the commit history from your clone, saving storage space while preventing you from testing older commits.
Note: **ARM64 is not supported!** As of writing, it will not build nor run. The instructions with respect to ARM64 are for developers only.
@ -20,23 +22,6 @@ Once you are within the installer:
2. Go to "Individual Components" tab then search and select both `C++ Clang Compiler for Windows` and `MSBuild support for LLVM`
3. Continue the installation
### (Prerequisite) Download [**Qt**](https://doc.qt.io/qt-6/get-and-install-qt.html)
Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead.
1. Under the current, non beta version of Qt, select the option `MSVC 2022 64-bit` or similar, as well as `QT Multimedia`.
If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead.
Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space.
2. Download and install [Qt Visual Studio Tools](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022)
Once you are finished, you will have to configure Qt within Visual Studio:
1. Tools -> Options -> Qt -> Versions
2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\<QtVersion>\msvc2022_64`
3. Enable the default checkmark on the new version you just created.
### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win)
Go through the Git for Windows installation as normal
@ -77,13 +62,10 @@ Normal x86-based computers, follow:
1. Open "MSYS2 MINGW64" from your new applications
2. Run `pacman -Syu`, let it complete;
3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-rapidjson mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg`
1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools mingw-w64-x86_64-qt6-multimedia`
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4`
6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`
1. Optional (Qt only): add `-DENABLE_QT_GUI=ON`
7. Run `cmake --build build`
1. Optional (Qt only): run `windeployqt6 build/shadps4.exe`
8. To run the finished product, run `./build/shadPS4.exe`
ARM64-based computers, follow:
@ -95,9 +77,7 @@ ARM64-based computers, follow:
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4`
6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`
1. Optional (Qt only): add `-DENABLE_QT_GUI=ON`
7. Run `cmake --build build`
1. Optional (Qt only): run `windeployqt6 build/shadps4.exe`
8. To run the finished product, run `./build/shadPS4.exe`
## Note on MSYS2 builds

View File

@ -231,6 +231,8 @@ if (APPLE)
# MoltenVK
if (NOT TARGET MoltenVK)
set(MVK_EXCLUDE_SPIRV_TOOLS ON)
set(MVK_USE_METAL_PRIVATE_API ON)
add_subdirectory(MoltenVK)
endif()
@ -238,3 +240,12 @@ if (APPLE)
add_subdirectory(epoll-shim)
endif()
endif()
#windows only
if (WIN32)
add_subdirectory(ext-wepoll)
endif()
#nlohmann json
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(json)

1
externals/MoltenVK vendored Submodule

@ -0,0 +1 @@
Subproject commit b23d42534622cd9926fe526fec1b7f8795a2853c

View File

@ -1,93 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# Prepare MoltenVK Git revision
find_package(Git)
if(GIT_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
OUTPUT_VARIABLE MVK_GIT_REV
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
set(MVK_GENERATED_INCLUDES ${CMAKE_CURRENT_BINARY_DIR}/Generated)
file(WRITE ${MVK_GENERATED_INCLUDES}/mvkGitRevDerived.h "static const char* mvkRevString = \"${MVK_GIT_REV}\";")
message(STATUS "MoltenVK revision: ${MVK_GIT_REV}")
# Prepare MoltenVK version
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/MoltenVK/API/mvk_private_api.h MVK_PRIVATE_API)
string(REGEX MATCH "#define MVK_VERSION_MAJOR [0-9]+" MVK_VERSION_MAJOR_LINE "${MVK_PRIVATE_API}")
string(REGEX MATCH "[0-9]+" MVK_VERSION_MAJOR "${MVK_VERSION_MAJOR_LINE}")
string(REGEX MATCH "#define MVK_VERSION_MINOR [0-9]+" MVK_VERSION_MINOR_LINE "${MVK_PRIVATE_API}")
string(REGEX MATCH "[0-9]+" MVK_VERSION_MINOR "${MVK_VERSION_MINOR_LINE}")
string(REGEX MATCH "#define MVK_VERSION_PATCH [0-9]+" MVK_VERSION_PATCH_LINE "${MVK_PRIVATE_API}")
string(REGEX MATCH "[0-9]+" MVK_VERSION_PATCH "${MVK_VERSION_PATCH_LINE}")
set(MVK_VERSION "${MVK_VERSION_MAJOR}.${MVK_VERSION_MINOR}.${MVK_VERSION_PATCH}")
message(STATUS "MoltenVK version: ${MVK_VERSION}")
# Find required system libraries
find_library(APPKIT_LIBRARY AppKit REQUIRED)
find_library(FOUNDATION_LIBRARY Foundation REQUIRED)
find_library(IOKIT_LIBRARY IOKit REQUIRED)
find_library(IOSURFACE_LIBRARY IOSurface REQUIRED)
find_library(METAL_LIBRARY Metal REQUIRED)
find_library(QUARTZCORE_LIBRARY QuartzCore REQUIRED)
# cereal
option(SKIP_PORTABILITY_TEST "" ON)
option(BUILD_DOC "" OFF)
option(BUILD_SANDBOX "" OFF)
option(SKIP_PERFORMANCE_COMPARISON "" ON)
option(SPIRV_CROSS_SKIP_INSTALL "" ON)
add_subdirectory(cereal)
# SPIRV-Cross
option(SPIRV_CROSS_CLI "" OFF)
option(SPIRV_CROSS_ENABLE_TESTS "" OFF)
option(SPIRV_CROSS_ENABLE_HLSL "" OFF)
option(SPIRV_CROSS_ENABLE_CPP "" OFF)
option(SPIRV_CROSS_SKIP_INSTALL "" ON)
add_subdirectory(SPIRV-Cross)
# Common
set(MVK_COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/Common)
file(GLOB_RECURSE MVK_COMMON_SOURCES CONFIGURE_DEPENDS
${MVK_COMMON_DIR}/*.cpp
${MVK_COMMON_DIR}/*.m
${MVK_COMMON_DIR}/*.mm)
set(MVK_COMMON_INCLUDES ${MVK_COMMON_DIR})
add_library(MoltenVKCommon STATIC ${MVK_COMMON_SOURCES})
target_include_directories(MoltenVKCommon PUBLIC ${MVK_COMMON_INCLUDES})
target_compile_options(MoltenVKCommon PRIVATE -w)
# MoltenVKShaderConverter
set(MVK_SHADER_CONVERTER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVKShaderConverter)
file(GLOB_RECURSE MVK_SHADER_CONVERTER_SOURCES CONFIGURE_DEPENDS
${MVK_SHADER_CONVERTER_DIR}/MoltenVKShaderConverter/*.cpp
${MVK_SHADER_CONVERTER_DIR}/MoltenVKShaderConverter/*.m
${MVK_SHADER_CONVERTER_DIR}/MoltenVKShaderConverter/*.mm)
set(MVK_SHADER_CONVERTER_INCLUDES ${MVK_SHADER_CONVERTER_DIR} ${MVK_SHADER_CONVERTER_DIR}/include)
add_library(MoltenVKShaderConverter STATIC ${MVK_SHADER_CONVERTER_SOURCES})
target_include_directories(MoltenVKShaderConverter PUBLIC ${MVK_SHADER_CONVERTER_INCLUDES})
target_compile_options(MoltenVKShaderConverter PRIVATE -w)
target_link_libraries(MoltenVKShaderConverter PRIVATE spirv-cross-msl spirv-cross-reflect MoltenVKCommon)
target_compile_definitions(MoltenVKShaderConverter PRIVATE MVK_EXCLUDE_SPIRV_TOOLS=1)
# MoltenVK
set(MVK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK)
file(GLOB_RECURSE MVK_SOURCES CONFIGURE_DEPENDS
${MVK_DIR}/MoltenVK/*.cpp
${MVK_DIR}/MoltenVK/*.m
${MVK_DIR}/MoltenVK/*.mm)
file(GLOB MVK_SRC_INCLUDES LIST_DIRECTORIES ON ${MVK_DIR}/MoltenVK/*)
set(MVK_INCLUDES ${MVK_SRC_INCLUDES} ${MVK_GENERATED_INCLUDES} ${MVK_DIR}/include)
add_library(MoltenVK SHARED ${MVK_SOURCES})
target_include_directories(MoltenVK PRIVATE ${MVK_INCLUDES})
target_compile_options(MoltenVK PRIVATE -w)
target_link_libraries(MoltenVK PRIVATE
${APPKIT_LIBRARY} ${FOUNDATION_LIBRARY} ${IOKIT_LIBRARY} ${IOSURFACE_LIBRARY} ${METAL_LIBRARY} ${QUARTZCORE_LIBRARY}
Vulkan::Headers cereal::cereal spirv-cross-msl MoltenVKCommon MoltenVKShaderConverter)
target_compile_definitions(MoltenVK PRIVATE MVK_FRAMEWORK_VERSION=${MVK_VERSION} MVK_USE_METAL_PRIVATE_API=1)

@ -1 +0,0 @@
Subproject commit 712fbb808ddb81c9369fa3870c41e4c36add311c

@ -1 +0,0 @@
Subproject commit 0a88b2d5c08708d45692b7096a0a84e7bfae366c

@ -1 +0,0 @@
Subproject commit a56bad8bbb770ee266e930c95d37fff2a5be7fea

1
externals/json vendored Submodule

@ -0,0 +1 @@
Subproject commit 55f93686c01528224f448c19128836e7df245f72

View File

@ -17,7 +17,6 @@ pkgs.mkShell {
pkgs.alsa-lib
pkgs.libpulseaudio
pkgs.openal
pkgs.openssl
pkgs.zlib
pkgs.libedit
pkgs.udev
@ -25,9 +24,6 @@ pkgs.mkShell {
pkgs.SDL2
pkgs.jack2
pkgs.sndio
pkgs.qt6.qtbase
pkgs.qt6.qttools
pkgs.qt6.qtmultimedia
pkgs.vulkan-headers
pkgs.vulkan-utility-libraries
@ -44,16 +40,12 @@ pkgs.mkShell {
pkgs.xorg.xcbutilwm
pkgs.sdl3
pkgs.stb
pkgs.qt6.qtwayland
pkgs.wayland-protocols
pkgs.libpng
];
shellHook = ''
echo "Entering shadPS4 dev shell"
export QT_QPA_PLATFORM="wayland"
export QT_PLUGIN_PATH="${pkgs.qt6.qtwayland}/lib/qt-6/plugins:${pkgs.qt6.qtbase}/lib/qt-6/plugins"
export QML2_IMPORT_PATH="${pkgs.qt6.qtbase}/lib/qt-6/qml"
export CMAKE_PREFIX_PATH="${pkgs.vulkan-headers}:$CMAKE_PREFIX_PATH"
# OpenGL

View File

@ -8,6 +8,7 @@
#include <fmt/xchar.h> // for wstring support
#include <toml.hpp>
#include "common/assert.h"
#include "common/config.h"
#include "common/logging/formatter.h"
#include "common/path_util.h"
@ -74,18 +75,34 @@ std::optional<T> get_optional(const toml::value& v, const std::string& key) {
namespace Config {
ConfigMode config_mode = ConfigMode::Default;
void setConfigMode(ConfigMode mode) {
config_mode = mode;
}
template <typename T>
class ConfigEntry {
public:
const T default_value;
T base_value;
optional<T> game_specific_value;
ConfigEntry(const T& t = T()) : base_value(t), game_specific_value(nullopt) {}
ConfigEntry(const T& t = T()) : default_value(t), base_value(t), game_specific_value(nullopt) {}
ConfigEntry operator=(const T& t) {
base_value = t;
return *this;
}
const T get() const {
return game_specific_value.has_value() ? *game_specific_value : base_value;
switch (config_mode) {
case ConfigMode::Default:
return game_specific_value.value_or(base_value);
case ConfigMode::Global:
return base_value;
case ConfigMode::Clean:
return default_value;
default:
UNREACHABLE();
}
}
void setFromToml(const toml::value& v, const std::string& key, bool is_game_specific = false) {
if (is_game_specific) {
@ -115,19 +132,18 @@ public:
static ConfigEntry<int> volumeSlider(100);
static ConfigEntry<bool> isNeo(false);
static ConfigEntry<bool> isDevKit(false);
static ConfigEntry<int> extraDmemInMbytes(0);
static ConfigEntry<bool> isPSNSignedIn(false);
static ConfigEntry<bool> isTrophyPopupDisabled(false);
static ConfigEntry<double> trophyNotificationDuration(6.0);
static ConfigEntry<string> logFilter("");
static ConfigEntry<string> logType("sync");
static ConfigEntry<string> userName("shadPS4");
static ConfigEntry<string> chooseHomeTab("General");
static ConfigEntry<bool> isShowSplash(false);
static ConfigEntry<string> isSideTrophy("right");
static ConfigEntry<bool> isConnectedToNetwork(false);
static bool enableDiscordRPC = false;
static bool checkCompatibilityOnStartup = false;
static bool compatibilityData = false;
static std::filesystem::path sys_modules_path = {};
// Input
static ConfigEntry<int> cursorState(HideCursorState::Idle);
@ -168,6 +184,7 @@ static ConfigEntry<int> rcasAttenuation(250);
// Vulkan
static ConfigEntry<s32> gpuId(-1);
static ConfigEntry<bool> vkValidation(false);
static ConfigEntry<bool> vkValidationCore(true);
static ConfigEntry<bool> vkValidationSync(false);
static ConfigEntry<bool> vkValidationGpu(false);
static ConfigEntry<bool> vkCrashDiagnostic(false);
@ -183,7 +200,6 @@ static ConfigEntry<bool> isFpsColor(true);
static ConfigEntry<bool> logEnabled(true);
// GUI
static bool load_game_size = true;
static std::vector<GameInstallDir> settings_install_dirs = {};
std::vector<bool> install_dirs_enabled = {};
std::filesystem::path settings_addon_install_dir = {};
@ -192,15 +208,39 @@ std::filesystem::path save_data_path = {};
// Settings
ConfigEntry<u32> m_language(1); // english
// USB Device
static ConfigEntry<int> usbDeviceBackend(UsbBackendType::Real);
// Keys
static string trophyKey = "";
// Config version, used to determine if a user's config file is outdated.
static string config_version = Common::g_scm_rev;
// These two entries aren't stored in the config
// These entries aren't stored in the config
static bool overrideControllerColor = false;
static int controllerCustomColorRGB[3] = {0, 0, 255};
static bool isGameRunning = false;
static bool load_auto_patches = true;
bool getGameRunning() {
return isGameRunning;
}
void setGameRunning(bool running) {
isGameRunning = running;
}
std::filesystem::path getSysModulesPath() {
if (sys_modules_path.empty()) {
return Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir);
}
return sys_modules_path;
}
void setSysModulesPath(const std::filesystem::path& path) {
sys_modules_path = path;
}
int getVolumeSlider() {
return volumeSlider.get();
@ -247,10 +287,6 @@ void setTrophyKey(string key) {
trophyKey = key;
}
bool GetLoadGameSizeEnabled() {
return load_game_size;
}
std::filesystem::path GetSaveDataPath() {
if (save_data_path.empty()) {
return Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "savedata";
@ -262,10 +298,6 @@ void setVolumeSlider(int volumeValue, bool is_game_specific) {
volumeSlider.set(volumeValue, is_game_specific);
}
void setLoadGameSizeEnabled(bool enable) {
load_game_size = enable;
}
bool isNeoModeConsole() {
return isNeo.get();
}
@ -274,6 +306,16 @@ bool isDevKitConsole() {
return isDevKit.get();
}
int getExtraDmemInMbytes() {
return extraDmemInMbytes.get();
}
void setExtraDmemInMbytes(int value, bool is_game_specific) {
// Disable setting in global config
is_game_specific ? extraDmemInMbytes.game_specific_value = value
: extraDmemInMbytes.base_value = 0;
}
bool getIsFullscreen() {
return isFullscreen.get();
}
@ -350,10 +392,6 @@ string getUserName() {
return userName.get();
}
string getChooseHomeTab() {
return chooseHomeTab.get();
}
bool getUseSpecialPad() {
return useSpecialPad.get();
}
@ -433,6 +471,10 @@ bool vkValidationEnabled() {
return vkValidation.get();
}
bool vkValidationCoreEnabled() {
return vkValidationCore.get();
}
bool vkValidationSyncEnabled() {
return vkValidationSync.get();
}
@ -465,14 +507,6 @@ void setVkGuestMarkersEnabled(bool enable, bool is_game_specific) {
vkGuestMarkers.set(enable, is_game_specific);
}
bool getCompatibilityEnabled() {
return compatibilityData;
}
bool getCheckCompatibilityOnStartup() {
return checkCompatibilityOnStartup;
}
bool getIsConnectedToNetwork() {
return isConnectedToNetwork.get();
}
@ -557,6 +591,14 @@ void setVkSyncValidation(bool enable, bool is_game_specific) {
vkValidationSync.set(enable, is_game_specific);
}
void setVkCoreValidation(bool enable, bool is_game_specific) {
vkValidationCore.set(enable, is_game_specific);
}
void setVkGpuValidation(bool enable, bool is_game_specific) {
vkValidationGpu.set(enable, is_game_specific);
}
void setRdocEnabled(bool enable, bool is_game_specific) {
rdocEnable.set(enable, is_game_specific);
}
@ -637,10 +679,6 @@ void setUserName(const string& name, bool is_game_specific) {
userName.set(name, is_game_specific);
}
void setChooseHomeTab(const string& type, bool is_game_specific) {
chooseHomeTab.set(type, is_game_specific);
}
void setUseSpecialPad(bool use) {
useSpecialPad.base_value = use;
}
@ -653,14 +691,6 @@ void setIsMotionControlsEnabled(bool use, bool is_game_specific) {
isMotionControlsEnabled.set(use, is_game_specific);
}
void setCompatibilityEnabled(bool use) {
compatibilityData = use;
}
void setCheckCompatibilityOnStartup(bool use) {
checkCompatibilityOnStartup = use;
}
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled) {
for (const auto& install_dir : settings_install_dirs) {
if (install_dir.path == dir) {
@ -790,6 +820,21 @@ void setRcasAttenuation(int value, bool is_game_specific) {
rcasAttenuation.set(value, is_game_specific);
}
int getUsbDeviceBackend() {
return usbDeviceBackend.get();
}
void setUsbDeviceBackend(int value, bool is_game_specific) {
usbDeviceBackend.set(value, is_game_specific);
}
bool getLoadAutoPatches() {
return load_auto_patches;
}
void setLoadAutoPatches(bool enable) {
load_auto_patches = enable;
}
void load(const std::filesystem::path& path, bool is_game_specific) {
// If the configuration file does not exist, create it and return, unless it is game specific
std::error_code error;
@ -818,6 +863,9 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
volumeSlider.setFromToml(general, "volumeSlider", is_game_specific);
isNeo.setFromToml(general, "isPS4Pro", is_game_specific);
isDevKit.setFromToml(general, "isDevKit", is_game_specific);
if (is_game_specific) { // do not get this value from the base config
extraDmemInMbytes.setFromToml(general, "extraDmemInMbytes", is_game_specific);
}
isPSNSignedIn.setFromToml(general, "isPSNSignedIn", is_game_specific);
isTrophyPopupDisabled.setFromToml(general, "isTrophyPopupDisabled", is_game_specific);
trophyNotificationDuration.setFromToml(general, "trophyNotificationDuration",
@ -828,13 +876,10 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
userName.setFromToml(general, "userName", is_game_specific);
isShowSplash.setFromToml(general, "showSplash", is_game_specific);
isSideTrophy.setFromToml(general, "sideTrophy", is_game_specific);
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", compatibilityData);
checkCompatibilityOnStartup = toml::find_or<bool>(general, "checkCompatibilityOnStartup",
checkCompatibilityOnStartup);
isConnectedToNetwork.setFromToml(general, "isConnectedToNetwork", is_game_specific);
chooseHomeTab.setFromToml(general, "chooseHomeTab", is_game_specific);
defaultControllerID.setFromToml(general, "defaultControllerID", is_game_specific);
sys_modules_path = toml::find_fs_path_or(general, "sysModulesPath", sys_modules_path);
}
if (data.contains("Input")) {
@ -847,6 +892,7 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
isMotionControlsEnabled.setFromToml(input, "isMotionControlsEnabled", is_game_specific);
useUnifiedInputConfig.setFromToml(input, "useUnifiedInputConfig", is_game_specific);
backgroundControllerInput.setFromToml(input, "backgroundControllerInput", is_game_specific);
usbDeviceBackend.setFromToml(input, "usbDeviceBackend", is_game_specific);
}
if (data.contains("Audio")) {
@ -886,6 +932,7 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
gpuId.setFromToml(vk, "gpuId", is_game_specific);
vkValidation.setFromToml(vk, "validation", is_game_specific);
vkValidationCore.setFromToml(vk, "validation_core", is_game_specific);
vkValidationSync.setFromToml(vk, "validation_sync", is_game_specific);
vkValidationGpu.setFromToml(vk, "validation_gpu", is_game_specific);
vkCrashDiagnostic.setFromToml(vk, "crashDiagnostic", is_game_specific);
@ -909,8 +956,6 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
if (data.contains("GUI")) {
const toml::value& gui = data.at("GUI");
load_game_size = toml::find_or<bool>(gui, "loadGameSizeEnabled", load_game_size);
const auto install_dir_array =
toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {});
@ -955,8 +1000,8 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
void sortTomlSections(toml::ordered_value& data) {
toml::ordered_value ordered_data;
std::vector<string> section_order = {"General", "Input", "GPU", "Vulkan",
"Debug", "Keys", "GUI", "Settings"};
std::vector<string> section_order = {"General", "Input", "Audio", "GPU", "Vulkan",
"Debug", "Keys", "GUI", "Settings"};
for (const auto& section : section_order) {
if (data.contains(section)) {
@ -1014,11 +1059,13 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
logFilter.setTomlValue(data, "General", "logFilter", is_game_specific);
logType.setTomlValue(data, "General", "logType", is_game_specific);
userName.setTomlValue(data, "General", "userName", is_game_specific);
chooseHomeTab.setTomlValue(data, "General", "chooseHomeTab", is_game_specific);
isShowSplash.setTomlValue(data, "General", "showSplash", is_game_specific);
isSideTrophy.setTomlValue(data, "General", "sideTrophy", is_game_specific);
isNeo.setTomlValue(data, "General", "isPS4Pro", is_game_specific);
isDevKit.setTomlValue(data, "General", "isDevKit", is_game_specific);
if (is_game_specific) {
extraDmemInMbytes.setTomlValue(data, "General", "extraDmemInMbytes", is_game_specific);
}
isPSNSignedIn.setTomlValue(data, "General", "isPSNSignedIn", is_game_specific);
isConnectedToNetwork.setTomlValue(data, "General", "isConnectedToNetwork", is_game_specific);
@ -1028,6 +1075,7 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
is_game_specific);
backgroundControllerInput.setTomlValue(data, "Input", "backgroundControllerInput",
is_game_specific);
usbDeviceBackend.setTomlValue(data, "Input", "usbDeviceBackend", is_game_specific);
micDevice.setTomlValue(data, "Audio", "micDevice", is_game_specific);
mainOutputDevice.setTomlValue(data, "Audio", "mainOutputDevice", is_game_specific);
@ -1053,6 +1101,8 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
gpuId.setTomlValue(data, "Vulkan", "gpuId", is_game_specific);
vkValidation.setTomlValue(data, "Vulkan", "validation", is_game_specific);
vkValidationSync.setTomlValue(data, "Vulkan", "validation_sync", is_game_specific);
vkValidationCore.setTomlValue(data, "Vulkan", "validation_core", is_game_specific);
vkValidationGpu.setTomlValue(data, "Vulkan", "validation_gpu", is_game_specific);
vkCrashDiagnostic.setTomlValue(data, "Vulkan", "crashDiagnostic", is_game_specific);
vkHostMarkers.setTomlValue(data, "Vulkan", "hostMarkers", is_game_specific);
vkGuestMarkers.setTomlValue(data, "Vulkan", "guestMarkers", is_game_specific);
@ -1098,12 +1148,10 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
// Non game-specific entries
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
data["General"]["compatibilityEnabled"] = compatibilityData;
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
data["General"]["sysModulesPath"] = string{fmt::UTF(sys_modules_path.u8string()).data};
data["GUI"]["installDirs"] = install_dirs;
data["GUI"]["installDirsEnabled"] = install_dirs_enabled;
data["GUI"]["saveDataPath"] = string{fmt::UTF(save_data_path.u8string()).data};
data["GUI"]["loadGameSizeEnabled"] = load_game_size;
data["GUI"]["addonInstallDir"] =
string{fmt::UTF(settings_addon_install_dir.u8string()).data};
data["Debug"]["ConfigVersion"] = config_version;
@ -1117,7 +1165,6 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
data["GPU"]["internalScreenWidth"] = internalScreenWidth.base_value;
data["GPU"]["internalScreenHeight"] = internalScreenHeight.base_value;
data["GPU"]["patchShaders"] = shouldPatchShaders.base_value;
data["Vulkan"]["validation_gpu"] = vkValidationGpu.base_value;
data["Debug"]["FPSColor"] = isFpsColor.base_value;
}
@ -1141,6 +1188,7 @@ void setDefaultValues(bool is_game_specific) {
isPSNSignedIn.set(false, is_game_specific);
isConnectedToNetwork.set(false, is_game_specific);
directMemoryAccessEnabled.set(false, is_game_specific);
extraDmemInMbytes.set(0, is_game_specific);
}
// Entries with game-specific settings that are in both the game-specific and global GUI
@ -1151,7 +1199,6 @@ void setDefaultValues(bool is_game_specific) {
logFilter.set("", is_game_specific);
logType.set("sync", is_game_specific);
userName.set("shadPS4", is_game_specific);
chooseHomeTab.set("General", is_game_specific);
isShowSplash.set(false, is_game_specific);
isSideTrophy.set("right", is_game_specific);
@ -1160,6 +1207,7 @@ void setDefaultValues(bool is_game_specific) {
cursorHideTimeout.set(5, is_game_specific);
isMotionControlsEnabled.set(true, is_game_specific);
backgroundControllerInput.set(false, is_game_specific);
usbDeviceBackend.set(UsbBackendType::Real, is_game_specific);
// GS - Audio
micDevice.set("Default Device", is_game_specific);
@ -1182,6 +1230,7 @@ void setDefaultValues(bool is_game_specific) {
// GS - Vulkan
gpuId.set(-1, is_game_specific);
vkValidation.set(false, is_game_specific);
vkValidationCore.set(true, is_game_specific);
vkValidationSync.set(false, is_game_specific);
vkValidationGpu.set(false, is_game_specific);
vkCrashDiagnostic.set(false, is_game_specific);
@ -1203,8 +1252,6 @@ void setDefaultValues(bool is_game_specific) {
// General
enableDiscordRPC = false;
compatibilityData = false;
checkCompatibilityOnStartup = false;
// Input
useSpecialPad.base_value = false;
@ -1223,9 +1270,6 @@ void setDefaultValues(bool is_game_specific) {
internalScreenWidth.base_value = 1280;
internalScreenHeight.base_value = 720;
// GUI
load_game_size = true;
// Debug
isFpsColor.base_value = true;
}

View File

@ -9,6 +9,13 @@
namespace Config {
enum class ConfigMode {
Default,
Global,
Clean,
};
void setConfigMode(ConfigMode mode);
struct GameInstallDir {
std::filesystem::path path;
bool enabled;
@ -20,6 +27,8 @@ void load(const std::filesystem::path& path, bool is_game_specific = false);
void save(const std::filesystem::path& path, bool is_game_specific = false);
void resetGameSpecificValue(std::string entry);
bool getGameRunning();
void setGameRunning(bool running);
int getVolumeSlider();
void setVolumeSlider(int volumeValue, bool is_game_specific = false);
std::string getTrophyKey();
@ -72,6 +81,10 @@ bool vkValidationEnabled();
void setVkValidation(bool enable, bool is_game_specific = false);
bool vkValidationSyncEnabled();
void setVkSyncValidation(bool enable, bool is_game_specific = false);
bool vkValidationGpuEnabled();
void setVkGpuValidation(bool enable, bool is_game_specific = false);
bool vkValidationCoreEnabled();
void setVkCoreValidation(bool enable, bool is_game_specific = false);
bool getVkCrashDiagnosticEnabled();
void setVkCrashDiagnosticEnabled(bool enable, bool is_game_specific = false);
bool getVkHostMarkersEnabled();
@ -90,11 +103,10 @@ double getTrophyNotificationDuration();
void setTrophyNotificationDuration(double newTrophyNotificationDuration,
bool is_game_specific = false);
int getCursorHideTimeout();
void setCursorHideTimeout(int newcursorHideTimeout);
std::string getMainOutputDevice();
void setMainOutputDevice(std::string device);
void setMainOutputDevice(std::string device, bool is_game_specific = false);
std::string getPadSpkOutputDevice();
void setPadSpkOutputDevice(std::string device);
void setPadSpkOutputDevice(std::string device, bool is_game_specific = false);
std::string getMicDevice();
void setCursorHideTimeout(int newcursorHideTimeout, bool is_game_specific = false);
void setMicDevice(std::string device, bool is_game_specific = false);
@ -115,7 +127,8 @@ void setNeoMode(bool enable, bool is_game_specific = false);
bool isDevKitConsole();
void setDevKitConsole(bool enable, bool is_game_specific = false);
bool vkValidationGpuEnabled(); // no set
int getExtraDmemInMbytes();
void setExtraDmemInMbytes(int value, bool is_game_specific = false);
bool getIsMotionControlsEnabled();
void setIsMotionControlsEnabled(bool use, bool is_game_specific = false);
std::string getDefaultControllerID();
@ -133,16 +146,18 @@ void setRcasAttenuation(int value, bool is_game_specific = false);
bool getIsConnectedToNetwork();
void setConnectedToNetwork(bool enable, bool is_game_specific = false);
void setUserName(const std::string& name, bool is_game_specific = false);
void setChooseHomeTab(const std::string& type, bool is_game_specific = false);
std::filesystem::path getSysModulesPath();
void setSysModulesPath(const std::filesystem::path& path);
bool getLoadAutoPatches();
void setLoadAutoPatches(bool enable);
enum UsbBackendType : int { Real, SkylandersPortal, InfinityBase, DimensionsToypad };
int getUsbDeviceBackend();
void setUsbDeviceBackend(int value, bool is_game_specific = false);
// TODO
bool GetLoadGameSizeEnabled();
std::filesystem::path GetSaveDataPath();
void setLoadGameSizeEnabled(bool enable);
bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup();
std::string getUserName();
std::string getChooseHomeTab();
bool GetUseUnifiedInputConfig();
void SetUseUnifiedInputConfig(bool use);
bool GetOverrideControllerColor();
@ -152,8 +167,6 @@ void SetControllerCustomColor(int r, int b, int g);
void setGameInstallDirs(const std::vector<std::filesystem::path>& dirs_config);
void setAllGameInstallDirs(const std::vector<GameInstallDir>& dirs_config);
void setSaveDataPath(const std::filesystem::path& path);
void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use);
// Gui
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled = true);
void removeGameInstallDir(const std::filesystem::path& dir);

View File

@ -61,8 +61,9 @@ private:
*/
class FileBackend {
public:
explicit FileBackend(const std::filesystem::path& filename)
: file{filename, FS::FileAccessMode::Write, FS::FileType::TextFile} {}
explicit FileBackend(const std::filesystem::path& filename, bool should_append = false)
: file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Write,
FS::FileType::TextFile} {}
~FileBackend() = default;
@ -145,6 +146,11 @@ public:
initialization_in_progress_suppress_logging = false;
}
static void ResetInstance() {
initialization_in_progress_suppress_logging = true;
instance.reset();
}
static bool IsActive() {
return instance != nullptr;
}
@ -157,6 +163,10 @@ public:
instance->StopBackendThread();
}
static void SetAppend() {
should_append = true;
}
Impl(const Impl&) = delete;
Impl& operator=(const Impl&) = delete;
@ -218,7 +228,7 @@ public:
private:
Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
: filter{filter_}, file_backend{file_backend_filename} {}
: filter{filter_}, file_backend{file_backend_filename, should_append} {}
~Impl() = default;
@ -264,6 +274,7 @@ private:
}
static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
static inline bool should_append{false};
Filter filter;
DebuggerBackend debugger_backend{};
@ -292,6 +303,11 @@ void Stop() {
Impl::Stop();
}
void Denitializer() {
Impl::Stop();
Impl::ResetInstance();
}
void SetGlobalFilter(const Filter& filter) {
Impl::Instance().SetGlobalFilter(filter);
}
@ -300,6 +316,10 @@ void SetColorConsoleBackendEnabled(bool enabled) {
Impl::Instance().SetColorConsoleBackendEnabled(enabled);
}
void SetAppend() {
Impl::SetAppend();
}
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
unsigned int line_num, const char* function, const char* format,
const fmt::format_args& args) {

View File

@ -21,9 +21,14 @@ void Start();
/// Explictily stops the logger thread and flushes the buffers
void Stop();
/// Closes log files and stops the logger
void Denitializer();
/// The global filter will prevent any messages from even being processed if they are filtered.
void SetGlobalFilter(const Filter& filter);
void SetColorConsoleBackendEnabled(bool enabled);
void SetAppend();
} // namespace Common::Log

View File

@ -109,6 +109,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, NpTrophy) \
SUB(Lib, NpWebApi) \
SUB(Lib, NpProfileDialog) \
SUB(Lib, NpSnsFacebookDialog) \
SUB(Lib, Screenshot) \
SUB(Lib, LibCInternal) \
SUB(Lib, AppContent) \

View File

@ -29,99 +29,100 @@ enum class Level : u8 {
* filter.cpp.
*/
enum class Class : u8 {
Log, ///< Messages about the log system itself
Common, ///< Library routines
Common_Filesystem, ///< Filesystem interface library
Common_Memory, ///< Memory mapping and management functions
Core, ///< LLE emulation core
Core_Linker, ///< The module linker
Core_Devices, ///< Devices emulation
Config, ///< Emulator configuration (including commandline)
Debug, ///< Debugging tools
Kernel, ///< The HLE implementation of the PS4 kernel.
Kernel_Pthread, ///< The pthread implementation of the kernel.
Kernel_Fs, ///< The filesystem implementation of the kernel.
Kernel_Vmm, ///< The virtual memory implementation of the kernel.
Kernel_Event, ///< The event management implementation of the kernel.
Kernel_Sce, ///< The sony specific interfaces provided by the kernel.
Lib, ///< HLE implementation of system library. Each major library
///< should have its own subclass.
Lib_LibC, ///< The LibC implementation.
Lib_LibcInternal, ///< The LibcInternal implementation.
Lib_Kernel, ///< The LibKernel implementation.
Lib_Pad, ///< The LibScePad implementation.
Lib_SystemGesture, ///< The LibSceSystemGesture implementation.
Lib_GnmDriver, ///< The LibSceGnmDriver implementation.
Lib_SystemService, ///< The LibSceSystemService implementation.
Lib_UserService, ///< The LibSceUserService implementation.
Lib_VideoOut, ///< The LibSceVideoOut implementation.
Lib_CommonDlg, ///< The LibSceCommonDialog implementation.
Lib_MsgDlg, ///< The LibSceMsgDialog implementation.
Lib_AudioOut, ///< The LibSceAudioOut implementation.
Lib_AudioIn, ///< The LibSceAudioIn implementation.
Lib_Move, ///< The LibSceMove implementation.
Lib_Net, ///< The LibSceNet implementation.
Lib_NetCtl, ///< The LibSceNetCtl implementation.
Lib_SaveData, ///< The LibSceSaveData implementation.
Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation.
Lib_Ssl, ///< The LibSceSsl implementation.
Lib_Ssl2, ///< The LibSceSsl2 implementation.
Lib_Http, ///< The LibSceHttp implementation.
Lib_Http2, ///< The LibSceHttp2 implementation.
Lib_SysModule, ///< The LibSceSysModule implementation
Lib_NpCommon, ///< The LibSceNpCommon implementation
Lib_NpAuth, ///< The LibSceNpAuth implementation
Lib_NpManager, ///< The LibSceNpManager implementation
Lib_NpScore, ///< The LibSceNpScore implementation
Lib_NpTrophy, ///< The LibSceNpTrophy implementation
Lib_NpWebApi, ///< The LibSceWebApi implementation
Lib_NpProfileDialog, ///< The LibSceNpProfileDialog implementation
Lib_Screenshot, ///< The LibSceScreenshot implementation
Lib_LibCInternal, ///< The LibCInternal implementation.
Lib_AppContent, ///< The LibSceAppContent implementation.
Lib_Rtc, ///< The LibSceRtc implementation.
Lib_DiscMap, ///< The LibSceDiscMap implementation.
Lib_Png, ///< The LibScePng implementation.
Lib_Jpeg, ///< The LibSceJpeg implementation.
Lib_PlayGo, ///< The LibScePlayGo implementation.
Lib_PlayGoDialog, ///< The LibScePlayGoDialog implementation.
Lib_Random, ///< The LibSceRandom implementation.
Lib_Usbd, ///< The LibSceUsbd implementation.
Lib_Ajm, ///< The LibSceAjm implementation.
Lib_ErrorDialog, ///< The LibSceErrorDialog implementation.
Lib_ImeDialog, ///< The LibSceImeDialog implementation.
Lib_AvPlayer, ///< The LibSceAvPlayer implementation.
Lib_Ngs2, ///< The LibSceNgs2 implementation.
Lib_Audio3d, ///< The LibSceAudio3d implementation.
Lib_Ime, ///< The LibSceIme implementation
Lib_GameLiveStreaming, ///< The LibSceGameLiveStreaming implementation
Lib_Remoteplay, ///< The LibSceRemotePlay implementation
Lib_SharePlay, ///< The LibSceSharePlay implemenation
Lib_Fiber, ///< The LibSceFiber implementation.
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
Lib_Videodec, ///< The LibSceVideodec implementation.
Lib_Voice, ///< The LibSceVoice implementation.
Lib_RazorCpu, ///< The LibRazorCpu implementation.
Lib_Mouse, ///< The LibSceMouse implementation
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
Lib_NpParty, ///< The LibSceNpParty implementation
Lib_Zlib, ///< The LibSceZlib implementation.
Lib_Hmd, ///< The LibSceHmd implementation.
Lib_HmdSetupDialog, ///< The LibSceHmdSetupDialog implementation.
Lib_SigninDialog, ///< The LibSigninDialog implementation.
Lib_Camera, ///< The LibCamera implementation.
Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
Lib_CompanionUtil, ///< The LibCompanionUtil implementation.
Lib_VrTracker, ///< The LibSceVrTracker implementation.
Frontend, ///< Emulator UI
Render, ///< Video Core
Render_Vulkan, ///< Vulkan backend
Render_Recompiler, ///< Shader recompiler
ImGui, ///< ImGui
Loader, ///< ROM loader
Input, ///< Input emulation
Tty, ///< Debug output from emu
Count ///< Total number of logging classes
Log, ///< Messages about the log system itself
Common, ///< Library routines
Common_Filesystem, ///< Filesystem interface library
Common_Memory, ///< Memory mapping and management functions
Core, ///< LLE emulation core
Core_Linker, ///< The module linker
Core_Devices, ///< Devices emulation
Config, ///< Emulator configuration (including commandline)
Debug, ///< Debugging tools
Kernel, ///< The HLE implementation of the PS4 kernel.
Kernel_Pthread, ///< The pthread implementation of the kernel.
Kernel_Fs, ///< The filesystem implementation of the kernel.
Kernel_Vmm, ///< The virtual memory implementation of the kernel.
Kernel_Event, ///< The event management implementation of the kernel.
Kernel_Sce, ///< The sony specific interfaces provided by the kernel.
Lib, ///< HLE implementation of system library. Each major library
///< should have its own subclass.
Lib_LibC, ///< The LibC implementation.
Lib_LibcInternal, ///< The LibcInternal implementation.
Lib_Kernel, ///< The LibKernel implementation.
Lib_Pad, ///< The LibScePad implementation.
Lib_SystemGesture, ///< The LibSceSystemGesture implementation.
Lib_GnmDriver, ///< The LibSceGnmDriver implementation.
Lib_SystemService, ///< The LibSceSystemService implementation.
Lib_UserService, ///< The LibSceUserService implementation.
Lib_VideoOut, ///< The LibSceVideoOut implementation.
Lib_CommonDlg, ///< The LibSceCommonDialog implementation.
Lib_MsgDlg, ///< The LibSceMsgDialog implementation.
Lib_AudioOut, ///< The LibSceAudioOut implementation.
Lib_AudioIn, ///< The LibSceAudioIn implementation.
Lib_Move, ///< The LibSceMove implementation.
Lib_Net, ///< The LibSceNet implementation.
Lib_NetCtl, ///< The LibSceNetCtl implementation.
Lib_SaveData, ///< The LibSceSaveData implementation.
Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation.
Lib_Ssl, ///< The LibSceSsl implementation.
Lib_Ssl2, ///< The LibSceSsl2 implementation.
Lib_Http, ///< The LibSceHttp implementation.
Lib_Http2, ///< The LibSceHttp2 implementation.
Lib_SysModule, ///< The LibSceSysModule implementation
Lib_NpCommon, ///< The LibSceNpCommon implementation
Lib_NpAuth, ///< The LibSceNpAuth implementation
Lib_NpManager, ///< The LibSceNpManager implementation
Lib_NpScore, ///< The LibSceNpScore implementation
Lib_NpTrophy, ///< The LibSceNpTrophy implementation
Lib_NpWebApi, ///< The LibSceWebApi implementation
Lib_NpProfileDialog, ///< The LibSceNpProfileDialog implementation
Lib_NpSnsFacebookDialog, ///< The LibSceNpSnsFacebookDialog implementation
Lib_Screenshot, ///< The LibSceScreenshot implementation
Lib_LibCInternal, ///< The LibCInternal implementation.
Lib_AppContent, ///< The LibSceAppContent implementation.
Lib_Rtc, ///< The LibSceRtc implementation.
Lib_DiscMap, ///< The LibSceDiscMap implementation.
Lib_Png, ///< The LibScePng implementation.
Lib_Jpeg, ///< The LibSceJpeg implementation.
Lib_PlayGo, ///< The LibScePlayGo implementation.
Lib_PlayGoDialog, ///< The LibScePlayGoDialog implementation.
Lib_Random, ///< The LibSceRandom implementation.
Lib_Usbd, ///< The LibSceUsbd implementation.
Lib_Ajm, ///< The LibSceAjm implementation.
Lib_ErrorDialog, ///< The LibSceErrorDialog implementation.
Lib_ImeDialog, ///< The LibSceImeDialog implementation.
Lib_AvPlayer, ///< The LibSceAvPlayer implementation.
Lib_Ngs2, ///< The LibSceNgs2 implementation.
Lib_Audio3d, ///< The LibSceAudio3d implementation.
Lib_Ime, ///< The LibSceIme implementation
Lib_GameLiveStreaming, ///< The LibSceGameLiveStreaming implementation
Lib_Remoteplay, ///< The LibSceRemotePlay implementation
Lib_SharePlay, ///< The LibSceSharePlay implemenation
Lib_Fiber, ///< The LibSceFiber implementation.
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
Lib_Videodec, ///< The LibSceVideodec implementation.
Lib_Voice, ///< The LibSceVoice implementation.
Lib_RazorCpu, ///< The LibRazorCpu implementation.
Lib_Mouse, ///< The LibSceMouse implementation
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
Lib_NpParty, ///< The LibSceNpParty implementation
Lib_Zlib, ///< The LibSceZlib implementation.
Lib_Hmd, ///< The LibSceHmd implementation.
Lib_HmdSetupDialog, ///< The LibSceHmdSetupDialog implementation.
Lib_SigninDialog, ///< The LibSigninDialog implementation.
Lib_Camera, ///< The LibCamera implementation.
Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
Lib_CompanionUtil, ///< The LibCompanionUtil implementation.
Lib_VrTracker, ///< The LibSceVrTracker implementation.
Frontend, ///< Emulator UI
Render, ///< Video Core
Render_Vulkan, ///< Vulkan backend
Render_Recompiler, ///< Shader recompiler
ImGui, ///< ImGui
Loader, ///< ROM loader
Input, ///< Input emulation
Tty, ///< Debug output from emu
Count ///< Total number of logging classes
};
} // namespace Common::Log

View File

@ -3,20 +3,12 @@
#include <algorithm>
#include <codecvt>
#include <fstream>
#include <sstream>
#include <string>
#include <nlohmann/json.hpp>
#include <pugixml.hpp>
#ifdef ENABLE_QT_GUI
#include <QDir>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QListView>
#include <QMessageBox>
#include <QString>
#include <QXmlStreamReader>
#endif
#include "common/config.h"
#include "common/elf_info.h"
#include "common/logging/log.h"
#include "common/path_util.h"
@ -28,7 +20,7 @@ namespace MemoryPatcher {
EXPORT uintptr_t g_eboot_address;
uint64_t g_eboot_image_size;
std::string g_game_serial;
std::string patchFile;
std::string patch_file;
bool patches_applied = false;
std::vector<patchInfo> pending_patches;
@ -122,256 +114,104 @@ std::string convertValueToHex(const std::string type, const std::string valueStr
void ApplyPendingPatches();
void OnGameLoaded() {
if (!patchFile.empty()) {
std::filesystem::path patchDir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir);
void ApplyPatchesFromXML(std::filesystem::path path) {
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(path.c_str());
auto filePath = (patchDir / patchFile).native();
auto* param_sfo = Common::Singleton<PSF>::Instance();
auto app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(filePath.c_str());
if (result) {
auto patchXML = doc.child("Patch");
for (pugi::xml_node_iterator it = patchXML.children().begin();
it != patchXML.children().end(); ++it) {
auto* param_sfo = Common::Singleton<PSF>::Instance();
auto app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
if (std::string(it->name()) == "Metadata") {
if (std::string(it->attribute("isEnabled").value()) == "true") {
std::string currentPatchName = it->attribute("Name").value();
std::string metadataAppVer = it->attribute("AppVer").value();
bool versionMatches = metadataAppVer == app_version;
if (result) {
auto patchXML = doc.child("Patch");
for (pugi::xml_node_iterator it = patchXML.children().begin();
it != patchXML.children().end(); ++it) {
auto patchList = it->first_child();
for (pugi::xml_node_iterator patchLineIt = patchList.children().begin();
patchLineIt != patchList.children().end(); ++patchLineIt) {
if (std::string(it->name()) == "Metadata") {
if (std::string(it->attribute("isEnabled").value()) == "true") {
std::string currentPatchName = it->attribute("Name").value();
std::string metadataAppVer = it->attribute("AppVer").value();
bool versionMatches = metadataAppVer == app_version;
std::string type = patchLineIt->attribute("Type").value();
if (!versionMatches && type != "mask" && type != "mask_jump32")
continue;
auto patchList = it->first_child();
for (pugi::xml_node_iterator patchLineIt = patchList.children().begin();
patchLineIt != patchList.children().end(); ++patchLineIt) {
std::string type = patchLineIt->attribute("Type").value();
if (!versionMatches && type != "mask" && type != "mask_jump32")
continue;
std::string currentPatchName = it->attribute("Name").value();
for (pugi::xml_node_iterator patchLineIt = patchList.children().begin();
patchLineIt != patchList.children().end(); ++patchLineIt) {
std::string type = patchLineIt->attribute("Type").value();
std::string address = patchLineIt->attribute("Address").value();
std::string patchValue = patchLineIt->attribute("Value").value();
std::string maskOffsetStr =
patchLineIt->attribute("Offset").value();
std::string targetStr = "";
std::string sizeStr = "";
if (type == "mask_jump32") {
targetStr = patchLineIt->attribute("Target").value();
sizeStr = patchLineIt->attribute("Size").value();
} else {
patchValue = convertValueToHex(type, patchValue);
}
bool littleEndian = false;
if (type == "bytes16" || type == "bytes32" || type == "bytes64") {
littleEndian = true;
}
MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
int maskOffsetValue = 0;
if (type == "mask")
patchMask = MemoryPatcher::PatchMask::Mask;
if (type == "mask_jump32")
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
if ((type == "mask" || type == "mask_jump32") &&
!maskOffsetStr.empty()) {
maskOffsetValue = std::stoi(maskOffsetStr, 0, 10);
}
MemoryPatcher::PatchMemory(currentPatchName, address, patchValue,
targetStr, sizeStr, false, littleEndian,
patchMask, maskOffsetValue);
}
std::string address = patchLineIt->attribute("Address").value();
std::string patchValue = patchLineIt->attribute("Value").value();
std::string maskOffsetStr = patchLineIt->attribute("Offset").value();
std::string targetStr = "";
std::string sizeStr = "";
if (type == "mask_jump32") {
targetStr = patchLineIt->attribute("Target").value();
sizeStr = patchLineIt->attribute("Size").value();
} else {
patchValue = convertValueToHex(type, patchValue);
}
bool littleEndian = false;
if (type == "bytes16" || type == "bytes32" || type == "bytes64") {
littleEndian = true;
}
MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
int maskOffsetValue = 0;
if (type == "mask")
patchMask = MemoryPatcher::PatchMask::Mask;
if (type == "mask_jump32")
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
if ((type == "mask" || type == "mask_jump32") && !maskOffsetStr.empty()) {
maskOffsetValue = std::stoi(maskOffsetStr, 0, 10);
}
MemoryPatcher::PatchMemory(currentPatchName, address, patchValue, targetStr,
sizeStr, false, littleEndian, patchMask,
maskOffsetValue);
}
}
}
}
} else {
LOG_ERROR(Loader, "Could not parse patch XML: {}", result.description());
}
}
void OnGameLoaded() {
std::filesystem::path patch_dir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir);
if (!patch_file.empty()) {
auto file_path = (patch_dir / patch_file).native();
if (std::filesystem::exists(patch_file)) {
ApplyPatchesFromXML(patch_file);
} else {
LOG_ERROR(Loader, "couldnt patch parse xml : {}", result.description());
ApplyPatchesFromXML(file_path);
}
} else if (Config::getLoadAutoPatches()) {
for (auto const& repo : std::filesystem::directory_iterator(patch_dir)) {
if (!repo.is_directory()) {
continue;
}
std::ifstream json_file{repo.path() / "files.json"};
nlohmann::json available_patches = nlohmann::json::parse(json_file);
std::filesystem::path game_patch_file;
for (auto const& [filename, serials] : available_patches.items()) {
if (std::find(serials.begin(), serials.end(), g_game_serial) != serials.end()) {
game_patch_file = repo.path() / filename;
break;
}
}
if (std::filesystem::exists(game_patch_file)) {
ApplyPatchesFromXML(game_patch_file);
}
}
}
ApplyPendingPatches();
#ifdef ENABLE_QT_GUI
// We use the QT headers for the xml and json parsing, this define is only true on QT builds
QString patchDir;
Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
QDir dir(patchDir);
QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString& folder : folders) {
QString filesJsonPath = patchDir + "/" + folder + "/files.json";
QFile jsonFile(filesJsonPath);
if (!jsonFile.open(QIODevice::ReadOnly)) {
LOG_ERROR(Loader, "Unable to open files.json for reading in repository {}",
folder.toStdString());
continue;
}
QByteArray jsonData = jsonFile.readAll();
jsonFile.close();
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
QJsonObject jsonObject = jsonDoc.object();
QString selectedFileName;
QString serial = QString::fromStdString(g_game_serial);
for (auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); ++it) {
QString filePath = it.key();
QJsonArray idsArray = it.value().toArray();
if (idsArray.contains(QJsonValue(serial))) {
selectedFileName = filePath;
break;
}
}
if (selectedFileName.isEmpty()) {
LOG_ERROR(Loader, "No patch file found for the current serial in repository {}",
folder.toStdString());
continue;
}
QString filePath = patchDir + "/" + folder + "/" + selectedFileName;
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
LOG_ERROR(Loader, "Unable to open the file for reading.");
continue;
}
QByteArray xmlData = file.readAll();
file.close();
QString newXmlData;
QXmlStreamReader xmlReader(xmlData);
bool isEnabled = false;
std::string currentPatchName;
auto* param_sfo = Common::Singleton<PSF>::Instance();
auto app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
bool versionMatches = true;
while (!xmlReader.atEnd()) {
xmlReader.readNext();
if (xmlReader.isStartElement()) {
QJsonArray patchLines;
if (xmlReader.name() == QStringLiteral("Metadata")) {
QString name = xmlReader.attributes().value("Name").toString();
currentPatchName = name.toStdString();
QString appVer = xmlReader.attributes().value("AppVer").toString();
// Check and update the isEnabled attribute
isEnabled = false;
for (const QXmlStreamAttribute& attr : xmlReader.attributes()) {
if (attr.name() == QStringLiteral("isEnabled")) {
isEnabled = (attr.value().toString() == "true");
}
}
versionMatches = (appVer.toStdString() == app_version);
} else if (xmlReader.name() == QStringLiteral("PatchList")) {
QJsonArray linesArray;
while (!xmlReader.atEnd() &&
!(xmlReader.tokenType() == QXmlStreamReader::EndElement &&
xmlReader.name() == QStringLiteral("PatchList"))) {
xmlReader.readNext();
if (xmlReader.tokenType() == QXmlStreamReader::StartElement &&
xmlReader.name() == QStringLiteral("Line")) {
QXmlStreamAttributes attributes = xmlReader.attributes();
QJsonObject lineObject;
lineObject["Type"] = attributes.value("Type").toString();
lineObject["Address"] = attributes.value("Address").toString();
lineObject["Value"] = attributes.value("Value").toString();
lineObject["Offset"] = attributes.value("Offset").toString();
if (lineObject["Type"].toString() == "mask_jump32") {
lineObject["Target"] = attributes.value("Target").toString();
lineObject["Size"] = attributes.value("Size").toString();
}
linesArray.append(lineObject);
}
}
patchLines = linesArray;
if (isEnabled) {
foreach (const QJsonValue& value, patchLines) {
QJsonObject lineObject = value.toObject();
QString type = lineObject["Type"].toString();
if ((type != "mask" && type != "mask_jump32") && !versionMatches)
continue;
QString address = lineObject["Address"].toString();
QString patchValue = lineObject["Value"].toString();
QString maskOffsetStr = lineObject["Offset"].toString();
QString targetStr;
QString sizeStr;
if (type == "mask_jump32") {
targetStr = lineObject["Target"].toString();
sizeStr = lineObject["Size"].toString();
} else {
patchValue = QString::fromStdString(convertValueToHex(
type.toStdString(), patchValue.toStdString()));
}
bool littleEndian = false;
if (type == "bytes16" || type == "bytes32" || type == "bytes64")
littleEndian = true;
MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
int maskOffsetValue = 0;
if (type == "mask")
patchMask = MemoryPatcher::PatchMask::Mask;
if (type == "mask_jump32")
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
if ((type == "mask" || type == "mask_jump32") &&
!maskOffsetStr.toStdString().empty()) {
maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10);
}
MemoryPatcher::PatchMemory(
currentPatchName, address.toStdString(), patchValue.toStdString(),
targetStr.toStdString(), sizeStr.toStdString(), false, littleEndian,
patchMask, maskOffsetValue);
}
}
}
}
}
if (xmlReader.hasError()) {
LOG_ERROR(Loader, "Failed to parse XML for {}", g_game_serial);
} else {
LOG_INFO(Loader, "Patches loaded successfully, repository: {}", folder.toStdString());
}
ApplyPendingPatches();
}
#endif
}
void AddPatchToQueue(patchInfo patchToAdd) {

View File

@ -17,7 +17,7 @@ namespace MemoryPatcher {
extern EXPORT uintptr_t g_eboot_address;
extern uint64_t g_eboot_image_size;
extern std::string g_game_serial;
extern std::string patchFile;
extern std::string patch_file;
enum PatchMask : uint8_t {
None,

View File

@ -1,20 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <half.hpp>
#include "common/number_utils.h"
#include "video_core/amdgpu/pixel_format.h"
#include "video_core/amdgpu/types.h"
#define UF11_EXPONENT_SHIFT 6
#define UF10_EXPONENT_SHIFT 5
#define RGB9E5_MANTISSA_BITS 9
#define RGB9E5_EXP_BIAS 1
#define F32_INFINITY 0x7f800000
constexpr u32 UF11_EXPONENT_SHIFT = 6;
constexpr u32 UF10_EXPONENT_SHIFT = 5;
constexpr u32 RGB9E5_MANTISSA_BITS = 9;
constexpr u32 RGB9E5_EXP_BIAS = 1;
constexpr u32 F32_INFINITY = 0x7f800000;
namespace NumberUtils {

View File

@ -25,10 +25,6 @@
#endif
#endif
#ifdef ENABLE_QT_GUI
#include <QString>
#endif
namespace Common::FS {
namespace fs = std::filesystem;
@ -88,13 +84,6 @@ static std::optional<std::filesystem::path> GetBundleParentDirectory() {
#endif
static auto UserPaths = [] {
#if defined(__APPLE__) && defined(ENABLE_QT_GUI)
// Set the current path to the directory containing the app bundle.
if (const auto bundle_dir = GetBundleParentDirectory()) {
std::filesystem::current_path(*bundle_dir);
}
#endif
// Try the portable user directory first.
auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
if (!std::filesystem::exists(user_dir)) {
@ -229,22 +218,4 @@ std::optional<fs::path> FindGameByID(const fs::path& dir, const std::string& gam
return std::nullopt;
}
#ifdef ENABLE_QT_GUI
void PathToQString(QString& result, const std::filesystem::path& path) {
#ifdef _WIN32
result = QString::fromStdWString(path.wstring());
#else
result = QString::fromStdString(path.string());
#endif
}
std::filesystem::path PathFromQString(const QString& path) {
#ifdef _WIN32
return std::filesystem::path(path.toStdWString());
#else
return std::filesystem::path(path.toStdString());
#endif
}
#endif
} // namespace Common::FS

View File

@ -7,10 +7,6 @@
#include <optional>
#include <vector>
#ifdef ENABLE_QT_GUI
class QString; // to avoid including <QString> in this header
#endif
namespace Common::FS {
enum class PathType {
@ -99,25 +95,6 @@ constexpr auto LOG_FILE = "shad_log.txt";
*/
void SetUserPath(PathType user_path, const std::filesystem::path& new_path);
#ifdef ENABLE_QT_GUI
/**
* Converts an std::filesystem::path to a QString.
* The native underlying string of a path is wstring on Windows and string on POSIX.
*
* @param result The resulting QString
* @param path The path to convert
*/
void PathToQString(QString& result, const std::filesystem::path& path);
/**
* Converts a QString to an std::filesystem::path.
* The native underlying string of a path is wstring on Windows and string on POSIX.
*
* @param path The path to convert
*/
[[nodiscard]] std::filesystem::path PathFromQString(const QString& path);
#endif
/**
* Recursively searches for a game directory by its ID.
* Limits search depth to prevent excessive filesystem traversal.

View File

@ -2,10 +2,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <map>
#include <boost/icl/separate_interval_set.hpp>
#include "common/alignment.h"
#include "common/arch.h"
#include "common/assert.h"
#include "common/config.h"
#include "common/error.h"
#include "core/address_space.h"
#include "core/libraries/kernel/memory.h"
@ -23,22 +23,70 @@
// Reserve space for the system address space using a zerofill section.
asm(".zerofill SYSTEM_MANAGED,SYSTEM_MANAGED,__SYSTEM_MANAGED,0x7FFBFC000");
asm(".zerofill SYSTEM_RESERVED,SYSTEM_RESERVED,__SYSTEM_RESERVED,0x7C0004000");
asm(".zerofill USER_AREA,USER_AREA,__USER_AREA,0x5F9000000000");
#endif
namespace Core {
static constexpr size_t BackingSize = SCE_KERNEL_TOTAL_MEM_PRO;
// Constants used for mapping address space.
constexpr VAddr SYSTEM_MANAGED_MIN = 0x400000ULL;
constexpr VAddr SYSTEM_MANAGED_MAX = 0x7FFFFBFFFULL;
constexpr VAddr SYSTEM_RESERVED_MIN = 0x7FFFFC000ULL;
#if defined(__APPLE__) && defined(ARCH_X86_64)
// Commpage ranges from 0xFC0000000 - 0xFFFFFFFFF, so decrease the system reserved maximum.
constexpr VAddr SYSTEM_RESERVED_MAX = 0xFBFFFFFFFULL;
// GPU-reserved memory ranges from 0x1000000000 - 0x6FFFFFFFFF, so increase the user minimum.
constexpr VAddr USER_MIN = 0x7000000000ULL;
#else
constexpr VAddr SYSTEM_RESERVED_MAX = 0xFFFFFFFFFULL;
constexpr VAddr USER_MIN = 0x1000000000ULL;
#endif
#if defined(__linux__)
// Linux maps the shadPS4 executable around here, so limit the user maximum
constexpr VAddr USER_MAX = 0x54FFFFFFFFFFULL;
#else
constexpr VAddr USER_MAX = 0x5FFFFFFFFFFFULL;
#endif
// Constants for the sizes of the ranges in address space.
static constexpr u64 SystemManagedSize = SYSTEM_MANAGED_MAX - SYSTEM_MANAGED_MIN + 1;
static constexpr u64 SystemReservedSize = SYSTEM_RESERVED_MAX - SYSTEM_RESERVED_MIN + 1;
static constexpr u64 UserSize = USER_MAX - USER_MIN + 1;
// Required backing file size for mapping physical address space.
static u64 BackingSize = ORBIS_KERNEL_TOTAL_MEM_DEV_PRO;
#ifdef _WIN32
[[nodiscard]] constexpr u64 ToWindowsProt(Core::MemoryProt prot) {
if (True(prot & Core::MemoryProt::CpuReadWrite) ||
True(prot & Core::MemoryProt::GpuReadWrite)) {
return PAGE_READWRITE;
} else if (True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead)) {
return PAGE_READONLY;
const bool read =
True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead);
const bool write =
True(prot & Core::MemoryProt::CpuWrite) || True(prot & Core::MemoryProt::GpuWrite);
const bool execute = True(prot & Core::MemoryProt::CpuExec);
if (write && !read) {
// While write-only CPU mappings aren't possible, write-only GPU mappings are.
LOG_WARNING(Core, "Converting write-only mapping to read-write");
}
// All cases involving execute permissions have separate permissions.
if (execute) {
if (write) {
return PAGE_EXECUTE_READWRITE;
} else if (read && !write) {
return PAGE_EXECUTE_READ;
} else {
return PAGE_EXECUTE;
}
} else {
return PAGE_NOACCESS;
if (write) {
return PAGE_READWRITE;
} else if (read && !write) {
return PAGE_READONLY;
} else {
return PAGE_NOACCESS;
}
}
}
@ -50,70 +98,100 @@ struct MemoryRegion {
struct AddressSpace::Impl {
Impl() : process{GetCurrentProcess()} {
// Allocate virtual address placeholder for our address space.
MEM_ADDRESS_REQUIREMENTS req{};
MEM_EXTENDED_PARAMETER param{};
req.LowestStartingAddress = reinterpret_cast<PVOID>(SYSTEM_MANAGED_MIN);
// The ending address must align to page boundary - 1
// https://stackoverflow.com/questions/54223343/virtualalloc2-with-memextendedparameteraddressrequirements-always-produces-error
req.HighestEndingAddress = reinterpret_cast<PVOID>(USER_MIN + UserSize - 1);
req.Alignment = 0;
param.Type = MemExtendedParameterAddressRequirements;
param.Pointer = &req;
// Determine the system's page alignment
SYSTEM_INFO sys_info{};
GetSystemInfo(&sys_info);
u64 alignment = sys_info.dwAllocationGranularity;
// Typically, lower parts of system managed area is already reserved in windows.
// If reservation fails attempt again by reducing the area size a little bit.
// System managed is about 31GB in size so also cap the number of times we can reduce it
// to a reasonable amount.
static constexpr size_t ReductionOnFail = 1_GB;
static constexpr size_t MaxReductions = 10;
// Determine the host OS build number
// Retrieve module handle for ntdll
auto ntdll_handle = GetModuleHandleW(L"ntdll.dll");
ASSERT_MSG(ntdll_handle, "Failed to retrieve ntdll handle");
size_t virtual_size = SystemManagedSize + SystemReservedSize + UserSize;
for (u32 i = 0; i < MaxReductions; i++) {
virtual_base = static_cast<u8*>(VirtualAlloc2(process, NULL, virtual_size,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, &param, 1));
if (virtual_base) {
break;
}
virtual_size -= ReductionOnFail;
// Get the RtlGetVersion function
s64(WINAPI * RtlGetVersion)(LPOSVERSIONINFOW);
*(FARPROC*)&RtlGetVersion = GetProcAddress(ntdll_handle, "RtlGetVersion");
ASSERT_MSG(RtlGetVersion, "failed to retrieve function pointer for RtlGetVersion");
// Call RtlGetVersion
RTL_OSVERSIONINFOW os_version_info{};
RtlGetVersion(&os_version_info);
u64 supported_user_max = USER_MAX;
// This is the build number for Windows 11 22H2
static constexpr s32 AffectedBuildNumber = 22621;
if (os_version_info.dwBuildNumber <= AffectedBuildNumber) {
// Older Windows builds have an issue with VirtualAlloc2 on higher addresses.
// To prevent regressions, limit the maximum address we reserve for this platform.
supported_user_max = 0x11000000000ULL;
LOG_WARNING(Core, "Windows 10 detected, reducing user max to {:#x} to avoid problems",
supported_user_max);
}
ASSERT_MSG(virtual_base, "Unable to reserve virtual address space: {}",
Common::GetLastErrorMsg());
// Determine the free address ranges we can access.
VAddr next_addr = SYSTEM_MANAGED_MIN;
MEMORY_BASIC_INFORMATION info{};
while (next_addr <= supported_user_max) {
ASSERT_MSG(VirtualQuery(reinterpret_cast<PVOID>(next_addr), &info, sizeof(info)),
"Failed to query memory information for address {:#x}", next_addr);
// Ensure logic uses values aligned to bage boundaries.
next_addr = reinterpret_cast<VAddr>(info.BaseAddress) + info.RegionSize;
next_addr = Common::AlignUp(next_addr, alignment);
// Prevent size from going past supported_user_max
u64 size = info.RegionSize;
if (next_addr > supported_user_max) {
size -= (next_addr - supported_user_max);
}
size = Common::AlignDown(size, alignment);
// Check for free memory areas
// Restrict region size to avoid overly fragmenting the virtual memory space.
if (info.State == MEM_FREE && info.RegionSize > 0x1000000) {
VAddr addr = Common::AlignUp(reinterpret_cast<VAddr>(info.BaseAddress), alignment);
regions.emplace(addr, MemoryRegion{addr, size, false});
}
}
// Reserve all detected free regions.
for (auto region : regions) {
auto addr = static_cast<u8*>(VirtualAlloc2(
process, reinterpret_cast<PVOID>(region.second.base), region.second.size,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, NULL, 0));
// All marked regions should reserve fine since they're free.
ASSERT_MSG(addr, "Unable to reserve virtual address space: {}",
Common::GetLastErrorMsg());
}
// Set these constants to ensure code relying on them works.
// These do not fully encapsulate the state of the address space.
system_managed_base = reinterpret_cast<u8*>(regions.begin()->first);
system_managed_size = SystemManagedSize - (regions.begin()->first - SYSTEM_MANAGED_MIN);
system_reserved_base = reinterpret_cast<u8*>(SYSTEM_RESERVED_MIN);
system_reserved_size = SystemReservedSize;
system_managed_base = virtual_base;
system_managed_size = system_reserved_base - virtual_base;
user_base = reinterpret_cast<u8*>(USER_MIN);
user_size = virtual_base + virtual_size - user_base;
user_size = supported_user_max - USER_MIN - 1;
LOG_INFO(Kernel_Vmm, "System managed virtual memory region: {} - {}",
fmt::ptr(system_managed_base),
fmt::ptr(system_managed_base + system_managed_size - 1));
LOG_INFO(Kernel_Vmm, "System reserved virtual memory region: {} - {}",
fmt::ptr(system_reserved_base),
fmt::ptr(system_reserved_base + system_reserved_size - 1));
LOG_INFO(Kernel_Vmm, "User virtual memory region: {} - {}", fmt::ptr(user_base),
fmt::ptr(user_base + user_size - 1));
// Initializer placeholder tracker
const uintptr_t system_managed_addr = reinterpret_cast<uintptr_t>(system_managed_base);
regions.emplace(system_managed_addr,
MemoryRegion{system_managed_addr, virtual_size, false});
// Increase BackingSize to account for config options.
BackingSize += Config::getExtraDmemInMbytes() * 1_MB;
// Allocate backing file that represents the total physical memory.
backing_handle =
CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
PAGE_READWRITE, SEC_COMMIT, BackingSize, nullptr, nullptr, 0);
backing_handle = CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_ALL_ACCESS,
PAGE_EXECUTE_READWRITE, SEC_COMMIT, BackingSize,
nullptr, nullptr, 0);
ASSERT_MSG(backing_handle, "{}", Common::GetLastErrorMsg());
// Allocate a virtual memory for the backing file map as placeholder
backing_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, BackingSize,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, nullptr, 0));
ASSERT_MSG(backing_base, "{}", Common::GetLastErrorMsg());
// Map backing placeholder. This will commit the pages
void* const ret = MapViewOfFile3(backing_handle, process, backing_base, 0, BackingSize,
MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
void* const ret =
MapViewOfFile3(backing_handle, process, backing_base, 0, BackingSize,
MEM_REPLACE_PLACEHOLDER, PAGE_EXECUTE_READWRITE, nullptr, 0);
ASSERT_MSG(ret == backing_base, "{}", Common::GetLastErrorMsg());
}
@ -154,7 +232,12 @@ struct AddressSpace::Impl {
ASSERT_MSG(ret, "VirtualProtect failed. {}", Common::GetLastErrorMsg());
} else {
ptr = MapViewOfFile3(backing, process, reinterpret_cast<PVOID>(virtual_addr),
phys_addr, size, MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0);
phys_addr, size, MEM_REPLACE_PLACEHOLDER,
PAGE_EXECUTE_READWRITE, nullptr, 0);
ASSERT_MSG(ptr, "MapViewOfFile3 failed. {}", Common::GetLastErrorMsg());
DWORD resultvar;
bool ret = VirtualProtect(ptr, size, prot, &resultvar);
ASSERT_MSG(ret, "VirtualProtect failed. {}", Common::GetLastErrorMsg());
}
} else {
ptr =
@ -296,17 +379,33 @@ struct AddressSpace::Impl {
void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) {
DWORD new_flags{};
if (read && write && execute) {
new_flags = PAGE_EXECUTE_READWRITE;
} else if (read && write) {
new_flags = PAGE_READWRITE;
} else if (read && !write) {
new_flags = PAGE_READONLY;
} else if (execute && !read && !write) {
new_flags = PAGE_EXECUTE;
} else if (!read && !write && !execute) {
new_flags = PAGE_NOACCESS;
if (write && !read) {
// While write-only CPU protection isn't possible, write-only GPU protection is.
LOG_WARNING(Core, "Converting write-only protection to read-write");
}
// All cases involving execute permissions have separate permissions.
if (execute) {
// If there's some form of write protection requested, provide read-write permissions.
if (write) {
new_flags = PAGE_EXECUTE_READWRITE;
} else if (read && !write) {
new_flags = PAGE_EXECUTE_READ;
} else {
new_flags = PAGE_EXECUTE;
}
} else {
if (write) {
new_flags = PAGE_READWRITE;
} else if (read && !write) {
new_flags = PAGE_READONLY;
} else {
new_flags = PAGE_NOACCESS;
}
}
// If no flags are assigned, then something's gone wrong.
if (new_flags == 0) {
LOG_CRITICAL(Common_Memory,
"Unsupported protection flag combination for address {:#x}, size {}, "
"read={}, write={}, execute={}",
@ -326,12 +425,20 @@ struct AddressSpace::Impl {
DWORD old_flags{};
if (!VirtualProtectEx(process, LPVOID(range_addr), range_size, new_flags, &old_flags)) {
UNREACHABLE_MSG(
"Failed to change virtual memory protection for address {:#x}, size {}",
"Failed to change virtual memory protection for address {:#x}, size {:#x}",
range_addr, range_size);
}
}
}
boost::icl::interval_set<VAddr> GetUsableRegions() {
boost::icl::interval_set<VAddr> reserved_regions;
for (auto region : regions) {
reserved_regions.insert({region.second.base, region.second.base + region.second.size});
}
return reserved_regions;
}
HANDLE process{};
HANDLE backing_handle{};
u8* backing_base{};
@ -356,62 +463,73 @@ enum PosixPageProtection {
};
[[nodiscard]] constexpr PosixPageProtection ToPosixProt(Core::MemoryProt prot) {
if (True(prot & Core::MemoryProt::CpuReadWrite) ||
True(prot & Core::MemoryProt::GpuReadWrite)) {
if (True(prot & Core::MemoryProt::CpuExec)) {
const bool read =
True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead);
const bool write =
True(prot & Core::MemoryProt::CpuWrite) || True(prot & Core::MemoryProt::GpuWrite);
const bool execute = True(prot & Core::MemoryProt::CpuExec);
if (write && !read) {
// While write-only CPU mappings aren't possible, write-only GPU mappings are.
LOG_WARNING(Core, "Converting write-only mapping to read-write");
}
// All cases involving execute permissions have separate permissions.
if (execute) {
if (write) {
return PAGE_EXECUTE_READWRITE;
} else {
return PAGE_READWRITE;
}
} else if (True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead)) {
if (True(prot & Core::MemoryProt::CpuExec)) {
} else if (read && !write) {
return PAGE_EXECUTE_READ;
} else {
return PAGE_READONLY;
return PAGE_EXECUTE;
}
} else {
return PAGE_NOACCESS;
if (write) {
return PAGE_READWRITE;
} else if (read && !write) {
return PAGE_READONLY;
} else {
return PAGE_NOACCESS;
}
}
}
struct AddressSpace::Impl {
Impl() {
BackingSize += Config::getExtraDmemInMbytes() * 1_MB;
// Allocate virtual address placeholder for our address space.
system_managed_size = SystemManagedSize;
system_reserved_size = SystemReservedSize;
user_size = UserSize;
constexpr int protection_flags = PROT_READ | PROT_WRITE;
constexpr int base_map_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
constexpr int map_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED;
#if defined(__APPLE__) && defined(ARCH_X86_64)
// On ARM64 Macs under Rosetta 2, we run into limitations due to the commpage from
// 0xFC0000000 - 0xFFFFFFFFF and the GPU carveout region from 0x1000000000 - 0x6FFFFFFFFF.
// We can allocate the system managed region, as well as system reserved if reduced in size
// slightly, but we cannot map the user region where we want, so we must let the OS put it
// wherever possible and hope the game won't rely on its location.
system_managed_base = reinterpret_cast<u8*>(
mmap(reinterpret_cast<void*>(SYSTEM_MANAGED_MIN), system_managed_size, protection_flags,
base_map_flags | MAP_FIXED, -1, 0));
system_reserved_base = reinterpret_cast<u8*>(
mmap(reinterpret_cast<void*>(SYSTEM_RESERVED_MIN), system_reserved_size,
protection_flags, base_map_flags | MAP_FIXED, -1, 0));
// Cannot guarantee enough space for these areas at the desired addresses, so not MAP_FIXED.
user_base = reinterpret_cast<u8*>(mmap(reinterpret_cast<void*>(USER_MIN), user_size,
protection_flags, base_map_flags, -1, 0));
// On ARM64 Macs, we run into limitations due to the commpage from 0xFC0000000 - 0xFFFFFFFFF
// and the GPU carveout region from 0x1000000000 - 0x6FFFFFFFFF. Because this creates gaps
// in the available virtual memory region, we map memory space using three distinct parts.
system_managed_base =
reinterpret_cast<u8*>(mmap(reinterpret_cast<void*>(SYSTEM_MANAGED_MIN),
system_managed_size, protection_flags, map_flags, -1, 0));
system_reserved_base =
reinterpret_cast<u8*>(mmap(reinterpret_cast<void*>(SYSTEM_RESERVED_MIN),
system_reserved_size, protection_flags, map_flags, -1, 0));
user_base = reinterpret_cast<u8*>(
mmap(reinterpret_cast<void*>(USER_MIN), user_size, protection_flags, map_flags, -1, 0));
#else
const auto virtual_size = system_managed_size + system_reserved_size + user_size;
#if defined(ARCH_X86_64)
const auto virtual_base =
reinterpret_cast<u8*>(mmap(reinterpret_cast<void*>(SYSTEM_MANAGED_MIN), virtual_size,
protection_flags, base_map_flags | MAP_FIXED, -1, 0));
protection_flags, map_flags, -1, 0));
system_managed_base = virtual_base;
system_reserved_base = reinterpret_cast<u8*>(SYSTEM_RESERVED_MIN);
user_base = reinterpret_cast<u8*>(USER_MIN);
#else
// Map memory wherever possible and instruction translation can handle offsetting to the
// base.
const auto virtual_base = reinterpret_cast<u8*>(
mmap(nullptr, virtual_size, protection_flags, base_map_flags, -1, 0));
const auto virtual_base =
reinterpret_cast<u8*>(mmap(nullptr, virtual_size, protection_flags, map_flags, -1, 0));
system_managed_base = virtual_base;
system_reserved_base = virtual_base + SYSTEM_RESERVED_MIN - SYSTEM_MANAGED_MIN;
user_base = virtual_base + USER_MIN - SYSTEM_MANAGED_MIN;
@ -579,9 +697,9 @@ void AddressSpace::Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VA
// the entire allocation and remap the portions outside of the requested unmapping range.
impl->Unmap(virtual_addr, size, has_backing && !readonly_file);
// TODO: Determine if any titles require partial unmapping support for flexible allocations.
// TODO: Determine if any titles require partial unmapping support for un-backed allocations.
ASSERT_MSG(has_backing || (start_in_vma == 0 && end_in_vma == size),
"Partial unmapping of flexible allocations is not supported");
"Partial unmapping of un-backed allocations is not supported");
if (start_in_vma != 0) {
Map(virtual_addr, start_in_vma, 0, phys_base, is_exec);
@ -602,4 +720,22 @@ void AddressSpace::Protect(VAddr virtual_addr, size_t size, MemoryPermission per
return impl->Protect(virtual_addr, size, read, write, execute);
}
boost::icl::interval_set<VAddr> AddressSpace::GetUsableRegions() {
#ifdef _WIN32
// On Windows, we need to obtain the accessible intervals from the implementation's regions.
return impl->GetUsableRegions();
#else
// On Linux and Mac, the memory space is fully represented by the three major regions
boost::icl::interval_set<VAddr> reserved_regions;
VAddr system_managed_addr = reinterpret_cast<VAddr>(system_managed_base);
VAddr system_reserved_addr = reinterpret_cast<VAddr>(system_reserved_base);
VAddr user_addr = reinterpret_cast<VAddr>(user_base);
reserved_regions.insert({system_managed_addr, system_managed_addr + system_managed_size});
reserved_regions.insert({system_reserved_addr, system_reserved_addr + system_reserved_size});
reserved_regions.insert({user_addr, user_addr + user_size});
return reserved_regions;
#endif
}
} // namespace Core

View File

@ -4,6 +4,7 @@
#pragma once
#include <memory>
#include <boost/icl/separate_interval_set.hpp>
#include "common/arch.h"
#include "common/enum.h"
#include "common/types.h"
@ -20,22 +21,6 @@ enum class MemoryPermission : u32 {
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
constexpr VAddr SYSTEM_MANAGED_MIN = 0x00000400000ULL;
constexpr VAddr SYSTEM_MANAGED_MAX = 0x07FFFFBFFFULL;
constexpr VAddr SYSTEM_RESERVED_MIN = 0x07FFFFC000ULL;
#if defined(__APPLE__) && defined(ARCH_X86_64)
// Can only comfortably reserve the first 0x7C0000000 of system reserved space.
constexpr VAddr SYSTEM_RESERVED_MAX = 0xFBFFFFFFFULL;
#else
constexpr VAddr SYSTEM_RESERVED_MAX = 0xFFFFFFFFFULL;
#endif
constexpr VAddr USER_MIN = 0x1000000000ULL;
constexpr VAddr USER_MAX = 0xFBFFFFFFFFULL;
static constexpr size_t SystemManagedSize = SYSTEM_MANAGED_MAX - SYSTEM_MANAGED_MIN + 1;
static constexpr size_t SystemReservedSize = SYSTEM_RESERVED_MAX - SYSTEM_RESERVED_MIN + 1;
static constexpr size_t UserSize = 1ULL << 40;
/**
* Represents the user virtual address space backed by a dmem memory block
*/
@ -100,6 +85,9 @@ public:
void Protect(VAddr virtual_addr, size_t size, MemoryPermission perms);
// Returns an interval set containing all usable regions.
boost::icl::interval_set<VAddr> GetUsableRegions();
private:
struct Impl;
std::unique_ptr<Impl> impl;

View File

@ -157,7 +157,7 @@ std::optional<RegDump*> DebugStateImpl::GetRegDump(uintptr_t base_addr, uintptr_
}
void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
const AmdGpu::Liverpool::Regs& regs) {
const AmdGpu::Regs& regs) {
std::scoped_lock lock{frame_dump_list_mutex};
auto dump = GetRegDump(base_addr, header_addr);
@ -170,15 +170,14 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
for (int i = 0; i < RegDump::MaxShaderStages; i++) {
if ((*dump)->regs.stage_enable.IsStageEnabled(i)) {
auto stage = (*dump)->regs.ProgramForStage(i);
if (stage->address_lo != 0) {
const auto& info = AmdGpu::Liverpool::SearchBinaryInfo(stage->Address<u32*>());
auto code = stage->Code();
if (stage->address) {
const auto params = AmdGpu::GetParams(*stage);
(*dump)->stages[i] = PipelineShaderProgramDump{
.name = Vulkan::PipelineCache::GetShaderName(Shader::StageFromIndex(i),
info.shader_hash),
.hash = info.shader_hash,
params.hash),
.hash = params.hash,
.user_data = *stage,
.code = std::vector<u32>{code.begin(), code.end()},
.code = std::vector<u32>{params.code.begin(), params.code.end()},
};
}
}
@ -198,12 +197,12 @@ void DebugStateImpl::PushRegsDumpCompute(uintptr_t base_addr, uintptr_t header_a
auto& cs = (*dump)->regs.cs_program;
cs = cs_state;
const auto& info = AmdGpu::Liverpool::SearchBinaryInfo(cs.Address<u32*>());
const auto params = AmdGpu::GetParams(cs);
(*dump)->cs_data = PipelineComputerProgramDump{
.name = Vulkan::PipelineCache::GetShaderName(Shader::Stage::Compute, info.shader_hash),
.hash = info.shader_hash,
.name = Vulkan::PipelineCache::GetShaderName(Shader::Stage::Compute, params.hash),
.hash = params.hash,
.cs_program = cs,
.code = std::vector<u32>{cs.Code().begin(), cs.Code().end()},
.code = std::vector<u32>{params.code.begin(), params.code.end()},
};
}

View File

@ -11,7 +11,9 @@
#include <queue>
#include "common/types.h"
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
#include "shader_recompiler/runtime_info.h"
#include "video_core/amdgpu/regs.h"
#include "video_core/renderer_vulkan/vk_common.h"
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
@ -54,21 +56,21 @@ struct QueueDump {
struct PipelineShaderProgramDump {
std::string name;
u64 hash;
Vulkan::Liverpool::ShaderProgram user_data{};
AmdGpu::ShaderProgram user_data{};
std::vector<u32> code{};
};
struct PipelineComputerProgramDump {
std::string name;
u64 hash;
Vulkan::Liverpool::ComputeProgram cs_program{};
AmdGpu::ComputeProgram cs_program{};
std::vector<u32> code{};
};
struct RegDump {
bool is_compute{false};
static constexpr size_t MaxShaderStages = 5;
Vulkan::Liverpool::Regs regs{};
AmdGpu::Regs regs;
std::array<PipelineShaderProgramDump, MaxShaderStages> stages{};
PipelineComputerProgramDump cs_data{};
};
@ -219,9 +221,8 @@ public:
void PushQueueDump(QueueDump dump);
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
const AmdGpu::Liverpool::Regs& regs);
using CsState = AmdGpu::Liverpool::ComputeProgram;
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, const AmdGpu::Regs& regs);
using CsState = AmdGpu::ComputeProgram;
void PushRegsDumpCompute(uintptr_t base_addr, uintptr_t header_addr, const CsState& cs_state);
void CollectShader(const std::string& name, Shader::LogicalStage l_stage,

99
src/core/debugger.cpp Normal file
View File

@ -0,0 +1,99 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "debugger.h"
#include <iostream>
#include <thread>
#if defined(_WIN32)
#include <Windows.h>
#include <debugapi.h>
#elif defined(__linux__)
#include <filesystem>
#include <fstream>
#elif defined(__APPLE__)
#include <errno.h>
#include <signal.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
#endif
bool Core::Debugger::IsDebuggerAttached() {
#if defined(_WIN32)
return IsDebuggerPresent();
#elif defined(__linux__)
std::ifstream status_file("/proc/self/status");
std::string line;
while (std::getline(status_file, line)) {
if (line.starts_with("TracerPid:")) {
std::string tracer_pid = line.substr(10);
tracer_pid.erase(0, tracer_pid.find_first_not_of(" \t"));
return tracer_pid != "0";
}
}
return false;
#elif defined(__APPLE__)
int mib[4];
struct kinfo_proc info;
size_t size = sizeof(info);
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
if (sysctl(mib, 4, &info, &size, nullptr, 0) == 0) {
return (info.kp_proc.p_flag & P_TRACED) != 0;
}
return false;
#else
#error "Unsupported platform"
#endif
}
void Core::Debugger::WaitForDebuggerAttach() {
int count = 0;
while (!IsDebuggerAttached()) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
if (--count <= 0) {
count = 10;
std::cerr << "Waiting for debugger to attach..." << std::endl;
}
}
}
int Core::Debugger::GetCurrentPid() {
#if defined(_WIN32)
return GetCurrentProcessId();
#elif defined(__APPLE__) || defined(__linux__)
return getpid();
#else
#error "Unsupported platform"
#endif
}
void Core::Debugger::WaitForPid(int pid) {
#if defined(_WIN32)
HANDLE process_handle = OpenProcess(SYNCHRONIZE, FALSE, pid);
if (process_handle != nullptr) {
std::cerr << "Waiting for process " << pid << " to exit..." << std::endl;
WaitForSingleObject(process_handle, INFINITE);
CloseHandle(process_handle);
}
#elif defined(__linux__)
std::string proc_path = "/proc/" + std::to_string(pid);
while (std::filesystem::exists(proc_path)) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cerr << "Waiting for process " << pid << " to exit..." << std::endl;
}
#elif defined(__APPLE__)
while (kill(pid, 0) == 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cerr << "Waiting for process " << pid << " to exit..." << std::endl;
}
#else
#error "Unsupported platform"
#endif
}

16
src/core/debugger.h Normal file
View File

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Core::Debugger {
bool IsDebuggerAttached();
void WaitForDebuggerAttach();
int GetCurrentPid();
void WaitForPid(int pid);
} // namespace Core::Debugger

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "layer.h"
@ -460,12 +460,12 @@ void L::Draw() {
}
void L::TextCentered(const std::string& text) {
float window_width = ImGui::GetWindowSize().x;
float text_width = ImGui::CalcTextSize(text.c_str()).x;
float window_width = GetWindowSize().x;
float text_width = CalcTextSize(text.c_str()).x;
float text_indentation = (window_width - text_width) * 0.5f;
ImGui::SameLine(text_indentation);
ImGui::Text("%s", text.c_str());
SameLine(text_indentation);
Text("%s", text.c_str());
}
namespace Overlay {

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@ -9,18 +9,20 @@
namespace Core::Devtools {
class Layer final : public ImGui::Layer {
static void DrawMenuBar();
static void DrawAdvanced();
static void DrawSimple();
public:
static void SetupSettings();
void Draw() override;
void TextCentered(const std::string& text);
// Must be inside a window
static void DrawNullGpuNotice();
private:
static void DrawMenuBar();
static void DrawAdvanced();
static void DrawSimple();
static void TextCentered(const std::string& text);
};
} // namespace Core::Devtools

View File

@ -0,0 +1,95 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "layer.h"
#include "imgui/imgui_std.h"
#include <imgui.h>
#include <imgui_internal.h>
#include <cmath>
#include <numbers>
namespace Core::Devtools {
void Layer::DrawNullGpuNotice() {
auto* window = ImGui::GetCurrentWindow();
if (window->SkipItems) {
return;
}
constexpr std::string_view mainNotice = "Null GPU is enabled";
constexpr std::string_view detailsNotice =
"Disable the nullGpu config to show the game display";
auto displaySize = window->Size;
ImVec2 targetSize = displaySize * 0.7f;
float minFontSize = 1.0f;
float maxFontSize = 200.0f;
float optimalFontSize = minFontSize;
static auto lastSize = ImVec2(-1, -1);
static float lastFontSize = -1.0f;
auto* font = ImGui::GetIO().Fonts->Fonts[IMGUI_FONT_TEXT_BIG];
if (lastSize != targetSize) {
while (maxFontSize - minFontSize > 0.1f) {
float testFontSize = (minFontSize + maxFontSize) / 2.0f;
ImVec2 textSize = font->CalcTextSizeA(testFontSize, FLT_MAX, 0.0f, &mainNotice.front(),
&mainNotice.back() + 1);
if (textSize.x <= targetSize.x && textSize.y <= targetSize.y) {
optimalFontSize = testFontSize;
minFontSize = testFontSize;
} else {
maxFontSize = testFontSize;
}
}
lastSize = targetSize;
lastFontSize = optimalFontSize;
} else {
optimalFontSize = lastFontSize;
}
ImVec2 textSize = font->CalcTextSizeA(optimalFontSize, FLT_MAX, 0.0f, &mainNotice.front(),
&mainNotice.back() + 1);
ImVec2 textPos = (displaySize - textSize) * 0.5f + window->Pos;
const float scale = optimalFontSize / font->FontSize;
double timeAnim = -std::numbers::pi * ImGui::GetTime();
int i = 0;
for (auto ch : mainNotice) {
double colorTime = sin(timeAnim + i * std::numbers::pi / 6.0) / 2.0 + 0.5;
int color = (int)(200 * colorTime) + 55;
double posTime = sin(timeAnim + i * std::numbers::pi / 15.0) / 2.0 + 0.5;
auto pos = textPos;
pos.y += 10.0 * (posTime < 0.5 ? std::pow(2.0, 20.0 * posTime - 10.0) / 2.0
: (2.0 - std::pow(2.0, -20.0 * posTime + 10.0)) / 2.0);
window->DrawList->AddText(font, optimalFontSize, pos, IM_COL32(color, color, color, 255),
&ch, &ch + 1);
textPos.x += font->FindGlyph(ch)->AdvanceX * scale;
i++;
}
font = ImGui::GetIO().Fonts->Fonts[IMGUI_FONT_TEXT];
textPos.y += textSize.y + 1.0;
optimalFontSize *= 0.2;
textSize = font->CalcTextSizeA(optimalFontSize, FLT_MAX, 0.0f, &detailsNotice.front(),
&detailsNotice.back() + 1);
textPos.x = window->Pos.x + (window->Size.x - textSize.x) * 0.5f;
window->DrawList->AddText(font, optimalFontSize, textPos, IM_COL32(200, 200, 200, 255),
&detailsNotice.front(), &detailsNotice.back() + 1);
}
} // namespace Core::Devtools

View File

@ -65,7 +65,7 @@ static HdrType GetNext(HdrType this_pm4, uint32_t n) {
}
void ParsePolygonControl(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<AmdGpu::Liverpool::PolygonControl const&>(value);
auto const reg = reinterpret_cast<AmdGpu::PolygonControl const&>(value);
if (!begin_table ||
BeginTable("PA_SU_SC_MODE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -73,80 +73,80 @@ void ParsePolygonControl(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("CULL_FRONT");
TableSetColumnIndex(1);
Text("%X", reg.cull_front.Value());
Text("%X", reg.cull_front);
TableNextRow();
TableSetColumnIndex(0);
Text("CULL_BACK");
TableSetColumnIndex(1);
Text("%X", reg.cull_back.Value());
Text("%X", reg.cull_back);
TableNextRow();
TableSetColumnIndex(0);
Text("FACE");
TableSetColumnIndex(1);
Text("%s", enum_name(reg.front_face.Value()).data());
Text("%s", enum_name(reg.front_face).data());
TableNextRow();
TableSetColumnIndex(0);
Text("POLY_MODE");
TableSetColumnIndex(1);
Text("%X", reg.enable_polygon_mode.Value());
Text("%X", reg.enable_polygon_mode);
TableNextRow();
TableSetColumnIndex(0);
Text("POLYMODE_FRONT_PTYPE");
TableSetColumnIndex(1);
Text("%s", enum_name(reg.polygon_mode_front.Value()).data());
Text("%s", enum_name(reg.polygon_mode_front).data());
TableNextRow();
TableSetColumnIndex(0);
Text("POLYMODE_BACK_PTYPE");
TableSetColumnIndex(1);
Text("%s", enum_name(reg.polygon_mode_back.Value()).data());
Text("%s", enum_name(reg.polygon_mode_back).data());
TableNextRow();
TableSetColumnIndex(0);
Text("POLY_OFFSET_FRONT_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.enable_polygon_offset_front.Value());
Text("%X", reg.enable_polygon_offset_front);
TableNextRow();
TableSetColumnIndex(0);
Text("POLY_OFFSET_BACK_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.enable_polygon_offset_back.Value());
Text("%X", reg.enable_polygon_offset_back);
TableNextRow();
TableSetColumnIndex(0);
Text("POLY_OFFSET_PARA_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.enable_polygon_offset_para.Value());
Text("%X", reg.enable_polygon_offset_para);
TableNextRow();
TableSetColumnIndex(0);
Text("VTX_WINDOW_OFFSET_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.enable_window_offset.Value());
Text("%X", reg.enable_window_offset);
TableNextRow();
TableSetColumnIndex(0);
Text("PROVOKING_VTX_LAST");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.provoking_vtx_last.Value(),
enum_name(reg.provoking_vtx_last.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.provoking_vtx_last),
enum_name(reg.provoking_vtx_last).data());
TableNextRow();
TableSetColumnIndex(0);
Text("PERSP_CORR_DIS");
TableSetColumnIndex(1);
Text("%X", reg.persp_corr_dis.Value());
Text("%X", reg.persp_corr_dis);
TableNextRow();
TableSetColumnIndex(0);
Text("MULTI_PRIM_IB_ENA");
TableSetColumnIndex(1);
Text("%X", reg.multi_prim_ib_ena.Value());
Text("%X", reg.multi_prim_ib_ena);
if (begin_table) {
EndTable();
@ -155,7 +155,7 @@ void ParsePolygonControl(u32 value, bool begin_table) {
}
void ParseAaConfig(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<Liverpool::AaConfig const&>(value);
auto const reg = reinterpret_cast<AmdGpu::AaConfig const&>(value);
if (!begin_table ||
BeginTable("PA_SC_AA_CONFIG", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -163,31 +163,31 @@ void ParseAaConfig(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("MSAA_NUM_SAMPLES");
TableSetColumnIndex(1);
Text("%X", reg.msaa_num_samples.Value());
Text("%X", reg.msaa_num_samples);
TableNextRow();
TableSetColumnIndex(0);
Text("AA_MASK_CENTROID_DTMN");
TableSetColumnIndex(1);
Text("%X", reg.aa_mask_centroid_dtmn.Value());
Text("%X", reg.aa_mask_centroid_dtmn);
TableNextRow();
TableSetColumnIndex(0);
Text("MAX_SAMPLE_DIST");
TableSetColumnIndex(1);
Text("%X", reg.max_sample_dst.Value());
Text("%X", reg.max_sample_dst);
TableNextRow();
TableSetColumnIndex(0);
Text("MSAA_EXPOSED_SAMPLES");
TableSetColumnIndex(1);
Text("%X", reg.msaa_exposed_samples.Value());
Text("%X", reg.msaa_exposed_samples);
TableNextRow();
TableSetColumnIndex(0);
Text("DETAIL_TO_EXPOSED_MODE");
TableSetColumnIndex(1);
Text("%X", reg.detail_to_exposed_mode.Value());
Text("%X", reg.detail_to_exposed_mode);
if (begin_table) {
EndTable();
@ -196,7 +196,7 @@ void ParseAaConfig(u32 value, bool begin_table) {
}
void ParseViewportControl(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<Liverpool::ViewportControl const&>(value);
auto const reg = reinterpret_cast<AmdGpu::ViewportControl const&>(value);
if (!begin_table ||
BeginTable("PA_CL_VTE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -204,61 +204,61 @@ void ParseViewportControl(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("VPORT_X_SCALE_ENA");
TableSetColumnIndex(1);
Text("%X", reg.xscale_enable.Value());
Text("%X", reg.xscale_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("VPORT_X_OFFSET_ENA");
TableSetColumnIndex(1);
Text("%X", reg.yoffset_enable.Value());
Text("%X", reg.yoffset_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("VPORT_Y_SCALE_ENA");
TableSetColumnIndex(1);
Text("%X", reg.yscale_enable.Value());
Text("%X", reg.yscale_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("VPORT_Y_OFFSET_ENA");
TableSetColumnIndex(1);
Text("%X", reg.yoffset_enable.Value());
Text("%X", reg.yoffset_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("VPORT_Z_SCALE_ENA");
TableSetColumnIndex(1);
Text("%X", reg.zscale_enable.Value());
Text("%X", reg.zscale_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("VPORT_Z_OFFSET_ENA");
TableSetColumnIndex(1);
Text("%X", reg.zoffset_enable.Value());
Text("%X", reg.zoffset_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("VTX_XY_FMT");
TableSetColumnIndex(1);
Text("%X", reg.xy_transformed.Value());
Text("%X", reg.xy_transformed);
TableNextRow();
TableSetColumnIndex(0);
Text("VTX_Z_FMT");
TableSetColumnIndex(1);
Text("%X", reg.z_transformed.Value());
Text("%X", reg.z_transformed);
TableNextRow();
TableSetColumnIndex(0);
Text("VTX_W0_FMT");
TableSetColumnIndex(1);
Text("%X", reg.w_transformed.Value());
Text("%X", reg.w_transformed);
TableNextRow();
TableSetColumnIndex(0);
Text("PERFCOUNTER_REF");
TableSetColumnIndex(1);
Text("%X", reg.perfcounter_ref.Value());
Text("%X", reg.perfcounter_ref);
if (begin_table) {
EndTable();
@ -267,7 +267,7 @@ void ParseViewportControl(u32 value, bool begin_table) {
}
void ParseColorControl(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<Liverpool::ColorControl const&>(value);
auto const reg = reinterpret_cast<AmdGpu::ColorControl const&>(value);
if (!begin_table ||
BeginTable("CB_COLOR_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -275,25 +275,25 @@ void ParseColorControl(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("DISABLE_DUAL_QUAD__VI");
TableSetColumnIndex(1);
Text("%X", reg.disable_dual_quad.Value());
Text("%X", reg.disable_dual_quad);
TableNextRow();
TableSetColumnIndex(0);
Text("DEGAMMA_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.degamma_enable.Value());
Text("%X", reg.degamma_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("MODE");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.mode.Value(), enum_name(reg.mode.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.mode), enum_name(reg.mode).data());
TableNextRow();
TableSetColumnIndex(0);
Text("ROP3");
TableSetColumnIndex(1);
Text("%X", static_cast<u32>(reg.rop3.Value()));
Text("%X", static_cast<u32>(reg.rop3));
if (begin_table) {
EndTable();
@ -302,7 +302,7 @@ void ParseColorControl(u32 value, bool begin_table) {
}
void ParseColor0Info(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<Liverpool::ColorBuffer::Color0Info const&>(value);
auto const reg = reinterpret_cast<AmdGpu::ColorBuffer::Color0Info const&>(value);
if (!begin_table ||
BeginTable("CB_COLOR_INFO", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -310,109 +310,109 @@ void ParseColor0Info(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("ENDIAN");
TableSetColumnIndex(1);
Text("%s", enum_name(reg.endian.Value()).data());
Text("%s", enum_name(reg.endian).data());
TableNextRow();
TableSetColumnIndex(0);
Text("FORMAT");
TableSetColumnIndex(1);
Text("%s", enum_name(reg.format.Value()).data());
Text("%s", enum_name(AmdGpu::DataFormat(reg.format)).data());
TableNextRow();
TableSetColumnIndex(0);
Text("LINEAR_GENERAL");
TableSetColumnIndex(1);
Text("%X", reg.linear_general.Value());
Text("%X", reg.linear_general);
TableNextRow();
TableSetColumnIndex(0);
Text("NUMBER_TYPE");
TableSetColumnIndex(1);
Text("%s", enum_name(reg.number_type.Value()).data());
Text("%s", enum_name(AmdGpu::NumberFormat(reg.number_type)).data());
TableNextRow();
TableSetColumnIndex(0);
Text("COMP_SWAP");
TableSetColumnIndex(1);
Text("%s", enum_name(reg.comp_swap.Value()).data());
Text("%s", enum_name(reg.comp_swap).data());
TableNextRow();
TableSetColumnIndex(0);
Text("FAST_CLEAR");
TableSetColumnIndex(1);
Text("%X", reg.fast_clear.Value());
Text("%X", reg.fast_clear);
TableNextRow();
TableSetColumnIndex(0);
Text("COMPRESSION");
TableSetColumnIndex(1);
Text("%X", reg.compression.Value());
Text("%X", reg.compression);
TableNextRow();
TableSetColumnIndex(0);
Text("BLEND_CLAMP");
TableSetColumnIndex(1);
Text("%X", reg.blend_clamp.Value());
Text("%X", reg.blend_clamp);
TableNextRow();
TableSetColumnIndex(0);
Text("BLEND_BYPASS");
TableSetColumnIndex(1);
Text("%X", reg.blend_bypass.Value());
Text("%X", reg.blend_bypass);
TableNextRow();
TableSetColumnIndex(0);
Text("SIMPLE_FLOAT");
TableSetColumnIndex(1);
Text("%X", reg.simple_float.Value());
Text("%X", reg.simple_float);
TableNextRow();
TableSetColumnIndex(0);
Text("ROUND_MODE");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.round_mode.Value(), enum_name(reg.round_mode.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.round_mode), enum_name(reg.round_mode).data());
TableNextRow();
TableSetColumnIndex(0);
Text("CMASK_IS_LINEAR");
TableSetColumnIndex(1);
Text("%X", reg.cmask_is_linear.Value());
Text("%X", reg.cmask_is_linear);
TableNextRow();
TableSetColumnIndex(0);
Text("BLEND_OPT_DONT_RD_DST");
TableSetColumnIndex(1);
Text("%X", reg.blend_opt_dont_rd_dst.Value());
Text("%X", reg.blend_opt_dont_rd_dst);
TableNextRow();
TableSetColumnIndex(0);
Text("BLEND_OPT_DISCARD_PIXEL");
TableSetColumnIndex(1);
Text("%X", reg.blend_opt_discard_pixel.Value());
Text("%X", reg.blend_opt_discard_pixel);
TableNextRow();
TableSetColumnIndex(0);
Text("FMASK_COMPRESSION_DISABLE__CI__VI");
TableSetColumnIndex(1);
Text("%X", reg.fmask_compression_disable_ci.Value());
Text("%X", reg.fmask_compression_disable_ci);
TableNextRow();
TableSetColumnIndex(0);
Text("FMASK_COMPRESS_1FRAG_ONLY__VI");
TableSetColumnIndex(1);
Text("%X", reg.fmask_compress_1frag_only.Value());
Text("%X", reg.fmask_compress_1frag_only);
TableNextRow();
TableSetColumnIndex(0);
Text("DCC_ENABLE__VI");
TableSetColumnIndex(1);
Text("%X", reg.dcc_enable.Value());
Text("%X", reg.dcc_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("CMASK_ADDR_TYPE__VI");
TableSetColumnIndex(1);
Text("%X", reg.cmask_addr_type.Value());
Text("%X", reg.cmask_addr_type);
if (begin_table) {
EndTable();
@ -421,7 +421,7 @@ void ParseColor0Info(u32 value, bool begin_table) {
}
void ParseColor0Attrib(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<Liverpool::ColorBuffer::Color0Attrib const&>(value);
auto const reg = reinterpret_cast<AmdGpu::ColorBuffer::Color0Attrib const&>(value);
if (!begin_table ||
BeginTable("CB_COLOR_ATTRIB", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -429,37 +429,37 @@ void ParseColor0Attrib(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("TILE_MODE_INDEX");
TableSetColumnIndex(1);
Text("%s", enum_name(reg.tile_mode_index.Value()).data());
Text("%s", enum_name(reg.tile_mode_index).data());
TableNextRow();
TableSetColumnIndex(0);
Text("FMASK_TILE_MODE_INDEX");
TableSetColumnIndex(1);
Text("%X", reg.fmask_tile_mode_index.Value());
Text("%X", reg.fmask_tile_mode_index);
TableNextRow();
TableSetColumnIndex(0);
Text("FMASK_BANK_HEIGHT");
TableSetColumnIndex(1);
Text("%X", reg.fmask_bank_height.Value());
Text("%X", reg.fmask_bank_height);
TableNextRow();
TableSetColumnIndex(0);
Text("NUM_SAMPLES");
TableSetColumnIndex(1);
Text("%X", reg.num_samples_log2.Value());
Text("%X", reg.num_samples_log2);
TableNextRow();
TableSetColumnIndex(0);
Text("NUM_FRAGMENTS");
TableSetColumnIndex(1);
Text("%X", reg.num_fragments_log2.Value());
Text("%X", reg.num_fragments_log2);
TableNextRow();
TableSetColumnIndex(0);
Text("FORCE_DST_ALPHA_1");
TableSetColumnIndex(1);
Text("%X", reg.force_dst_alpha_1.Value());
Text("%X", reg.force_dst_alpha_1);
if (begin_table) {
EndTable();
@ -468,7 +468,7 @@ void ParseColor0Attrib(u32 value, bool begin_table) {
}
void ParseBlendControl(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<Liverpool::BlendControl const&>(value);
auto const reg = reinterpret_cast<AmdGpu::BlendControl const&>(value);
if (!begin_table ||
BeginTable("CB_BLEND_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -476,59 +476,59 @@ void ParseBlendControl(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("COLOR_SRCBLEND");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.color_src_factor.Value(),
enum_name(reg.color_src_factor.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.color_src_factor),
enum_name(reg.color_src_factor).data());
TableNextRow();
TableSetColumnIndex(0);
Text("COLOR_COMB_FCN");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.color_func.Value(), enum_name(reg.color_func.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.color_func), enum_name(reg.color_func).data());
TableNextRow();
TableSetColumnIndex(0);
Text("COLOR_DESTBLEND");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.color_dst_factor.Value(),
enum_name(reg.color_dst_factor.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.color_dst_factor),
enum_name(reg.color_dst_factor).data());
TableNextRow();
TableSetColumnIndex(0);
Text("ALPHA_SRCBLEND");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.alpha_src_factor.Value(),
enum_name(reg.alpha_src_factor.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.alpha_src_factor),
enum_name(reg.alpha_src_factor).data());
TableNextRow();
TableSetColumnIndex(0);
Text("ALPHA_COMB_FCN");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.alpha_func.Value(), enum_name(reg.alpha_func.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.alpha_func), enum_name(reg.alpha_func).data());
TableNextRow();
TableSetColumnIndex(0);
Text("ALPHA_DESTBLEND");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.alpha_dst_factor.Value(),
enum_name(reg.alpha_dst_factor.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.alpha_dst_factor),
enum_name(reg.alpha_dst_factor).data());
TableNextRow();
TableSetColumnIndex(0);
Text("SEPARATE_ALPHA_BLEND");
TableSetColumnIndex(1);
Text("%X", reg.separate_alpha_blend.Value());
Text("%X", reg.separate_alpha_blend);
TableNextRow();
TableSetColumnIndex(0);
Text("ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.enable.Value());
Text("%X", reg.enable);
TableNextRow();
TableSetColumnIndex(0);
Text("DISABLE_ROP3");
TableSetColumnIndex(1);
Text("%X", reg.disable_rop3.Value());
Text("%X", reg.disable_rop3);
if (begin_table) {
EndTable();
@ -537,7 +537,7 @@ void ParseBlendControl(u32 value, bool begin_table) {
}
void ParseDepthRenderControl(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<Liverpool::DepthRenderControl const&>(value);
auto const reg = reinterpret_cast<AmdGpu::DepthRenderControl const&>(value);
if (!begin_table ||
BeginTable("DB_RENDER_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -545,61 +545,61 @@ void ParseDepthRenderControl(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("DEPTH_CLEAR_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.depth_clear_enable.Value());
Text("%X", reg.depth_clear_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("STENCIL_CLEAR_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.stencil_clear_enable.Value());
Text("%X", reg.stencil_clear_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("DEPTH_COPY");
TableSetColumnIndex(1);
Text("%X", reg.depth_clear_enable.Value());
Text("%X", reg.depth_clear_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("STENCIL_COPY");
TableSetColumnIndex(1);
Text("%X", reg.stencil_copy.Value());
Text("%X", reg.stencil_copy);
TableNextRow();
TableSetColumnIndex(0);
Text("RESUMMARIZE_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.resummarize_enable.Value());
Text("%X", reg.resummarize_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("STENCIL_COMPRESS_DISABLE");
TableSetColumnIndex(1);
Text("%X", reg.stencil_compress_disable.Value());
Text("%X", reg.stencil_compress_disable);
TableNextRow();
TableSetColumnIndex(0);
Text("DEPTH_COMPRESS_DISABLE");
TableSetColumnIndex(1);
Text("%X", reg.depth_compress_disable.Value());
Text("%X", reg.depth_compress_disable);
TableNextRow();
TableSetColumnIndex(0);
Text("COPY_CENTROID");
TableSetColumnIndex(1);
Text("%X", reg.copy_centroid.Value());
Text("%X", reg.copy_centroid);
TableNextRow();
TableSetColumnIndex(0);
Text("COPY_SAMPLE");
TableSetColumnIndex(1);
Text("%X", reg.copy_sample.Value());
Text("%X", reg.copy_sample);
TableNextRow();
TableSetColumnIndex(0);
Text("DECOMPRESS_ENABLE__VI");
TableSetColumnIndex(1);
Text("%X", reg.decompress_enable.Value());
Text("%X", reg.decompress_enable);
if (begin_table) {
EndTable();
@ -608,7 +608,7 @@ void ParseDepthRenderControl(u32 value, bool begin_table) {
}
void ParseDepthControl(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<Liverpool::DepthControl const&>(value);
auto const reg = reinterpret_cast<AmdGpu::DepthControl const&>(value);
if (!begin_table ||
BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -616,63 +616,63 @@ void ParseDepthControl(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("STENCIL_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.stencil_enable.Value());
Text("%X", reg.stencil_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("Z_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.depth_enable.Value());
Text("%X", reg.depth_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("Z_WRITE_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.depth_write_enable.Value());
Text("%X", reg.depth_write_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("DEPTH_BOUNDS_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.depth_bounds_enable.Value());
Text("%X", reg.depth_bounds_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("ZFUNC");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.depth_func.Value(), enum_name(reg.depth_func.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.depth_func), enum_name(reg.depth_func).data());
TableNextRow();
TableSetColumnIndex(0);
Text("BACKFACE_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.backface_enable.Value());
Text("%X", reg.backface_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("STENCILFUNC");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.stencil_ref_func.Value(),
enum_name(reg.stencil_ref_func.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.stencil_ref_func),
enum_name(reg.stencil_ref_func).data());
TableNextRow();
TableSetColumnIndex(0);
Text("STENCILFUNC_BF");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.stencil_bf_func.Value(),
enum_name(reg.stencil_bf_func.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.stencil_bf_func),
enum_name(reg.stencil_bf_func).data());
TableNextRow();
TableSetColumnIndex(0);
Text("ENABLE_COLOR_WRITES_ON_DEPTH_FAIL");
TableSetColumnIndex(1);
Text("%X", reg.enable_color_writes_on_depth_fail.Value());
Text("%X", reg.enable_color_writes_on_depth_fail);
TableNextRow();
TableSetColumnIndex(0);
Text("DISABLE_COLOR_WRITES_ON_DEPTH_PASS");
TableSetColumnIndex(1);
Text("%X", reg.disable_color_writes_on_depth_pass.Value());
Text("%X", reg.disable_color_writes_on_depth_pass);
if (begin_table) {
EndTable();
@ -681,7 +681,7 @@ void ParseDepthControl(u32 value, bool begin_table) {
}
void ParseEqaa(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<Liverpool::Eqaa const&>(value);
auto const reg = reinterpret_cast<AmdGpu::Eqaa const&>(value);
if (!begin_table ||
BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -689,73 +689,73 @@ void ParseEqaa(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("MAX_ANCHOR_SAMPLES");
TableSetColumnIndex(1);
Text("%X", reg.max_anchor_samples.Value());
Text("%X", reg.max_anchor_samples);
TableNextRow();
TableSetColumnIndex(0);
Text("PS_ITER_SAMPLES");
TableSetColumnIndex(1);
Text("%X", reg.ps_iter_samples.Value());
Text("%X", reg.ps_iter_samples);
TableNextRow();
TableSetColumnIndex(0);
Text("MASK_EXPORT_NUM_SAMPLES");
TableSetColumnIndex(1);
Text("%X", reg.mask_export_num_samples.Value());
Text("%X", reg.mask_export_num_samples);
TableNextRow();
TableSetColumnIndex(0);
Text("ALPHA_TO_MASK_NUM_SAMPLES");
TableSetColumnIndex(1);
Text("%X", reg.alpha_to_mask_num_samples.Value());
Text("%X", reg.alpha_to_mask_num_samples);
TableNextRow();
TableSetColumnIndex(0);
Text("HIGH_QUALITY_INTERSECTIONS");
TableSetColumnIndex(1);
Text("%X", reg.high_quality_intersections.Value());
Text("%X", reg.high_quality_intersections);
TableNextRow();
TableSetColumnIndex(0);
Text("INCOHERENT_EQAA_READS");
TableSetColumnIndex(1);
Text("%X", reg.incoherent_eqaa_reads.Value());
Text("%X", reg.incoherent_eqaa_reads);
TableNextRow();
TableSetColumnIndex(0);
Text("INTERPOLATE_COMP_Z");
TableSetColumnIndex(1);
Text("%X", reg.interpolate_comp_z.Value());
Text("%X", reg.interpolate_comp_z);
TableNextRow();
TableSetColumnIndex(0);
Text("INTERPOLATE_SRC_Z");
TableSetColumnIndex(1);
Text("%X", reg.interpolate_src_z.Value());
Text("%X", reg.interpolate_src_z);
TableNextRow();
TableSetColumnIndex(0);
Text("STATIC_ANCHOR_ASSOCIATIONS");
TableSetColumnIndex(1);
Text("%X", reg.static_anchor_associations.Value());
Text("%X", reg.static_anchor_associations);
TableNextRow();
TableSetColumnIndex(0);
Text("ALPHA_TO_MASK_EQAA_DISABLE");
TableSetColumnIndex(1);
Text("%X", reg.alpha_to_mask_eqaa_disable.Value());
Text("%X", reg.alpha_to_mask_eqaa_disable);
TableNextRow();
TableSetColumnIndex(0);
Text("OVERRASTERIZATION_AMOUNT");
TableSetColumnIndex(1);
Text("%X", reg.overrasterization_amount.Value());
Text("%X", reg.overrasterization_amount);
TableNextRow();
TableSetColumnIndex(0);
Text("ENABLE_POSTZ_OVERRASTERIZATION");
TableSetColumnIndex(1);
Text("%X", reg.enable_postz_overrasterization.Value());
Text("%X", reg.enable_postz_overrasterization);
if (begin_table) {
EndTable();
@ -764,7 +764,7 @@ void ParseEqaa(u32 value, bool begin_table) {
}
void ParseZInfo(u32 value, bool begin_table) {
auto const reg = reinterpret_cast<Liverpool::DepthBuffer::ZInfo const&>(value);
auto const reg = reinterpret_cast<AmdGpu::DepthBuffer::ZInfo const&>(value);
if (!begin_table ||
BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
@ -772,61 +772,61 @@ void ParseZInfo(u32 value, bool begin_table) {
TableSetColumnIndex(0);
Text("FORMAT");
TableSetColumnIndex(1);
Text("%X (%s)", (u32)reg.format.Value(), enum_name(reg.format.Value()).data());
Text("%X (%s)", static_cast<u32>(reg.format), enum_name(reg.format).data());
TableNextRow();
TableSetColumnIndex(0);
Text("NUM_SAMPLES");
TableSetColumnIndex(1);
Text("%X", reg.num_samples.Value());
Text("%X", reg.num_samples);
TableNextRow();
TableSetColumnIndex(0);
Text("TILE_SPLIT__CI__VI");
TableSetColumnIndex(1);
Text("%X", reg.tile_split.Value());
Text("%X", reg.tile_split);
TableNextRow();
TableSetColumnIndex(0);
Text("TILE_MODE_INDEX");
TableSetColumnIndex(1);
Text("%X", static_cast<u32>(reg.tile_mode_index.Value()));
Text("%X", static_cast<u32>(reg.tile_mode_index));
TableNextRow();
TableSetColumnIndex(0);
Text("DECOMPRESS_ON_N_ZPLANES__VI");
TableSetColumnIndex(1);
Text("%X", reg.decompress_on_n_zplanes.Value());
Text("%X", reg.decompress_on_n_zplanes);
TableNextRow();
TableSetColumnIndex(0);
Text("ALLOW_EXPCLEAR");
TableSetColumnIndex(1);
Text("%X", reg.allow_expclear.Value());
Text("%X", reg.allow_expclear);
TableNextRow();
TableSetColumnIndex(0);
Text("READ_SIZE");
TableSetColumnIndex(1);
Text("%X", reg.read_size.Value());
Text("%X", reg.read_size);
TableNextRow();
TableSetColumnIndex(0);
Text("TILE_SURFACE_ENABLE");
TableSetColumnIndex(1);
Text("%X", reg.tile_surface_en.Value());
Text("%X", reg.tile_surface_enable);
TableNextRow();
TableSetColumnIndex(0);
Text("CLEAR_DISALLOWED__VI");
TableSetColumnIndex(1);
Text("%X", reg.clear_disallowed.Value());
Text("%X", reg.clear_disallowed);
TableNextRow();
TableSetColumnIndex(0);
Text("ZRANGE_PRECISION");
TableSetColumnIndex(1);
Text("%X", reg.zrange_precision.Value());
Text("%X", reg.zrange_precision);
if (begin_table) {
EndTable();
@ -1515,4 +1515,4 @@ void CmdListViewer::Draw(bool only_batches_view, CmdListFilter& filter) {
PopID();
}
} // namespace Core::Devtools::Widget
} // namespace Core::Devtools::Widget

View File

@ -5,14 +5,13 @@
#pragma once
#include <memory>
#include <vector>
#include <imgui.h>
#include "common.h"
#include "common/types.h"
#include "imgui_memory_editor.h"
#include "reg_view.h"
#include "core/devtools/widget/imgui_memory_editor.h"
#include "core/devtools/widget/reg_view.h"
namespace AmdGpu {
union PM4Type3Header;

View File

@ -118,7 +118,8 @@ void FrameDumpViewer::Draw() {
SameLine();
BeginDisabled(selected_cmd == -1);
if (SmallButton("Dump cmd")) {
auto now_time = fmt::localtime(std::time(nullptr));
auto time = std::time(nullptr);
auto now_time = *std::localtime(&time);
const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time,
magic_enum::enum_name(selected_queue_type),
selected_submit_num, selected_queue_num2);

View File

@ -44,7 +44,7 @@ bool MemoryMapViewer::Iterator::DrawLine() {
return false;
}
auto m = dmem.it->second;
if (m.is_free) {
if (m.dma_type == DMAType::Free) {
++dmem.it;
return DrawLine();
}
@ -56,7 +56,7 @@ bool MemoryMapViewer::Iterator::DrawLine() {
auto type = static_cast<::Libraries::Kernel::MemoryTypes>(m.memory_type);
Text("%s", magic_enum::enum_name(type).data());
TableNextColumn();
Text("%d", m.is_pooled);
Text("%d", m.dma_type == DMAType::Pooled || m.dma_type == DMAType::Committed);
++dmem.it;
return true;
}

View File

@ -8,6 +8,7 @@
#include <mutex>
#include <string>
#include <vector>
#include "common/config.h"
#include "common/elf_info.h"
#include "common/path_util.h"
@ -22,7 +23,7 @@ public:
bool open = false;
static bool IsSystemModule(const std::filesystem::path& path) {
const auto sys_modules_path = Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir);
const auto sys_modules_path = Config::getSysModulesPath();
const auto abs_path = std::filesystem::absolute(path).lexically_normal();
const auto abs_sys_path = std::filesystem::absolute(sys_modules_path).lexically_normal();

View File

@ -16,7 +16,7 @@ using magic_enum::enum_name;
namespace Core::Devtools::Widget {
void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) {
void RegPopup::DrawColorBuffer(const AmdGpu::ColorBuffer& buffer) {
if (BeginTable("COLOR_BUFFER", 2, ImGuiTableFlags_Borders)) {
TableNextRow();
@ -36,7 +36,7 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) {
if (TreeNode("Color0Info")) {
TableNextRow();
TableNextColumn();
ParseColor0Info(buffer.info.u32all, false);
ParseColor0Info(buffer.info.raw, false);
TreePop();
}
@ -45,7 +45,7 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) {
if (TreeNode("Color0Attrib")) {
TableNextRow();
TableNextColumn();
ParseColor0Attrib(buffer.attrib.u32all, false);
ParseColor0Attrib(buffer.attrib.raw, false);
TreePop();
}
@ -75,9 +75,8 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) {
}
}
void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) {
const auto& [depth_buffer, depth_control] = depth_data;
void RegPopup::DrawDepthBuffer(const AmdGpu::DepthBuffer& buffer,
const AmdGpu::DepthControl control) {
SeparatorText("Depth buffer");
if (BeginTable("DEPTH_BUFFER", 2, ImGuiTableFlags_Borders)) {
@ -85,31 +84,31 @@ void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) {
// clang-format off
DrawValueRowList(
"Z_INFO.FORMAT", depth_buffer.z_info.format,
"Z_INFO.NUM_SAMPLES", depth_buffer.z_info.num_samples,
"Z_INFO.TILE_SPLIT", depth_buffer.z_info.tile_split,
"Z_INFO.TILE_MODE_INDEX", depth_buffer.z_info.tile_mode_index,
"Z_INFO.DECOMPRESS_ON_N_ZPLANES", depth_buffer.z_info.decompress_on_n_zplanes,
"Z_INFO.ALLOW_EXPCLEAR", depth_buffer.z_info.allow_expclear,
"Z_INFO.READ_SIZE", depth_buffer.z_info.read_size,
"Z_INFO.TILE_SURFACE_EN", depth_buffer.z_info.tile_surface_en,
"Z_INFO.CLEAR_DISALLOWED", depth_buffer.z_info.clear_disallowed,
"Z_INFO.ZRANGE_PRECISION", depth_buffer.z_info.zrange_precision,
"STENCIL_INFO.FORMAT", depth_buffer.stencil_info.format,
"Z_READ_BASE", depth_buffer.z_read_base,
"STENCIL_READ_BASE", depth_buffer.stencil_read_base,
"Z_WRITE_BASE", depth_buffer.z_write_base,
"STENCIL_WRITE_BASE", depth_buffer.stencil_write_base,
"DEPTH_SIZE.PITCH_TILE_MAX", depth_buffer.depth_size.pitch_tile_max,
"DEPTH_SIZE.HEIGHT_TILE_MAX", depth_buffer.depth_size.height_tile_max,
"DEPTH_SLICE.TILE_MAX", depth_buffer.depth_slice.tile_max,
"Pitch()", depth_buffer.Pitch(),
"Height()", depth_buffer.Height(),
"DepthAddress()", depth_buffer.DepthAddress(),
"StencilAddress()", depth_buffer.StencilAddress(),
"NumSamples()", depth_buffer.NumSamples(),
"NumBits()", depth_buffer.NumBits(),
"GetDepthSliceSize()", depth_buffer.GetDepthSliceSize()
"Z_INFO.FORMAT", buffer.z_info.format,
"Z_INFO.NUM_SAMPLES", buffer.z_info.num_samples,
"Z_INFO.TILE_SPLIT", buffer.z_info.tile_split,
"Z_INFO.TILE_MODE_INDEX", buffer.z_info.tile_mode_index,
"Z_INFO.DECOMPRESS_ON_N_ZPLANES", buffer.z_info.decompress_on_n_zplanes,
"Z_INFO.ALLOW_EXPCLEAR", buffer.z_info.allow_expclear,
"Z_INFO.READ_SIZE", buffer.z_info.read_size,
"Z_INFO.TILE_SURFACE_ENABLE", buffer.z_info.tile_surface_enable,
"Z_INFO.CLEAR_DISALLOWED", buffer.z_info.clear_disallowed,
"Z_INFO.ZRANGE_PRECISION", buffer.z_info.zrange_precision,
"STENCIL_INFO.FORMAT", buffer.stencil_info.format,
"Z_READ_BASE", buffer.z_read_base,
"STENCIL_READ_BASE", buffer.stencil_read_base,
"Z_WRITE_BASE", buffer.z_write_base,
"STENCIL_WRITE_BASE", buffer.stencil_write_base,
"DEPTH_SIZE.PITCH_TILE_MAX", buffer.depth_size.pitch_tile_max,
"DEPTH_SIZE.HEIGHT_TILE_MAX", buffer.depth_size.height_tile_max,
"DEPTH_SLICE.TILE_MAX", buffer.depth_slice.tile_max,
"Pitch()", buffer.Pitch(),
"Height()", buffer.Height(),
"DepthAddress()", buffer.DepthAddress(),
"StencilAddress()", buffer.StencilAddress(),
"NumSamples()", buffer.NumSamples(),
"NumBits()", buffer.NumBits(),
"GetDepthSliceSize()", buffer.GetDepthSliceSize()
);
// clang-format on
@ -121,16 +120,16 @@ void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) {
// clang-format off
DrawValueRowList(
"STENCIL_ENABLE", depth_control.stencil_enable,
"DEPTH_ENABLE", depth_control.depth_enable,
"DEPTH_WRITE_ENABLE", depth_control.depth_write_enable,
"DEPTH_BOUNDS_ENABLE", depth_control.depth_bounds_enable,
"DEPTH_FUNC", depth_control.depth_func,
"BACKFACE_ENABLE", depth_control.backface_enable,
"STENCIL_FUNC", depth_control.stencil_ref_func,
"STENCIL_FUNC_BF", depth_control.stencil_bf_func,
"ENABLE_COLOR_WRITES_ON_DEPTH_FAIL", depth_control.enable_color_writes_on_depth_fail,
"DISABLE_COLOR_WRITES_ON_DEPTH_PASS", depth_control.disable_color_writes_on_depth_pass
"STENCIL_ENABLE", control.stencil_enable,
"DEPTH_ENABLE", control.depth_enable,
"DEPTH_WRITE_ENABLE", control.depth_write_enable,
"DEPTH_BOUNDS_ENABLE", control.depth_bounds_enable,
"DEPTH_FUNC", control.depth_func,
"BACKFACE_ENABLE", control.backface_enable,
"STENCIL_FUNC", control.stencil_ref_func,
"STENCIL_FUNC_BF", control.stencil_bf_func,
"ENABLE_COLOR_WRITES_ON_DEPTH_FAIL", control.enable_color_writes_on_depth_fail,
"DISABLE_COLOR_WRITES_ON_DEPTH_PASS", control.disable_color_writes_on_depth_pass
);
// clang-format on
@ -143,15 +142,17 @@ RegPopup::RegPopup() {
id = unique_id++;
}
void RegPopup::SetData(const std::string& base_title, AmdGpu::Liverpool::ColorBuffer color_buffer,
u32 cb_id) {
this->data = color_buffer;
void RegPopup::SetData(const std::string& base_title, AmdGpu::ColorBuffer color_buffer, u32 cb_id) {
this->type = DataType::Color;
this->color = color_buffer;
this->title = fmt::format("{}/CB #{}", base_title, cb_id);
}
void RegPopup::SetData(const std::string& base_title, AmdGpu::Liverpool::DepthBuffer depth_buffer,
AmdGpu::Liverpool::DepthControl depth_control) {
this->data = std::make_tuple(depth_buffer, depth_control);
void RegPopup::SetData(const std::string& base_title, AmdGpu::DepthBuffer depth_buffer,
AmdGpu::DepthControl depth_control) {
this->type = DataType::Depth;
this->depth.buffer = depth_buffer;
this->depth.control = depth_control;
this->title = fmt::format("{}/Depth", base_title);
}
@ -161,10 +162,10 @@ void RegPopup::SetPos(ImVec2 pos, bool auto_resize) {
Begin(name, &open, flags);
SetWindowPos(pos);
if (auto_resize) {
if (std::holds_alternative<AmdGpu::Liverpool::ColorBuffer>(data)) {
if (type == DataType::Color) {
SetWindowSize({365.0f, 520.0f});
KeepWindowInside();
} else if (std::holds_alternative<DepthBuffer>(data)) {
} else if (type == DataType::Depth) {
SetWindowSize({404.0f, 543.0f});
KeepWindowInside();
}
@ -182,10 +183,10 @@ void RegPopup::Draw() {
moved = true;
}
if (const auto* buffer = std::get_if<AmdGpu::Liverpool::ColorBuffer>(&data)) {
DrawColorBuffer(*buffer);
} else if (const auto* depth_data = std::get_if<DepthBuffer>(&data)) {
DrawDepthBuffer(*depth_data);
if (type == DataType::Color) {
DrawColorBuffer(color);
} else if (type == DataType::Depth) {
DrawDepthBuffer(depth.buffer, depth.control);
}
}
End();

View File

@ -3,12 +3,10 @@
#pragma once
#include <variant>
#include <imgui.h>
#include "common/types.h"
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/amdgpu/regs_color.h"
#include "video_core/amdgpu/regs_depth.h"
namespace Core::Devtools::Widget {
@ -16,15 +14,24 @@ class RegPopup {
int id;
ImGuiWindowFlags flags{ImGuiWindowFlags_NoSavedSettings};
using DepthBuffer = std::tuple<AmdGpu::Liverpool::DepthBuffer, AmdGpu::Liverpool::DepthControl>;
ImVec2 last_pos;
std::variant<AmdGpu::Liverpool::ColorBuffer, DepthBuffer> data;
AmdGpu::ColorBuffer color;
struct {
AmdGpu::DepthBuffer buffer;
AmdGpu::DepthControl control;
} depth;
enum class DataType {
None = 0,
Color = 1,
Depth = 2,
};
DataType type{};
std::string title{};
static void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer);
static void DrawColorBuffer(const AmdGpu::ColorBuffer& buffer);
static void DrawDepthBuffer(const DepthBuffer& depth_data);
static void DrawDepthBuffer(const AmdGpu::DepthBuffer& buffer,
const AmdGpu::DepthControl control);
public:
bool open = false;
@ -32,11 +39,10 @@ public:
RegPopup();
void SetData(const std::string& base_title, AmdGpu::Liverpool::ColorBuffer color_buffer,
u32 cb_id);
void SetData(const std::string& base_title, AmdGpu::ColorBuffer color_buffer, u32 cb_id);
void SetData(const std::string& base_title, AmdGpu::Liverpool::DepthBuffer depth_buffer,
AmdGpu::Liverpool::DepthControl depth_control);
void SetData(const std::string& base_title, AmdGpu::DepthBuffer depth_buffer,
AmdGpu::DepthControl depth_control);
void SetPos(ImVec2 pos, bool auto_resize = false);

View File

@ -29,7 +29,7 @@ namespace Core::Devtools::Widget {
void RegView::ProcessShader(int shader_id) {
std::vector<u32> shader_code;
Vulkan::Liverpool::UserData user_data;
AmdGpu::UserData user_data;
if (data.is_compute) {
shader_code = data.cs_data.code;
user_data = data.cs_data.cs_program.user_data;
@ -129,7 +129,7 @@ void RegView::DrawGraphicsRegs() {
}
};
for (int cb = 0; cb < AmdGpu::Liverpool::NumColorBuffers; ++cb) {
for (int cb = 0; cb < AmdGpu::NUM_COLOR_BUFFERS; ++cb) {
PushID(cb);
TableNextRow();
@ -246,8 +246,7 @@ void RegView::SetData(DebugStateType::RegDump _data, const std::string& base_tit
default_reg_popup.SetData(title, regs.depth_buffer, regs.depth_control);
default_reg_popup.open = true;
}
} else if (last_selected_cb >= 0 &&
last_selected_cb < AmdGpu::Liverpool::NumColorBuffers) {
} else if (last_selected_cb >= 0 && last_selected_cb < AmdGpu::NUM_COLOR_BUFFERS) {
const auto& buffer = regs.color_buffers[last_selected_cb];
const bool has_cb = buffer && regs.color_target_mask.GetMask(last_selected_cb);
if (has_cb) {
@ -348,7 +347,7 @@ void RegView::Draw() {
} else {
shader->hex_view.DrawContents(shader->user_data.data(),
shader->user_data.size() *
sizeof(Vulkan::Liverpool::UserData::value_type));
sizeof(AmdGpu::UserData::value_type));
}
}
End();
@ -392,4 +391,4 @@ void RegView::Draw() {
}
}
} // namespace Core::Devtools::Widget
} // namespace Core::Devtools::Widget

View File

@ -2,17 +2,18 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/debug_state.h"
#include "imgui_memory_editor.h"
#include "reg_popup.h"
#include "text_editor.h"
#include "core/devtools/widget/imgui_memory_editor.h"
#include "core/devtools/widget/reg_popup.h"
#include "core/devtools/widget/text_editor.h"
namespace Core::Devtools::Widget {
struct ShaderCache {
MemoryEditor hex_view;
TextEditor dis_view;
Vulkan::Liverpool::UserData user_data;
AmdGpu::UserData user_data;
};
class RegView {
@ -54,4 +55,4 @@ public:
void Draw();
};
} // namespace Core::Devtools::Widget
} // namespace Core::Devtools::Widget

View File

@ -65,7 +65,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit
const auto user_key_str = Config::getTrophyKey();
if (user_key_str.size() != 32) {
LOG_CRITICAL(Common_Filesystem, "Trophy decryption key is not specified");
LOG_INFO(Common_Filesystem, "Trophy decryption key is not specified");
return false;
}
@ -105,7 +105,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit
TrpEntry entry;
file.Read(entry);
std::string_view name(entry.entry_name);
if (entry.flag == 0 && name.find("TROP") != std::string::npos) { // PNG
if (entry.flag == 0) { // PNG
if (!file.Seek(entry.entry_pos)) {
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry offset");
return false;

View File

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "base_device.h"
#include "core/file_sys/devices/base_device.h"
namespace Core::Devices {

View File

@ -3,9 +3,9 @@
#pragma once
#include <core/libraries/kernel/orbis_error.h>
#include "common/types.h"
#include "common/va_ctx.h"
#include "core/libraries/kernel/orbis_error.h"
/**
*
@ -31,27 +31,27 @@ public:
virtual ~BaseDevice() = 0;
virtual int ioctl(u64 cmd, Common::VaCtx* args) {
virtual s32 ioctl(u64 cmd, Common::VaCtx* args) {
return ORBIS_KERNEL_ERROR_ENOTTY;
}
virtual s64 write(const void* buf, size_t nbytes) {
virtual s64 write(const void* buf, u64 nbytes) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual size_t readv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt) {
virtual s64 readv(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual size_t writev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt) {
virtual s64 writev(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, u64 offset) {
virtual s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt, s64 offset) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual size_t pwritev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, u64 offset) {
virtual s64 pwritev(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt, s64 offset) {
return ORBIS_KERNEL_ERROR_EBADF;
}
@ -59,11 +59,11 @@ public:
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 read(void* buf, size_t nbytes) {
virtual s64 read(void* buf, u64 nbytes) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual int fstat(Libraries::Kernel::OrbisKernelStat* sb) {
virtual s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) {
return ORBIS_KERNEL_ERROR_EBADF;
}
@ -71,15 +71,15 @@ public:
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual int ftruncate(s64 length) {
virtual s32 ftruncate(s64 length) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual int getdents(void* buf, u32 nbytes, s64* basep) {
virtual s64 getdents(void* buf, u32 nbytes, s64* basep) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 pwrite(const void* buf, size_t nbytes, u64 offset) {
virtual s64 pwrite(const void* buf, u64 nbytes, s64 offset) {
return ORBIS_KERNEL_ERROR_EBADF;
}
};

View File

@ -4,6 +4,7 @@
#pragma once
#include "common/logging/log.h"
#include "common/types.h"
#include "core/file_sys/quasifs/quasifs_inode_quasi_device.h"
@ -25,17 +26,17 @@ public:
s64 read(void* buf, u64 count) override { DEVICE_STUB(); };
s64 write(const void* buf, u64 count) override { DEVICE_STUB(); };
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); };
s64 pread(void* buf, size_t count, u64 offset) override { DEVICE_STUB(); };
s64 pwrite(const void* buf, size_t count, u64 offset) override { DEVICE_STUB(); };
s64 pread(void* buf, size_t count, s64 offset) override { DEVICE_STUB(); };
s64 pwrite(const void* buf, size_t count, s64 offset) override { DEVICE_STUB(); };
s64 readv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt) override { DEVICE_STUB(); };
s64 writev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt) override { DEVICE_STUB(); };
s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, u64 offset) override { DEVICE_STUB(); };
s64 pwritev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, u64 offset) override { DEVICE_STUB(); };
s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, s64 offset) override { DEVICE_STUB(); };
s64 pwritev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, s64 offset) override { DEVICE_STUB(); };
s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); };
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); };
s32 fsync() override { DEVICE_STUB(); };
s32 ftruncate(s64 length) override { DEVICE_STUB(); };
s32 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); };
s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); };
// clang-format on
};

View File

@ -2,9 +2,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/logging/log.h"
#include "common/types.h"
#include "core/file_sys/quasifs/quasifs_inode_quasi_device.h"
@ -26,17 +26,17 @@ public:
s64 read(void* buf, u64 count) override { DEVICE_STUB(); };
s64 write(const void* buf, u64 count) override { DEVICE_STUB(); };
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); };
s64 pread(void* buf, size_t count, u64 offset) override { DEVICE_STUB(); };
s64 pwrite(const void* buf, size_t count, u64 offset) override { DEVICE_STUB(); };
s64 pread(void* buf, size_t count, s64 offset) override { DEVICE_STUB(); };
s64 pwrite(const void* buf, size_t count, s64 offset) override { DEVICE_STUB(); };
s64 readv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt) override { DEVICE_STUB(); };
s64 writev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt) override { DEVICE_STUB(); };
s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, u64 offset) override { DEVICE_STUB(); };
s64 pwritev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, u64 offset) override { DEVICE_STUB(); };
s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, s64 offset) override { DEVICE_STUB(); };
s64 pwritev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, s64 offset) override { DEVICE_STUB(); };
s64 lseek(s64 offset, int whence) override { DEVICE_STUB(); };
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); };
s32 fsync() override { DEVICE_STUB(); };
s32 ftruncate(s64 length) override { DEVICE_STUB(); };
s32 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); };
s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); };
// clang-format on
};

View File

@ -2,8 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/file_sys/devices/logger.h"
#include "core/libraries/kernel/file_system.h"
#include "logger.h"
namespace Core::Devices {
@ -11,17 +11,17 @@ Logger::Logger(std::string prefix, bool is_err) : prefix(std::move(prefix)), is_
Logger::~Logger() = default;
s64 Logger::write(const void* buf, size_t nbytes) {
s64 Logger::write(const void* buf, u64 nbytes) {
log(static_cast<const char*>(buf), nbytes);
return nbytes;
}
s64 Logger::pwrite(const void* buf, size_t nbytes, u64 offset) {
s64 Logger::pwrite(const void* buf, u64 nbytes, s64 offset) {
log(static_cast<const char*>(buf), nbytes);
return nbytes;
}
s64 Logger::writev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt) {
s64 Logger::writev(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt) {
size_t total_written = 0;
for (int i = 0; i < iovcnt; i++) {
log(static_cast<const char*>(iov[i].iov_base), iov[i].iov_len);
@ -35,7 +35,7 @@ s32 Logger::fsync() {
return 0;
}
void Logger::log(const char* buf, size_t nbytes) {
void Logger::log(const char* buf, u64 nbytes) {
std::scoped_lock lock{mtx};
const char* end = buf + nbytes;
for (const char* it = buf; it < end; ++it) {

View File

@ -30,14 +30,14 @@ public:
explicit Logger(std::string prefix, bool is_err);
~Logger();
s64 write(const void* buf, size_t nbytes) override;
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
s64 writev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt) override;
s64 write(const void* buf, u64 nbytes) override;
s64 writev(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt) override;
s64 pwrite(const void* buf, u64 nbytes, s64 offset) override;
s32 fsync() override;
private:
void log(const char* buf, size_t nbytes);
void log(const char* buf, u64 nbytes);
void log_flush();
};

View File

@ -4,15 +4,10 @@
#pragma once
#include "common/logging/log.h"
#include "common/types.h"
#include "core/file_sys/quasifs/quasifs_inode_quasi_device.h"
#define DEVICE_STUB() \
{ \
LOG_ERROR(Kernel_Fs, "(STUBBED) called"); \
return -QUASI_ENOSYS; \
}
namespace Core::Devices {
class NopDevice final : public QuasiFS::Device {
@ -25,17 +20,17 @@ public:
s64 read(void* buf, u64 count) override { return 0; };
s64 write(const void* buf, u64 count) override { return 0; };
s32 ioctl(u64 cmd, Common::VaCtx* args) override { return 0; };
s64 pread(void* buf, size_t count, u64 offset) override { return 0; };
s64 pwrite(const void* buf, size_t count, u64 offset) override { return 0; };
s64 pread(void* buf, size_t count, s64 offset) override { return 0; };
s64 pwrite(const void* buf, size_t count, s64 offset) override { return 0; };
s64 readv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt) override { return 0; };
s64 writev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt) override { return 0; };
s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, u64 offset) override { return 0; };
s64 pwritev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, u64 offset) override { return 0; };
s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, s64 offset) override { return 0; };
s64 pwritev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, s64 offset) override { return 0; };
s64 lseek(s64 offset, int whence) override { return 0; };
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { return 0; };
s32 fsync() override { return 0; };
s32 ftruncate(s64 length) override { return 0; };
s32 getdents(void* buf, u32 nbytes, s64* basep) override { return 0; };
s64 getdents(void* buf, u32 nbytes, s64* basep) override { return 0; };
// clang-format on
};

View File

@ -10,11 +10,11 @@ namespace Core::Devices {
NullDevice::NullDevice() = default;
NullDevice::~NullDevice() = default;
s64 NullDevice::pread(void* buf, size_t count, u64 offset) {
s64 NullDevice::pread(void* buf, size_t count, s64 offset) {
return 0;
}
s64 NullDevice::pwrite(const void* buf, size_t count, u64 offset) {
s64 NullDevice::pwrite(const void* buf, size_t count, s64 offset) {
return count;
}

View File

@ -23,8 +23,8 @@ public:
NullDevice();
~NullDevice();
s64 pread(void* buf, size_t count, u64 offset) override;
s64 pwrite(const void* buf, size_t count, u64 offset) override;
s64 pread(void* buf, size_t count, s64 offset) override;
s64 pwrite(const void* buf, size_t count, s64 offset) override;
// clang-format off
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); }
@ -32,7 +32,7 @@ public:
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }
s32 fsync() override { DEVICE_STUB(); }
s32 ftruncate(s64 length) override { DEVICE_STUB(); }
s32 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); }
s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); }
// clang-format on
};

View File

@ -14,7 +14,7 @@ RandomDevice::RandomDevice() {
RandomDevice::~RandomDevice() = default;
s64 RandomDevice::pread(void* buf, u64 count, u64 offset) {
s64 RandomDevice::pread(void* buf, u64 count, s64 offset) {
auto rbuf = static_cast<char*>(buf);
for (size_t i = 0; i < count; i++) {
rbuf[i] = std::rand() & 0xFF;
@ -22,7 +22,7 @@ s64 RandomDevice::pread(void* buf, u64 count, u64 offset) {
return count;
}
s64 RandomDevice::pwrite(const void* buf, u64 count, u64 offset) {
s64 RandomDevice::pwrite(const void* buf, u64 count, s64 offset) {
return count;
}

View File

@ -3,9 +3,8 @@
#pragma once
#include <memory>
#include "common/logging/log.h"
#include "common/types.h"
#include "core/file_sys/quasifs/quasifs_inode_quasi_device.h"
@ -22,8 +21,8 @@ public:
RandomDevice();
~RandomDevice();
s64 pread(void* buf, size_t count, u64 offset) override;
s64 pwrite(const void* buf, size_t count, u64 offset) override;
s64 pread(void* buf, size_t count, s64 offset) override;
s64 pwrite(const void* buf, size_t count, s64 offset) override;
// clang-format off
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); }
@ -31,7 +30,7 @@ public:
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }
s32 fsync() override { DEVICE_STUB(); }
s32 ftruncate(s64 length) override { DEVICE_STUB(); }
s32 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); }
s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); }
// clang-format on
};

View File

@ -0,0 +1,87 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib>
#include <ctime>
#include "common/logging/log.h"
#include "core/file_sys/devices/rng_device.h"
namespace Core::Devices {
std::shared_ptr<BaseDevice> RngDevice::Create(u32 handle, const char*, s32, u16) {
std::srand(std::time(nullptr));
return std::static_pointer_cast<BaseDevice>(std::make_shared<RngDevice>(handle));
}
s32 RngDevice::ioctl(u64 cmd, Common::VaCtx* args) {
LOG_INFO(Kernel_Pthread, "called, cmd = {:#x}", cmd);
// Both commands are for generating a random number
if (cmd == 0x40445301 || cmd == 0x40445302) {
auto& data = *vaArgPtr<GetRandomArgs>(&args->va_list);
data.result = 0;
for (u64 i = 0; i < 64; i++) {
data.buf[i] = std::rand();
}
} else {
// ENOIOCTL
return -3;
}
return 0;
}
s64 RngDevice::write(const void* buf, u64 nbytes) {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
s64 RngDevice::writev(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt) {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
s64 RngDevice::readv(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt) {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
s64 RngDevice::preadv(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt, s64 offset) {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
s64 RngDevice::lseek(s64 offset, s32 whence) {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
s64 RngDevice::read(void* buf, u64 nbytes) {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
s32 RngDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
s32 RngDevice::fsync() {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
s32 RngDevice::ftruncate(s64 length) {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
s64 RngDevice::getdents(void* buf, u32 nbytes, s64* basep) {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
s64 RngDevice::pwrite(const void* buf, u64 nbytes, s64 offset) {
LOG_ERROR(Kernel_Fs, "(STUBBED) called");
return 0;
}
} // namespace Core::Devices

View File

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "core/file_sys/devices/base_device.h"
namespace Core::Devices {
class RngDevice final : public BaseDevice {
u32 handle;
public:
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, s32, u16);
explicit RngDevice(u32 handle) : handle(handle) {}
~RngDevice() override = default;
s32 ioctl(u64 cmd, Common::VaCtx* args) override;
s64 write(const void* buf, u64 nbytes) override;
s64 readv(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt) override;
s64 writev(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt) override;
s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, s32 iovcnt, s64 offset) override;
s64 lseek(s64 offset, s32 whence) override;
s64 read(void* buf, u64 nbytes) override;
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
s32 fsync() override;
s32 ftruncate(s64 length) override;
s64 getdents(void* buf, u32 nbytes, s64* basep) override;
s64 pwrite(const void* buf, u64 nbytes, s64 offset) override;
private:
struct GetRandomArgs {
s32 result;
s8 buf[64];
};
};
} // namespace Core::Devices

View File

@ -14,7 +14,7 @@ SRandomDevice::SRandomDevice() {
SRandomDevice::~SRandomDevice() = default;
s64 SRandomDevice::pread(void* buf, size_t count, u64 offset) {
s64 SRandomDevice::pread(void* buf, size_t count, s64 offset) {
auto rbuf = static_cast<char*>(buf);
for (size_t i = 0; i < count; i++) {
rbuf[i] = std::rand();
@ -22,7 +22,7 @@ s64 SRandomDevice::pread(void* buf, size_t count, u64 offset) {
return count;
}
s64 SRandomDevice::pwrite(const void* buf, size_t count, u64 offset) {
s64 SRandomDevice::pwrite(const void* buf, size_t count, s64 offset) {
return count;
}

View File

@ -3,9 +3,8 @@
#pragma once
#include <memory>
#include "common/logging/log.h"
#include "common/types.h"
#include "core/file_sys/quasifs/quasifs_inode_quasi_device.h"
@ -23,8 +22,8 @@ public:
SRandomDevice();
~SRandomDevice();
s64 pread(void* buf, size_t count, u64 offset) override;
s64 pwrite(const void* buf, size_t count, u64 offset) override;
s64 pread(void* buf, size_t count, s64 offset) override;
s64 pwrite(const void* buf, size_t count, s64 offset) override;
// clang-format off
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); }
@ -32,7 +31,7 @@ public:
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }
s32 fsync() override { DEVICE_STUB(); }
s32 ftruncate(s64 length) override { DEVICE_STUB(); }
s32 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); }
s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); }
// clang-format on
};

View File

@ -10,12 +10,12 @@ namespace Core::Devices {
ZeroDevice::ZeroDevice() = default;
ZeroDevice::~ZeroDevice() = default;
s64 ZeroDevice::pread(void* buf, size_t count, u64 offset) {
s64 ZeroDevice::pread(void* buf, size_t count, s64 offset) {
memset(buf, 0, count);
return count;
}
s64 ZeroDevice::pwrite(const void* buf, size_t count, u64 offset) {
s64 ZeroDevice::pwrite(const void* buf, size_t count, s64 offset) {
return count;
}

View File

@ -21,8 +21,8 @@ public:
ZeroDevice();
~ZeroDevice();
s64 pread(void* buf, size_t count, u64 offset) override;
s64 pwrite(const void* buf, size_t count, u64 offset) override;
s64 pread(void* buf, size_t count, s64 offset) override;
s64 pwrite(const void* buf, size_t count, s64 offset) override;
// clang-format off
s32 ioctl(u64 cmd, Common::VaCtx* args) override { DEVICE_STUB(); }
@ -30,7 +30,7 @@ public:
s32 fstat(Libraries::Kernel::OrbisKernelStat* sb) override { DEVICE_STUB(); }
s32 fsync() override { DEVICE_STUB(); }
s32 ftruncate(s64 length) override { DEVICE_STUB(); }
s32 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); }
s64 getdents(void* buf, u32 nbytes, s64* basep) override { DEVICE_STUB(); }
// clang-format on
};

View File

@ -218,6 +218,30 @@ File* HandleTable::GetSocket(int d) {
return file;
}
File* HandleTable::GetEpoll(int d) {
std::scoped_lock lock{m_mutex};
if (d < 0 || d >= m_files.size()) {
return nullptr;
}
auto file = m_files.at(d);
if (file->type != Core::FileSys::FileType::Epoll) {
return nullptr;
}
return file;
}
File* HandleTable::GetResolver(int d) {
std::scoped_lock lock{m_mutex};
if (d < 0 || d >= m_files.size()) {
return nullptr;
}
auto file = m_files.at(d);
if (file->type != Core::FileSys::FileType::Resolver) {
return nullptr;
}
return file;
}
File* HandleTable::GetFile(const std::filesystem::path& host_name) {
for (auto* file : m_files) {
if (file != nullptr && file->m_host_name == host_name) {

View File

@ -15,7 +15,9 @@
namespace Libraries::Net {
struct Socket;
}
struct Epoll;
struct Resolver;
} // namespace Libraries::Net
namespace Core::FileSys {
@ -70,6 +72,8 @@ enum class FileType {
Directory,
Device,
Socket,
Epoll,
Resolver
};
struct File {
@ -82,6 +86,8 @@ struct File {
std::shared_ptr<Directories::BaseDirectory> directory; // only valid for type == Directory
std::shared_ptr<Devices::BaseDevice> device; // only valid for type == Device
std::shared_ptr<Libraries::Net::Socket> socket; // only valid for type == Socket
std::shared_ptr<Libraries::Net::Epoll> epoll; // only valid for type == Epoll
std::shared_ptr<Libraries::Net::Resolver> resolver; // only valid for type == Resolver
};
class HandleTable {
@ -92,6 +98,8 @@ public:
int CreateHandle();
File* GetFile(int d);
File* GetSocket(int d);
File* GetEpoll(int d);
File* GetResolver(int d);
File* GetFile(const std::filesystem::path& host_name);

View File

@ -29,19 +29,19 @@ public:
return -QUASI_ENOTTY;
}
virtual s64 read(void* buf, size_t count) {
virtual s64 read(void* buf, u64 count) {
return pread(buf, count, 0);
}
virtual s64 write(const void* buf, size_t count) {
virtual s64 write(const void* buf, u64 count) {
return pwrite(buf, count, 0);
}
virtual s64 pread(void* buf, size_t count, u64 offset) {
virtual s64 pread(void* buf, u64 count, s64 offset) {
return -QUASI_EBADF;
}
virtual s64 pwrite(const void* buf, size_t count, u64 offset) {
virtual s64 pwrite(const void* buf, u64 count, s64 offset) {
return -QUASI_EBADF;
}
@ -67,7 +67,7 @@ public:
return tb;
}
virtual s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, u64 offset) {
virtual s64 preadv(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, s64 offset) {
u64 tb = 0;
for (unsigned int idx = 0; idx < iovcnt; idx++) {
int status = this->pread(iov[idx].iov_base, iov[idx].iov_len, offset);
@ -78,7 +78,7 @@ public:
return tb;
}
virtual s64 pwritev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, u64 offset) {
virtual s64 pwritev(const Libraries::Kernel::OrbisKernelIovec* iov, int iovcnt, s64 offset) {
u64 tb = 0;
for (unsigned int idx = 0; idx < iovcnt; idx++) {
int status = this->pwrite(iov[idx].iov_base, iov[idx].iov_len, offset);
@ -106,7 +106,7 @@ public:
return -QUASI_EINVAL;
}
virtual s32 getdents(void* buf, u32 nbytes, s64* basep) {
virtual s64 getdents(void* buf, u32 nbytes, s64* basep) {
return -QUASI_EBADF;
}

View File

@ -27,8 +27,8 @@ public:
s64 read(void* buf, size_t count) override;
s64 write(const void* buf, size_t count) override;
s64 pread(void* buf, size_t count, u64 offset) override;
s64 pwrite(const void* buf, size_t count, u64 offset) override;
s64 pread(void* buf, size_t count, s64 offset) override;
s64 pwrite(const void* buf, size_t count, s64 offset) override;
s32 ftruncate(s64 length) override;
};

View File

@ -21,8 +21,8 @@ public:
//
s64 read(void* buf, size_t count) override;
s64 write(const void* buf, size_t count) override;
s64 pread(void* buf, size_t count, u64 offset) override;
s64 pwrite(const void* buf, size_t count, u64 offset) override;
s64 pread(void* buf, size_t count, s64 offset) override;
s64 pwrite(const void* buf, size_t count, s64 offset) override;
s32 ftruncate(s64 length) override;
};

View File

@ -15,14 +15,14 @@ s64 QuasiFile::write(const void* buf, size_t count) {
return pwrite(buf, count, 0);
}
s64 QuasiFile::pread(void* buf, size_t count, u64 offset) {
s64 QuasiFile::pread(void* buf, size_t count, s64 offset) {
auto size = &this->st.st_size;
auto end_pos = offset + count;
return end_pos > *size ? *size - offset : count;
}
s64 QuasiFile::pwrite(const void* buf, size_t count, u64 offset) {
s64 QuasiFile::pwrite(const void* buf, size_t count, s64 offset) {
auto size = &this->st.st_size;
auto end_pos = offset + count;

View File

@ -15,7 +15,7 @@ s64 VirtualFile::write(const void* buf, size_t count) {
return pwrite(buf, count, 0);
}
s64 VirtualFile::pread(void* buf, size_t count, u64 offset) {
s64 VirtualFile::pread(void* buf, size_t count, s64 offset) {
s64 idx;
s64 read_amt = this->data.size() - offset - count;
@ -31,7 +31,7 @@ s64 VirtualFile::pread(void* buf, size_t count, u64 offset) {
return read_amt;
}
s64 VirtualFile::pwrite(const void* buf, size_t count, u64 offset) {
s64 VirtualFile::pwrite(const void* buf, size_t count, s64 offset) {
auto size = &this->st.st_size;
auto end_pos = offset + count;
*size = end_pos > *size ? end_pos : *size;

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "ipc.h"
@ -8,12 +8,19 @@
#include <SDL3/SDL.h>
#include "common/config.h"
#include "common/memory_patcher.h"
#include "common/thread.h"
#include "common/types.h"
#include "core/debug_state.h"
#include "core/debugger.h"
#include "core/libraries/audio/audioout.h"
#include "input/input_handler.h"
#include "sdl_window.h"
#include "src/core/libraries/usbd/usbd.h"
#include "video_core/renderer_vulkan/vk_presenter.h"
extern std::unique_ptr<Vulkan::Presenter> presenter;
/**
* Protocol summary:
@ -40,6 +47,7 @@
* Command list:
* - CAPABILITIES:
* - ENABLE_MEMORY_PATCH: enables PATCH_MEMORY command
* - ENABLE_EMU_CONTROL: enables PAUSE, RESUME, STOP, TOGGLE_FULLSCREEN commands
* - INPUT CMD:
* - RUN: start the emulator execution
* - START: start the game execution
@ -53,7 +61,7 @@
* - STOP: stop and quit the emulator
* - TOGGLE_FULLSCREEN: enable / disable fullscreen
* - OUTPUT CMD:
* - N/A
* - RESTART(argn: number, argv: ...string): Request restart of the emulator, must call STOP
**/
void IPC::Init() {
@ -63,6 +71,8 @@ void IPC::Init() {
return;
}
Config::setLoadAutoPatches(false);
input_thread = std::jthread([this] {
Common::SetCurrentThreadName("IPC Read thread");
this->InputLoop();
@ -81,6 +91,15 @@ void IPC::Init() {
}
}
void IPC::SendRestart(const std::vector<std::string>& args) {
std::cerr << ";RESTART\n";
std::cerr << ";" << args.size() << "\n";
for (const auto& arg : args) {
std::cerr << ";" << arg << "\n";
}
std::cerr.flush();
}
void IPC::InputLoop() {
auto next_str = [&] -> const std::string& {
static std::string line_buffer;
@ -130,8 +149,70 @@ void IPC::InputLoop() {
SDL_memset(&event, 0, sizeof(event));
event.type = SDL_EVENT_TOGGLE_FULLSCREEN;
SDL_PushEvent(&event);
} else if (cmd == "ADJUST_VOLUME") {
int value = static_cast<int>(next_u64());
bool is_game_specific = next_u64() != 0;
Config::setVolumeSlider(value, is_game_specific);
Libraries::AudioOut::AdjustVol();
} else if (cmd == "SET_FSR") {
bool use_fsr = next_u64() != 0;
if (presenter) {
presenter->GetFsrSettingsRef().enable = use_fsr;
}
} else if (cmd == "SET_RCAS") {
bool use_rcas = next_u64() != 0;
if (presenter) {
presenter->GetFsrSettingsRef().use_rcas = use_rcas;
}
} else if (cmd == "SET_RCAS_ATTENUATION") {
int value = static_cast<int>(next_u64());
if (presenter) {
presenter->GetFsrSettingsRef().rcas_attenuation =
static_cast<float>(value / 1000.0f);
}
} else if (cmd == "USB_LOAD_FIGURE") {
const auto ref = Libraries::Usbd::usb_backend->GetImplRef();
if (ref) {
ref->LoadFigure(next_str(), next_u64(), next_u64());
}
} else if (cmd == "USB_REMOVE_FIGURE") {
const auto ref = Libraries::Usbd::usb_backend->GetImplRef();
if (ref) {
ref->RemoveFigure(next_u64(), next_u64(), next_u64() != 0);
}
} else if (cmd == "USB_MOVE_FIGURE") {
const auto ref = Libraries::Usbd::usb_backend->GetImplRef();
if (ref) {
const u8 new_pad = next_u64();
const u8 new_index = next_u64();
const u8 old_pad = next_u64();
const u8 old_index = next_u64();
ref->MoveFigure(new_pad, new_index, old_pad, old_index);
}
} else if (cmd == "USB_TEMP_REMOVE_FIGURE") {
const auto ref = Libraries::Usbd::usb_backend->GetImplRef();
if (ref) {
const u8 index = next_u64();
ref->TempRemoveFigure(index);
}
} else if (cmd == "USB_CANCEL_REMOVE_FIGURE") {
const auto ref = Libraries::Usbd::usb_backend->GetImplRef();
if (ref) {
const u8 index = next_u64();
ref->CancelRemoveFigure(index);
}
} else if (cmd == "RELOAD_INPUTS") {
std::string config = next_str();
Input::ParseInputConfig(config);
} else if (cmd == "SET_ACTIVE_CONTROLLER") {
std::string active_controller = next_str();
GamepadSelect::SetSelectedGamepad(active_controller);
SDL_Event checkGamepad;
SDL_memset(&checkGamepad, 0, sizeof(checkGamepad));
checkGamepad.type = SDL_EVENT_CHANGE_CONTROLLER;
SDL_PushEvent(&checkGamepad);
} else {
std::cerr << ";UNKNOWN CMD: " << cmd << std::endl;
}
}
}
}

View File

@ -6,7 +6,9 @@
#include "common/singleton.h"
#include <semaphore>
#include <string>
#include <thread>
#include <vector>
class IPC {
bool enabled{false};
@ -34,6 +36,8 @@ public:
start_semaphore.acquire();
}
void SendRestart(const std::vector<std::string>& args);
private:
[[noreturn]] void InputLoop();
};

View File

@ -10,15 +10,51 @@ extern "C" {
#include <libatrac9.h>
}
#include <vector>
namespace Libraries::Ajm {
struct ChunkHeader {
u32 tag;
u32 length;
};
static_assert(sizeof(ChunkHeader) == 8);
struct AudioFormat {
u16 fmt_type;
u16 num_channels;
u32 avg_sample_rate;
u32 avg_byte_rate;
u16 block_align;
u16 bits_per_sample;
u16 ext_size;
union {
u16 valid_bits_per_sample;
u16 samples_per_block;
u16 reserved;
};
u32 channel_mask;
u8 guid[16];
u32 version;
u8 config_data[4];
u32 reserved2;
};
static_assert(sizeof(AudioFormat) == 52);
struct SampleData {
u32 sample_length;
u32 encoder_delay;
u32 encoder_delay2;
};
static_assert(sizeof(SampleData) == 12);
struct RIFFHeader {
u32 riff;
u32 size;
u32 wave;
};
static_assert(sizeof(RIFFHeader) == 12);
AjmAt9Decoder::AjmAt9Decoder(AjmFormatEncoding format, AjmAt9CodecFlags flags)
: m_format(format), m_flags(flags), m_handle(Atrac9GetHandle()) {
ASSERT_MSG(m_handle, "Atrac9GetHandle failed");
AjmAt9Decoder::Reset();
}
: m_format(format), m_flags(flags), m_handle(Atrac9GetHandle()) {}
AjmAt9Decoder::~AjmAt9Decoder() {
Atrac9ReleaseHandle(m_handle);
@ -42,6 +78,7 @@ void AjmAt9Decoder::Initialize(const void* buffer, u32 buffer_size) {
AjmAt9Decoder::Reset();
m_pcm_buffer.resize(m_codec_info.frameSamples * m_codec_info.channels * GetPCMSize(m_format),
0);
m_is_initialized = true;
}
void AjmAt9Decoder::GetInfo(void* out_info) const {
@ -52,8 +89,64 @@ void AjmAt9Decoder::GetInfo(void* out_info) const {
info->next_frame_size = static_cast<Atrac9Handle*>(m_handle)->Config.FrameBytes;
}
std::tuple<u32, u32> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf, SparseOutputBuffer& output,
AjmInstanceGapless& gapless) {
u8 g_at9_guid[] = {0xD2, 0x42, 0xE1, 0x47, 0xBA, 0x36, 0x8D, 0x4D,
0x88, 0xFC, 0x61, 0x65, 0x4F, 0x8C, 0x83, 0x6C};
void AjmAt9Decoder::ParseRIFFHeader(std::span<u8>& in_buf, AjmInstanceGapless& gapless) {
auto* header = reinterpret_cast<RIFFHeader*>(in_buf.data());
in_buf = in_buf.subspan(sizeof(RIFFHeader));
ASSERT(header->riff == 'FFIR');
ASSERT(header->wave == 'EVAW');
auto* chunk = reinterpret_cast<ChunkHeader*>(in_buf.data());
in_buf = in_buf.subspan(sizeof(ChunkHeader));
while (chunk->tag != 'atad') {
switch (chunk->tag) {
case ' tmf': {
ASSERT(chunk->length == sizeof(AudioFormat));
auto* fmt = reinterpret_cast<AudioFormat*>(in_buf.data());
ASSERT(fmt->fmt_type == 0xFFFE);
ASSERT(memcmp(fmt->guid, g_at9_guid, 16) == 0);
AjmDecAt9InitializeParameters init_params = {};
std::memcpy(init_params.config_data, fmt->config_data, ORBIS_AT9_CONFIG_DATA_SIZE);
Initialize(&init_params, sizeof(init_params));
break;
}
case 'tcaf': {
ASSERT(chunk->length == sizeof(SampleData));
auto* samples = reinterpret_cast<SampleData*>(in_buf.data());
gapless.init.total_samples = samples->sample_length;
gapless.init.skip_samples = samples->encoder_delay;
gapless.Reset();
break;
}
default:
break;
}
in_buf = in_buf.subspan(chunk->length);
chunk = reinterpret_cast<ChunkHeader*>(in_buf.data());
in_buf = in_buf.subspan(sizeof(ChunkHeader));
}
}
std::tuple<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf,
SparseOutputBuffer& output,
AjmInstanceGapless& gapless) {
bool is_reset = false;
if (True(m_flags & AjmAt9CodecFlags::ParseRiffHeader) &&
*reinterpret_cast<u32*>(in_buf.data()) == 'FFIR') {
ParseRIFFHeader(in_buf, gapless);
is_reset = true;
}
if (!m_is_initialized) {
return {0, 0, is_reset};
}
int ret = 0;
int bytes_used = 0;
switch (m_format) {
@ -118,7 +211,7 @@ std::tuple<u32, u32> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf, SparseOut
m_num_frames = 0;
}
return {1, samples_written};
return {1, m_codec_info.frameSamples, is_reset};
}
AjmSidebandFormat AjmAt9Decoder::GetFormat() const {
@ -134,12 +227,13 @@ AjmSidebandFormat AjmAt9Decoder::GetFormat() const {
}
u32 AjmAt9Decoder::GetNextFrameSize(const AjmInstanceGapless& gapless) const {
const auto max_samples =
const auto skip_samples =
std::min<u32>(gapless.current.skip_samples, m_codec_info.frameSamples);
const auto samples =
gapless.init.total_samples != 0
? std::min(gapless.current.total_samples, u32(m_codec_info.frameSamples))
? std::min<u32>(gapless.current.total_samples, m_codec_info.frameSamples - skip_samples)
: m_codec_info.frameSamples;
const auto skip_samples = std::min(u32(gapless.current.skip_samples), max_samples);
return (max_samples - skip_samples) * m_codec_info.channels * GetPCMSize(m_format);
return samples * m_codec_info.channels * GetPCMSize(m_format);
}
} // namespace Libraries::Ajm

View File

@ -9,6 +9,7 @@
#include "libatrac9.h"
#include <span>
#include <vector>
namespace Libraries::Ajm {
@ -36,8 +37,8 @@ struct AjmAt9Decoder final : AjmCodec {
void GetInfo(void* out_info) const override;
AjmSidebandFormat GetFormat() const override;
u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const override;
std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
AjmInstanceGapless& gapless) override;
std::tuple<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
AjmInstanceGapless& gapless) override;
private:
template <class T>
@ -49,8 +50,12 @@ private:
return output.Write(pcm_data.subspan(0, pcm_size));
}
void ParseRIFFHeader(std::span<u8>& input, AjmInstanceGapless& gapless);
const AjmFormatEncoding m_format;
const AjmAt9CodecFlags m_flags;
bool m_is_initialized{};
void* m_handle{};
u8 m_config_data[ORBIS_AT9_CONFIG_DATA_SIZE]{};
u32 m_superframe_bytes_remain{};

View File

@ -52,21 +52,22 @@ AjmInstance::AjmInstance(AjmCodecType codec_type, AjmInstanceFlags flags) : m_fl
}
}
void AjmInstance::Reset() {
m_total_samples = 0;
m_gapless.Reset();
m_codec->Reset();
}
void AjmInstance::ExecuteJob(AjmJob& job) {
const auto control_flags = job.flags.control_flags;
if (True(control_flags & AjmJobControlFlags::Reset)) {
LOG_TRACE(Lib_Ajm, "Resetting instance {}", job.instance_id);
m_format = {};
m_gapless = {};
m_resample_parameters = {};
m_total_samples = 0;
m_codec->Reset();
Reset();
}
if (job.input.init_params.has_value()) {
LOG_TRACE(Lib_Ajm, "Initializing instance {}", job.instance_id);
auto& params = job.input.init_params.value();
m_codec->Initialize(&params, sizeof(params));
is_initialized = true;
}
if (job.input.resample_parameters.has_value()) {
LOG_ERROR(Lib_Ajm, "Unimplemented: resample parameters");
@ -78,20 +79,37 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
}
if (job.input.gapless_decode.has_value()) {
auto& params = job.input.gapless_decode.value();
if (params.total_samples != 0) {
const auto max = std::max(params.total_samples, m_gapless.init.total_samples);
m_gapless.current.total_samples += max - m_gapless.init.total_samples;
m_gapless.init.total_samples = max;
}
if (params.skip_samples != 0) {
const auto max = std::max(params.skip_samples, m_gapless.init.skip_samples);
m_gapless.current.skip_samples += max - m_gapless.init.skip_samples;
m_gapless.init.skip_samples = max;
}
}
if (!is_initialized) {
return;
const auto samples_processed =
m_gapless.init.total_samples - m_gapless.current.total_samples;
if (params.total_samples != 0 || params.skip_samples == 0) {
if (params.total_samples >= samples_processed) {
const auto sample_difference =
s64(m_gapless.init.total_samples) - params.total_samples;
m_gapless.init.total_samples = params.total_samples;
m_gapless.current.total_samples -= sample_difference;
} else {
LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_INVALID_PARAMETER");
job.output.p_result->result = ORBIS_AJM_RESULT_INVALID_PARAMETER;
return;
}
}
const auto samples_skipped = m_gapless.init.skip_samples - m_gapless.current.skip_samples;
if (params.skip_samples != 0 || params.total_samples == 0) {
if (params.skip_samples >= samples_skipped) {
const auto sample_difference =
s32(m_gapless.init.skip_samples) - params.skip_samples;
m_gapless.init.skip_samples = params.skip_samples;
m_gapless.current.skip_samples -= sample_difference;
} else {
LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_INVALID_PARAMETER");
job.output.p_result->result = ORBIS_AJM_RESULT_INVALID_PARAMETER;
return;
}
}
}
if (!job.input.buffer.empty() && !job.output.buffers.empty()) {
@ -104,12 +122,23 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
while (!in_buf.empty() && !out_buf.IsEmpty() && !m_gapless.IsEnd()) {
if (!HasEnoughSpace(out_buf)) {
if (job.output.p_mframe == nullptr || frames_decoded == 0) {
LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM ({} < {})",
out_buf.Size(), m_codec->GetNextFrameSize(m_gapless));
job.output.p_result->result = ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM;
break;
}
}
const auto [nframes, nsamples] = m_codec->ProcessData(in_buf, out_buf, m_gapless);
const auto [nframes, nsamples, reset] =
m_codec->ProcessData(in_buf, out_buf, m_gapless);
if (reset) {
m_total_samples = 0;
}
if (!nframes) {
LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_INITIALIZED");
job.output.p_result->result = ORBIS_AJM_RESULT_NOT_INITIALIZED;
break;
}
frames_decoded += nframes;
m_total_samples += nsamples;
@ -118,10 +147,10 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
}
}
if (m_gapless.IsEnd()) {
const auto total_decoded_samples = m_total_samples;
if (m_flags.gapless_loop && m_gapless.IsEnd()) {
in_buf = in_buf.subspan(in_buf.size());
m_gapless.current.total_samples = m_gapless.init.total_samples;
m_gapless.current.skip_samples = m_gapless.init.skip_samples;
m_gapless.Reset();
m_codec->Reset();
}
if (job.output.p_mframe) {
@ -130,7 +159,7 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
if (job.output.p_stream) {
job.output.p_stream->input_consumed = in_size - in_buf.size();
job.output.p_stream->output_written = out_size - out_buf.Size();
job.output.p_stream->total_decoded_samples = m_total_samples;
job.output.p_stream->total_decoded_samples = total_decoded_samples;
}
}

View File

@ -65,6 +65,12 @@ struct AjmInstanceGapless {
bool IsEnd() const {
return init.total_samples != 0 && current.total_samples == 0;
}
void Reset() {
current.total_samples = init.total_samples;
current.skip_samples = init.skip_samples;
current.skipped_samples = 0;
}
};
class AjmCodec {
@ -76,8 +82,8 @@ public:
virtual void GetInfo(void* out_info) const = 0;
virtual AjmSidebandFormat GetFormat() const = 0;
virtual u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const = 0;
virtual std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
AjmInstanceGapless& gapless) = 0;
virtual std::tuple<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
AjmInstanceGapless& gapless) = 0;
};
class AjmInstance {
@ -89,6 +95,7 @@ public:
private:
bool HasEnoughSpace(const SparseOutputBuffer& output) const;
std::optional<u32> GetNumRemainingSamples() const;
void Reset();
AjmInstanceFlags m_flags{};
AjmSidebandFormat m_format{};
@ -96,7 +103,6 @@ private:
AjmSidebandResampleParameters m_resample_parameters{};
u32 m_total_samples{};
std::unique_ptr<AjmCodec> m_codec;
bool is_initialized = false;
};
} // namespace Libraries::Ajm

View File

@ -138,8 +138,9 @@ void AjmMp3Decoder::GetInfo(void* out_info) const {
}
}
std::tuple<u32, u32> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf, SparseOutputBuffer& output,
AjmInstanceGapless& gapless) {
std::tuple<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf,
SparseOutputBuffer& output,
AjmInstanceGapless& gapless) {
AVPacket* pkt = av_packet_alloc();
if ((!m_header.has_value() || m_frame_samples == 0) && in_buf.size() >= 4) {
@ -155,7 +156,7 @@ std::tuple<u32, u32> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf, SparseOut
in_buf = in_buf.subspan(ret);
u32 frames_decoded = 0;
u32 samples_written = 0;
u32 samples_decoded = 0;
if (pkt->size) {
// Send the packet with the compressed data to the decoder
@ -176,6 +177,7 @@ std::tuple<u32, u32> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf, SparseOut
UNREACHABLE_MSG("Error during decoding");
}
frame = ConvertAudioFrame(frame);
samples_decoded += u32(frame->nb_samples);
frames_decoded += 1;
u32 skip_samples = 0;
@ -205,7 +207,6 @@ std::tuple<u32, u32> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf, SparseOut
}
const auto samples = pcm_written / m_codec_context->ch_layout.nb_channels;
samples_written += samples;
gapless.current.skipped_samples += frame->nb_samples - samples;
if (gapless.init.total_samples != 0) {
gapless.current.total_samples -= samples;
@ -217,7 +218,7 @@ std::tuple<u32, u32> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf, SparseOut
av_packet_free(&pkt);
return {frames_decoded, samples_written};
return {frames_decoded, samples_decoded, false};
}
u32 AjmMp3Decoder::GetNextFrameSize(const AjmInstanceGapless& gapless) const {

View File

@ -71,8 +71,8 @@ public:
void GetInfo(void* out_info) const override;
AjmSidebandFormat GetFormat() const override;
u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const override;
std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
AjmInstanceGapless& gapless) override;
std::tuple<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
AjmInstanceGapless& gapless) override;
static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl,
AjmDecMp3ParseFrame* frame);

View File

@ -301,7 +301,7 @@ static void AudioOutputThread(PortOut* port, const std::stop_token& stop) {
timer.Start();
{
std::unique_lock lock{port->mutex};
if (port->output_cv.wait(lock, stop, [&] { return port->output_ready; })) {
if (port->output_ready) {
port->impl->Output(port->output_buffer);
port->output_ready = false;
}
@ -413,7 +413,6 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) {
samples_sent = port.buffer_frames * port.format_info.num_channels;
}
}
port.output_cv.notify_one();
return samples_sent;
}

View File

@ -9,23 +9,22 @@
namespace Libraries::AvPlayer {
s32 PS4_SYSV_ABI sceAvPlayerAddSource(SceAvPlayerHandle handle, const char* filename) {
s32 PS4_SYSV_ABI sceAvPlayerAddSource(AvPlayerHandle handle, const char* filename) {
LOG_TRACE(Lib_AvPlayer, "filename = {}", filename);
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
const auto res = handle->AddSource(filename);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->AddSource(filename);
}
s32 PS4_SYSV_ABI sceAvPlayerAddSourceEx(SceAvPlayerHandle handle, SceAvPlayerUriType uriType,
SceAvPlayerSourceDetails* sourceDetails) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
if (handle == nullptr) {
s32 PS4_SYSV_ABI sceAvPlayerAddSourceEx(AvPlayerHandle handle, AvPlayerUriType uri_type,
AvPlayerSourceDetails* source_details) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr || uri_type != AvPlayerUriType::Source) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
return ORBIS_OK;
const auto path = std::string_view(source_details->uri.name, source_details->uri.length);
return handle->AddSourceEx(path, source_details->source_type);
}
int PS4_SYSV_ABI sceAvPlayerChangeStream() {
@ -33,28 +32,24 @@ int PS4_SYSV_ABI sceAvPlayerChangeStream() {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAvPlayerClose(SceAvPlayerHandle handle) {
s32 PS4_SYSV_ABI sceAvPlayerClose(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) {
LOG_TRACE(Lib_AvPlayer, "returning ORBIS_AVPLAYER_ERROR_INVALID_PARAMS");
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
delete handle;
LOG_TRACE(Lib_AvPlayer, "returning ORBIS_OK");
return ORBIS_OK;
}
u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(SceAvPlayerHandle handle) {
u64 PS4_SYSV_ABI sceAvPlayerCurrentTime(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
const auto res = handle->CurrentTime();
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->CurrentTime();
}
s32 PS4_SYSV_ABI sceAvPlayerDisableStream(SceAvPlayerHandle handle, u32 stream_id) {
s32 PS4_SYSV_ABI sceAvPlayerDisableStream(AvPlayerHandle handle, u32 stream_id) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
@ -62,60 +57,49 @@ s32 PS4_SYSV_ABI sceAvPlayerDisableStream(SceAvPlayerHandle handle, u32 stream_i
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAvPlayerEnableStream(SceAvPlayerHandle handle, u32 stream_id) {
s32 PS4_SYSV_ABI sceAvPlayerEnableStream(AvPlayerHandle handle, u32 stream_id) {
LOG_TRACE(Lib_AvPlayer, "stream_id = {}", stream_id);
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
const auto res = handle->EnableStream(stream_id);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->EnableStream(stream_id);
}
bool PS4_SYSV_ABI sceAvPlayerGetAudioData(SceAvPlayerHandle handle, SceAvPlayerFrameInfo* p_info) {
bool PS4_SYSV_ABI sceAvPlayerGetAudioData(AvPlayerHandle handle, AvPlayerFrameInfo* p_info) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr || p_info == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
return false;
}
const auto res = handle->GetAudioData(*p_info);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->GetAudioData(*p_info);
}
s32 PS4_SYSV_ABI sceAvPlayerGetStreamInfo(SceAvPlayerHandle handle, u32 stream_id,
SceAvPlayerStreamInfo* p_info) {
s32 PS4_SYSV_ABI sceAvPlayerGetStreamInfo(AvPlayerHandle handle, u32 stream_id,
AvPlayerStreamInfo* p_info) {
LOG_TRACE(Lib_AvPlayer, "stream_id = {}", stream_id);
if (handle == nullptr || p_info == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
const auto res = handle->GetStreamInfo(stream_id, *p_info);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->GetStreamInfo(stream_id, *p_info);
}
bool PS4_SYSV_ABI sceAvPlayerGetVideoData(SceAvPlayerHandle handle,
SceAvPlayerFrameInfo* video_info) {
bool PS4_SYSV_ABI sceAvPlayerGetVideoData(AvPlayerHandle handle, AvPlayerFrameInfo* video_info) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr || video_info == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
return false;
}
const auto res = handle->GetVideoData(*video_info);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->GetVideoData(*video_info);
}
bool PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(SceAvPlayerHandle handle,
SceAvPlayerFrameInfoEx* video_info) {
bool PS4_SYSV_ABI sceAvPlayerGetVideoDataEx(AvPlayerHandle handle,
AvPlayerFrameInfoEx* video_info) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr || video_info == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
return false;
}
const auto res = handle->GetVideoData(*video_info);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->GetVideoData(*video_info);
}
SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) {
AvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(AvPlayerInitData* data) {
LOG_TRACE(Lib_AvPlayer, "called");
if (data == nullptr) {
return nullptr;
@ -125,15 +109,14 @@ SceAvPlayerHandle PS4_SYSV_ABI sceAvPlayerInit(SceAvPlayerInitData* data) {
data->memory_replacement.allocate_texture == nullptr ||
data->memory_replacement.deallocate == nullptr ||
data->memory_replacement.deallocate_texture == nullptr) {
LOG_ERROR(Lib_AvPlayer, "All allocators are required for AVPlayer Initialisation.");
LOG_ERROR(Lib_AvPlayer, "All allocators are required for AvPlayer Initialisation.");
return nullptr;
}
return new AvPlayer(*data);
}
s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
SceAvPlayerHandle* p_player) {
s32 PS4_SYSV_ABI sceAvPlayerInitEx(const AvPlayerInitDataEx* p_data, AvPlayerHandle* p_player) {
LOG_TRACE(Lib_AvPlayer, "called");
if (p_data == nullptr || p_player == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
@ -143,11 +126,11 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
p_data->memory_replacement.allocate_texture == nullptr ||
p_data->memory_replacement.deallocate == nullptr ||
p_data->memory_replacement.deallocate_texture == nullptr) {
LOG_ERROR(Lib_AvPlayer, "All allocators are required for AVPlayer Initialisation.");
LOG_ERROR(Lib_AvPlayer, "All allocators are required for AvPlayer Initialisation.");
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
SceAvPlayerInitData data = {};
AvPlayerInitData data = {};
data.memory_replacement = p_data->memory_replacement;
data.file_replacement = p_data->file_replacement;
data.event_replacement = p_data->event_replacement;
@ -159,18 +142,15 @@ s32 PS4_SYSV_ABI sceAvPlayerInitEx(const SceAvPlayerInitDataEx* p_data,
return ORBIS_OK;
}
bool PS4_SYSV_ABI sceAvPlayerIsActive(SceAvPlayerHandle handle) {
bool PS4_SYSV_ABI sceAvPlayerIsActive(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) {
LOG_TRACE(Lib_AvPlayer, "returning false");
return false;
}
const auto res = handle->IsActive();
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->IsActive();
}
s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(SceAvPlayerHandle handle, uint64_t time) {
s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(AvPlayerHandle handle, uint64_t time) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called, time (msec) = {}", time);
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
@ -178,22 +158,20 @@ s32 PS4_SYSV_ABI sceAvPlayerJumpToTime(SceAvPlayerHandle handle, uint64_t time)
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAvPlayerPause(SceAvPlayerHandle handle) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
s32 PS4_SYSV_ABI sceAvPlayerPause(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
return ORBIS_OK;
return handle->Pause();
}
s32 PS4_SYSV_ABI sceAvPlayerPostInit(SceAvPlayerHandle handle, SceAvPlayerPostInitData* data) {
s32 PS4_SYSV_ABI sceAvPlayerPostInit(AvPlayerHandle handle, AvPlayerPostInitData* data) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr || data == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
const auto res = handle->PostInit(*data);
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->PostInit(*data);
}
s32 PS4_SYSV_ABI sceAvPlayerPrintf(const char* format, ...) {
@ -201,29 +179,28 @@ s32 PS4_SYSV_ABI sceAvPlayerPrintf(const char* format, ...) {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAvPlayerResume(SceAvPlayerHandle handle) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
s32 PS4_SYSV_ABI sceAvPlayerResume(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
return ORBIS_OK;
return handle->Resume();
}
s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(SceAvPlayerHandle handle,
SceAvPlayerAvSyncMode sync_mode) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
s32 PS4_SYSV_ABI sceAvPlayerSetAvSyncMode(AvPlayerHandle handle, AvPlayerAvSyncMode sync_mode) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
return ORBIS_OK;
return handle->SetAvSyncMode(sync_mode);
}
s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(SceAvPlayerLogCallback log_cb, void* user_data) {
s32 PS4_SYSV_ABI sceAvPlayerSetLogCallback(AvPlayerLogCallback log_cb, void* user_data) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAvPlayerSetLooping(SceAvPlayerHandle handle, bool loop_flag) {
s32 PS4_SYSV_ABI sceAvPlayerSetLooping(AvPlayerHandle handle, bool loop_flag) {
LOG_TRACE(Lib_AvPlayer, "called, looping = {}", loop_flag);
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
@ -234,43 +211,36 @@ s32 PS4_SYSV_ABI sceAvPlayerSetLooping(SceAvPlayerHandle handle, bool loop_flag)
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAvPlayerSetTrickSpeed(SceAvPlayerHandle handle, s32 trick_speed) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called");
s32 PS4_SYSV_ABI sceAvPlayerSetTrickSpeed(AvPlayerHandle handle, s32 trick_speed) {
LOG_ERROR(Lib_AvPlayer, "(STUBBED) called speed = {}", trick_speed);
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAvPlayerStart(SceAvPlayerHandle handle) {
s32 PS4_SYSV_ABI sceAvPlayerStart(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
const auto res = handle->Start();
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->Start();
}
s32 PS4_SYSV_ABI sceAvPlayerStop(SceAvPlayerHandle handle) {
s32 PS4_SYSV_ABI sceAvPlayerStop(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) {
LOG_TRACE(Lib_AvPlayer, "returning ORBIS_AVPLAYER_ERROR_INVALID_PARAMS");
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
const auto res = handle->Stop();
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->Stop();
}
s32 PS4_SYSV_ABI sceAvPlayerStreamCount(SceAvPlayerHandle handle) {
s32 PS4_SYSV_ABI sceAvPlayerStreamCount(AvPlayerHandle handle) {
LOG_TRACE(Lib_AvPlayer, "called");
if (handle == nullptr) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
}
const auto res = handle->GetStreamCount();
LOG_TRACE(Lib_AvPlayer, "returning {}", res);
return res;
return handle->GetStreamCount();
}
s32 PS4_SYSV_ABI sceAvPlayerVprintf(const char* format, va_list args) {

View File

@ -16,38 +16,38 @@ namespace Libraries::AvPlayer {
class AvPlayer;
using SceAvPlayerHandle = AvPlayer*;
using AvPlayerHandle = AvPlayer*;
enum class SceAvPlayerUriType : u32 {
enum class AvPlayerUriType : u32 {
Source = 0,
};
struct SceAvPlayerUri {
struct AvPlayerUri {
const char* name;
u32 length;
};
enum class SceAvPlayerSourceType {
enum class AvPlayerSourceType {
Unknown = 0,
FileMp4 = 1,
Hls = 8,
};
enum class SceAvPlayerStreamType : u32 {
enum class AvPlayerStreamType : u32 {
Video,
Audio,
TimedText,
Unknown,
};
struct SceAvPlayerSourceDetails {
SceAvPlayerUri uri;
struct AvPlayerSourceDetails {
AvPlayerUri uri;
u8 reserved1[64];
SceAvPlayerSourceType source_type;
AvPlayerSourceType source_type;
u8 reserved2[44];
};
struct SceAvPlayerAudio {
struct AvPlayerAudio {
u16 channel_count;
u8 reserved1[2];
u32 sample_rate;
@ -55,50 +55,50 @@ struct SceAvPlayerAudio {
u8 language_code[4];
};
struct SceAvPlayerVideo {
struct AvPlayerVideo {
u32 width;
u32 height;
f32 aspect_ratio;
char language_code[4];
};
struct SceAvPlayerTextPosition {
struct AvPlayerTextPosition {
u16 top;
u16 left;
u16 bottom;
u16 right;
};
struct SceAvPlayerTimedText {
struct AvPlayerTimedText {
u8 language_code[4];
u16 text_size;
u16 font_size;
SceAvPlayerTextPosition position;
AvPlayerTextPosition position;
};
union SceAvPlayerStreamDetails {
union AvPlayerStreamDetails {
u8 reserved[16];
SceAvPlayerAudio audio;
SceAvPlayerVideo video;
SceAvPlayerTimedText subs;
AvPlayerAudio audio;
AvPlayerVideo video;
AvPlayerTimedText subs;
};
struct SceAvPlayerFrameInfo {
u8* pData;
struct AvPlayerFrameInfo {
u8* p_data;
u8 reserved[4];
u64 timestamp;
SceAvPlayerStreamDetails details;
AvPlayerStreamDetails details;
};
struct SceAvPlayerStreamInfo {
SceAvPlayerStreamType type;
struct AvPlayerStreamInfo {
AvPlayerStreamType type;
u8 reserved[4];
SceAvPlayerStreamDetails details;
AvPlayerStreamDetails details;
u64 duration;
u64 start_time;
};
struct SceAvPlayerAudioEx {
struct AvPlayerAudioEx {
u16 channel_count;
u8 reserved[2];
u32 sample_rate;
@ -107,7 +107,7 @@ struct SceAvPlayerAudioEx {
u8 reserved1[64];
};
struct SceAvPlayerVideoEx {
struct AvPlayerVideoEx {
u32 width;
u32 height;
f32 aspect_ratio;
@ -124,53 +124,53 @@ struct SceAvPlayerVideoEx {
u8 reserved1[37];
};
struct SceAvPlayerTimedTextEx {
struct AvPlayerTimedTextEx {
u8 language_code[4];
u8 reserved[12];
u8 reserved1[64];
};
union SceAvPlayerStreamDetailsEx {
SceAvPlayerAudioEx audio;
SceAvPlayerVideoEx video;
SceAvPlayerTimedTextEx subs;
union AvPlayerStreamDetailsEx {
AvPlayerAudioEx audio;
AvPlayerVideoEx video;
AvPlayerTimedTextEx subs;
u8 reserved1[80];
};
struct SceAvPlayerFrameInfoEx {
void* pData;
struct AvPlayerFrameInfoEx {
void* p_data;
u8 reserved[4];
u64 timestamp;
SceAvPlayerStreamDetailsEx details;
AvPlayerStreamDetailsEx details;
};
using SceAvPlayerAllocate = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size);
using SceAvPlayerDeallocate = void PS4_SYSV_ABI (*)(void* p, void* mem);
using SceAvPlayerAllocateTexture = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size);
using SceAvPlayerDeallocateTexture = void PS4_SYSV_ABI (*)(void* p, void* mem);
using AvPlayerAllocate = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size);
using AvPlayerDeallocate = void PS4_SYSV_ABI (*)(void* p, void* mem);
using AvPlayerAllocateTexture = void* PS4_SYSV_ABI (*)(void* p, u32 align, u32 size);
using AvPlayerDeallocateTexture = void PS4_SYSV_ABI (*)(void* p, void* mem);
struct SceAvPlayerMemAllocator {
struct AvPlayerMemAllocator {
void* object_ptr;
SceAvPlayerAllocate allocate;
SceAvPlayerDeallocate deallocate;
SceAvPlayerAllocateTexture allocate_texture;
SceAvPlayerDeallocateTexture deallocate_texture;
AvPlayerAllocate allocate;
AvPlayerDeallocate deallocate;
AvPlayerAllocateTexture allocate_texture;
AvPlayerDeallocateTexture deallocate_texture;
};
using SceAvPlayerOpenFile = s32 PS4_SYSV_ABI (*)(void* p, const char* name);
using SceAvPlayerCloseFile = s32 PS4_SYSV_ABI (*)(void* p);
using SceAvPlayerReadOffsetFile = s32 PS4_SYSV_ABI (*)(void* p, u8* buf, u64 pos, u32 len);
using SceAvPlayerSizeFile = u64 PS4_SYSV_ABI (*)(void* p);
using AvPlayerOpenFile = s32 PS4_SYSV_ABI (*)(void* p, const char* name);
using AvPlayerCloseFile = s32 PS4_SYSV_ABI (*)(void* p);
using AvPlayerReadOffsetFile = s32 PS4_SYSV_ABI (*)(void* p, u8* buf, u64 pos, u32 len);
using AvPlayerSizeFile = u64 PS4_SYSV_ABI (*)(void* p);
struct SceAvPlayerFileReplacement {
struct AvPlayerFileReplacement {
void* object_ptr;
SceAvPlayerOpenFile open;
SceAvPlayerCloseFile close;
SceAvPlayerReadOffsetFile readOffset;
SceAvPlayerSizeFile size;
AvPlayerOpenFile open;
AvPlayerCloseFile close;
AvPlayerReadOffsetFile read_offset;
AvPlayerSizeFile size;
};
enum class SceAvPlayerEvents {
enum class AvPlayerEvents {
StateStop = 0x01,
StateReady = 0x02,
StatePlay = 0x03,
@ -182,26 +182,26 @@ enum class SceAvPlayerEvents {
DrmError = 0x40,
};
using SceAvPlayerEventCallback = void PS4_SYSV_ABI (*)(void* p, SceAvPlayerEvents event, s32 src_id,
void* data);
using AvPlayerEventCallback = void PS4_SYSV_ABI (*)(void* p, AvPlayerEvents event, s32 src_id,
void* data);
struct SceAvPlayerEventReplacement {
struct AvPlayerEventReplacement {
void* object_ptr;
SceAvPlayerEventCallback event_callback;
AvPlayerEventCallback event_callback;
};
enum class SceAvPlayerDebuglevels {
enum class AvPlayerDebuglevels {
None,
Info,
Warnings,
All,
};
struct SceAvPlayerInitData {
SceAvPlayerMemAllocator memory_replacement;
SceAvPlayerFileReplacement file_replacement;
SceAvPlayerEventReplacement event_replacement;
SceAvPlayerDebuglevels debug_level;
struct AvPlayerInitData {
AvPlayerMemAllocator memory_replacement;
AvPlayerFileReplacement file_replacement;
AvPlayerEventReplacement event_replacement;
AvPlayerDebuglevels debug_level;
u32 base_priority;
s32 num_output_video_framebuffers;
bool auto_start;
@ -209,13 +209,13 @@ struct SceAvPlayerInitData {
const char* default_language;
};
struct SceAvPlayerInitDataEx {
struct AvPlayerInitDataEx {
size_t this_size;
SceAvPlayerMemAllocator memory_replacement;
SceAvPlayerFileReplacement file_replacement;
SceAvPlayerEventReplacement event_replacement;
AvPlayerMemAllocator memory_replacement;
AvPlayerFileReplacement file_replacement;
AvPlayerEventReplacement event_replacement;
const char* default_language;
SceAvPlayerDebuglevels debug_level;
AvPlayerDebuglevels debug_level;
u32 audio_decoder_priority;
u32 audio_decoder_affinity;
u32 video_decoder_priority;
@ -233,25 +233,25 @@ struct SceAvPlayerInitDataEx {
u8 reserved[3];
};
enum class SceAvPlayerVideoDecoderType {
enum class AvPlayerVideoDecoderType {
Default = 0,
Reserved1,
Software,
Software2,
};
enum class SceAvPlayerAudioDecoderType {
enum class AvPlayerAudioDecoderType {
Default = 0,
Reserved1,
Reserved2,
};
struct SceAvPlayerDecoderInit {
struct AvPlayerDecoderInit {
union {
SceAvPlayerVideoDecoderType video_type;
SceAvPlayerAudioDecoderType audio_type;
AvPlayerVideoDecoderType video_type;
AvPlayerAudioDecoderType audio_type;
u8 reserved[4];
} decoderType;
} decoder_type;
union {
struct {
s32 cpu_affinity_mask;
@ -261,34 +261,34 @@ struct SceAvPlayerDecoderInit {
u8 compute_queue_id;
u8 enable_interlaced;
u8 reserved[16];
} avcSw2;
} avc_sw2;
struct {
u8 audio_channel_order;
u8 reserved[27];
} aac;
u8 reserved[28];
} decoderParams;
} decoder_params;
};
struct SceAvPlayerHTTPCtx {
struct AvPlayerHTTPCtx {
u32 http_context_id;
u32 ssl_context_id;
};
struct SceAvPlayerPostInitData {
struct AvPlayerPostInitData {
u32 demux_video_buffer_size;
SceAvPlayerDecoderInit video_decoder_init;
SceAvPlayerDecoderInit audio_decoder_init;
SceAvPlayerHTTPCtx http_context;
AvPlayerDecoderInit video_decoder_init;
AvPlayerDecoderInit audio_decoder_init;
AvPlayerHTTPCtx http_context;
u8 reserved[56];
};
enum class SceAvPlayerAvSyncMode {
enum class AvPlayerAvSyncMode {
Default = 0,
None,
};
using SceAvPlayerLogCallback = int PS4_SYSV_ABI (*)(void* p, const char* format, va_list args);
using AvPlayerLogCallback = int PS4_SYSV_ABI (*)(void* p, const char* format, va_list args);
void RegisterLib(Core::Loader::SymbolsResolver* sym);

View File

@ -13,9 +13,9 @@ static bool iequals(std::string_view l, std::string_view r) {
return std::ranges::equal(l, r, [](u8 a, u8 b) { return std::tolower(a) == std::tolower(b); });
}
SceAvPlayerSourceType GetSourceType(std::string_view path) {
AvPlayerSourceType GetSourceType(std::string_view path) {
if (path.empty()) {
return SceAvPlayerSourceType::Unknown;
return AvPlayerSourceType::Unknown;
}
std::string_view name = path;
@ -25,14 +25,14 @@ SceAvPlayerSourceType GetSourceType(std::string_view path) {
// -> schema://server.domain/path/to/file.ext/and/beyond
name = path.substr(0, path.find_first_of("?#"));
if (name.empty()) {
return SceAvPlayerSourceType::Unknown;
return AvPlayerSourceType::Unknown;
}
}
// schema://server.domain/path/to/file.ext/and/beyond -> .ext/and/beyond
auto ext = name.substr(name.rfind('.'));
if (ext.empty()) {
return SceAvPlayerSourceType::Unknown;
return AvPlayerSourceType::Unknown;
}
// .ext/and/beyond -> .ext
@ -40,14 +40,14 @@ SceAvPlayerSourceType GetSourceType(std::string_view path) {
if (iequals(ext, ".mp4") || iequals(ext, ".m4v") || iequals(ext, ".m3d") ||
iequals(ext, ".m4a") || iequals(ext, ".mov")) {
return SceAvPlayerSourceType::FileMp4;
return AvPlayerSourceType::FileMp4;
}
if (iequals(ext, ".m3u8")) {
return SceAvPlayerSourceType::Hls;
return AvPlayerSourceType::Hls;
}
return SceAvPlayerSourceType::Unknown;
return AvPlayerSourceType::Unknown;
}
} // namespace Libraries::AvPlayer

View File

@ -16,6 +16,7 @@
namespace Libraries::AvPlayer {
enum class AvState {
Unknown,
Initial,
AddingSource,
Ready,
@ -64,6 +65,10 @@ public:
m_queue.emplace(std::forward<T>(value));
}
T& Front() {
return m_queue.front();
}
std::optional<T> Pop() {
if (Size() == 0) {
return std::nullopt;
@ -84,6 +89,6 @@ private:
std::queue<T> m_queue{};
};
SceAvPlayerSourceType GetSourceType(std::string_view path);
AvPlayerSourceType GetSourceType(std::string_view path);
} // namespace Libraries::AvPlayer

View File

@ -17,6 +17,7 @@ class IDataStreamer {
public:
virtual ~IDataStreamer() = default;
virtual bool Init(std::string_view path) = 0;
virtual void Reset() = 0;
virtual AVIOContext* GetContext() = 0;
};

View File

@ -14,7 +14,7 @@ constexpr u32 AVPLAYER_AVIO_BUFFER_SIZE = 4096;
namespace Libraries::AvPlayer {
AvPlayerFileStreamer::AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement)
AvPlayerFileStreamer::AvPlayerFileStreamer(const AvPlayerFileReplacement& file_replacement)
: m_file_replacement(file_replacement) {}
AvPlayerFileStreamer::~AvPlayerFileStreamer() {
@ -43,6 +43,10 @@ bool AvPlayerFileStreamer::Init(std::string_view path) {
return true;
}
void AvPlayerFileStreamer::Reset() {
m_position = 0;
}
s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) {
const auto self = reinterpret_cast<AvPlayerFileStreamer*>(opaque);
if (self->m_position >= self->m_file_size) {
@ -51,7 +55,7 @@ s32 AvPlayerFileStreamer::ReadPacket(void* opaque, u8* buffer, s32 size) {
if (self->m_position + size > self->m_file_size) {
size = self->m_file_size - self->m_position;
}
const auto read_offset = self->m_file_replacement.readOffset;
const auto read_offset = self->m_file_replacement.read_offset;
const auto ptr = self->m_file_replacement.object_ptr;
const auto bytes_read = read_offset(ptr, buffer, self->m_position, size);
if (bytes_read == 0 && size != 0) {

View File

@ -13,10 +13,11 @@ namespace Libraries::AvPlayer {
class AvPlayerFileStreamer : public IDataStreamer {
public:
AvPlayerFileStreamer(const SceAvPlayerFileReplacement& file_replacement);
AvPlayerFileStreamer(const AvPlayerFileReplacement& file_replacement);
~AvPlayerFileStreamer();
bool Init(std::string_view path) override;
void Reset() override;
AVIOContext* GetContext() override {
return m_avio_context;
@ -26,7 +27,7 @@ private:
static s32 ReadPacket(void* opaque, u8* buffer, s32 size);
static s64 Seek(void* opaque, s64 buffer, int whence);
SceAvPlayerFileReplacement m_file_replacement;
AvPlayerFileReplacement m_file_replacement;
int m_fd = -1;
u64 m_position{};

View File

@ -58,7 +58,7 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position
auto const self = reinterpret_cast<AvPlayer*>(handle);
std::lock_guard guard(self->m_file_io_mutex);
const auto read_offset = self->m_init_data_original.file_replacement.readOffset;
const auto read_offset = self->m_init_data_original.file_replacement.read_offset;
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
return Core::ExecuteGuest(read_offset, ptr, buffer, position, length);
}
@ -72,38 +72,46 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) {
return Core::ExecuteGuest(size, ptr);
}
SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) {
SceAvPlayerInitData result = data;
AvPlayerInitData AvPlayer::StubInitData(const AvPlayerInitData& data) {
AvPlayerInitData result = data;
result.memory_replacement.object_ptr = this;
result.memory_replacement.allocate = &AvPlayer::Allocate;
result.memory_replacement.deallocate = &AvPlayer::Deallocate;
result.memory_replacement.allocate_texture = &AvPlayer::AllocateTexture;
result.memory_replacement.deallocate_texture = &AvPlayer::DeallocateTexture;
if (data.file_replacement.open == nullptr || data.file_replacement.close == nullptr ||
data.file_replacement.readOffset == nullptr || data.file_replacement.size == nullptr) {
data.file_replacement.read_offset == nullptr || data.file_replacement.size == nullptr) {
result.file_replacement = {};
} else {
result.file_replacement.object_ptr = this;
result.file_replacement.open = &AvPlayer::OpenFile;
result.file_replacement.close = &AvPlayer::CloseFile;
result.file_replacement.readOffset = &AvPlayer::ReadOffsetFile;
result.file_replacement.read_offset = &AvPlayer::ReadOffsetFile;
result.file_replacement.size = &AvPlayer::SizeFile;
}
return result;
}
AvPlayer::AvPlayer(const SceAvPlayerInitData& data)
AvPlayer::AvPlayer(const AvPlayerInitData& data)
: m_init_data(StubInitData(data)), m_init_data_original(data),
m_state(std::make_unique<AvPlayerState>(m_init_data)) {}
s32 AvPlayer::PostInit(const SceAvPlayerPostInitData& data) {
s32 AvPlayer::PostInit(const AvPlayerPostInitData& data) {
m_state->PostInit(data);
return ORBIS_OK;
}
s32 AvPlayer::AddSource(std::string_view path) {
if (path.empty()) {
return ORBIS_AVPLAYER_ERROR_INVALID_PARAMS;
return AddSourceEx(path, AvPlayerSourceType::Unknown);
}
s32 AvPlayer::AddSourceEx(std::string_view path, AvPlayerSourceType source_type) {
if (source_type == AvPlayerSourceType::Unknown) {
source_type = GetSourceType(path);
}
if (source_type == AvPlayerSourceType::Hls) {
LOG_ERROR(Lib_AvPlayer, "HTTP Live Streaming is not implemented");
return ORBIS_AVPLAYER_ERROR_NOT_SUPPORTED;
}
if (!m_state->AddSource(path, GetSourceType(path))) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
@ -122,7 +130,7 @@ s32 AvPlayer::GetStreamCount() {
return res;
}
s32 AvPlayer::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info) {
s32 AvPlayer::GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info) {
if (!m_state->GetStreamInfo(stream_index, info)) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
}
@ -140,27 +148,49 @@ s32 AvPlayer::EnableStream(u32 stream_index) {
}
s32 AvPlayer::Start() {
if (!m_state->Start()) {
if (m_state == nullptr || !m_state->Start()) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
}
return ORBIS_OK;
}
bool AvPlayer::GetVideoData(SceAvPlayerFrameInfo& video_info) {
s32 AvPlayer::Pause() {
if (m_state == nullptr || !m_state->Pause()) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
}
return ORBIS_OK;
}
s32 AvPlayer::Resume() {
if (m_state == nullptr || !m_state->Resume()) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
}
return ORBIS_OK;
}
s32 AvPlayer::SetAvSyncMode(AvPlayerAvSyncMode sync_mode) {
if (m_state == nullptr) {
return ORBIS_AVPLAYER_ERROR_OPERATION_FAILED;
}
m_state->SetAvSyncMode(sync_mode);
return ORBIS_OK;
}
bool AvPlayer::GetVideoData(AvPlayerFrameInfo& video_info) {
if (m_state == nullptr) {
return false;
}
return m_state->GetVideoData(video_info);
}
bool AvPlayer::GetVideoData(SceAvPlayerFrameInfoEx& video_info) {
bool AvPlayer::GetVideoData(AvPlayerFrameInfoEx& video_info) {
if (m_state == nullptr) {
return false;
}
return m_state->GetVideoData(video_info);
}
bool AvPlayer::GetAudioData(SceAvPlayerFrameInfo& audio_info) {
bool AvPlayer::GetAudioData(AvPlayerFrameInfo& audio_info) {
if (m_state == nullptr) {
return false;
}

View File

@ -19,17 +19,21 @@ namespace Libraries::AvPlayer {
class AvPlayer {
public:
AvPlayer(const SceAvPlayerInitData& data);
AvPlayer(const AvPlayerInitData& data);
s32 PostInit(const SceAvPlayerPostInitData& data);
s32 PostInit(const AvPlayerPostInitData& data);
s32 AddSource(std::string_view filename);
s32 AddSourceEx(std::string_view path, AvPlayerSourceType source_type);
s32 GetStreamCount();
s32 GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
s32 GetStreamInfo(u32 stream_index, AvPlayerStreamInfo& info);
s32 EnableStream(u32 stream_index);
s32 Start();
bool GetAudioData(SceAvPlayerFrameInfo& audio_info);
bool GetVideoData(SceAvPlayerFrameInfo& video_info);
bool GetVideoData(SceAvPlayerFrameInfoEx& video_info);
s32 Pause();
s32 Resume();
s32 SetAvSyncMode(AvPlayerAvSyncMode sync_mode);
bool GetAudioData(AvPlayerFrameInfo& audio_info);
bool GetVideoData(AvPlayerFrameInfo& video_info);
bool GetVideoData(AvPlayerFrameInfoEx& video_info);
bool IsActive();
u64 CurrentTime();
s32 Stop();
@ -48,13 +52,12 @@ private:
static int PS4_SYSV_ABI ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length);
static u64 PS4_SYSV_ABI SizeFile(void* handle);
SceAvPlayerInitData StubInitData(const SceAvPlayerInitData& data);
AvPlayerInitData StubInitData(const AvPlayerInitData& data);
SceAvPlayerInitData m_init_data{};
SceAvPlayerInitData m_init_data_original{};
AvPlayerInitData m_init_data{};
AvPlayerInitData m_init_data_original{};
std::mutex m_file_io_mutex{};
std::atomic_bool m_has_source{};
std::unique_ptr<AvPlayerState> m_state{};
};

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