Merge branch 'shadps4-emu:main' into main

This commit is contained in:
DanielSvoboda 2024-12-08 20:32:00 -03:00 committed by GitHub
commit f2b82d3db1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
381 changed files with 15344 additions and 8673 deletions

View File

@ -10,7 +10,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dis
fi fi
# Default clang-format points to default 3.5 version one # Default clang-format points to default 3.5 version one
CLANG_FORMAT=clang-format-17 CLANG_FORMAT=clang-format-18
$CLANG_FORMAT --version $CLANG_FORMAT --version
if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then

View File

@ -18,7 +18,7 @@ jobs:
continue-on-error: true continue-on-error: true
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: fsfe/reuse-action@v4 - uses: fsfe/reuse-action@v5
clang-format: clang-format:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -30,9 +30,9 @@ jobs:
- name: Install - name: Install
run: | run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main' sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main'
sudo apt update sudo apt update
sudo apt install clang-format-17 sudo apt install clang-format-18
- name: Build - name: Build
env: env:
COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}
@ -92,7 +92,7 @@ jobs:
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS
- name: Upload Windows SDL artifact - name: Upload Windows SDL artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@ -146,7 +146,7 @@ jobs:
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS
- name: Deploy and Package - name: Deploy and Package
run: | run: |
@ -315,7 +315,7 @@ jobs:
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
- name: Package and Upload Linux(ubuntu64) SDL artifact - name: Package and Upload Linux(ubuntu64) SDL artifact
run: | run: |
@ -371,7 +371,7 @@ jobs:
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel3 run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
- name: Run AppImage packaging script - name: Run AppImage packaging script
run: ./.github/linux-appimage-qt.sh run: ./.github/linux-appimage-qt.sh

4
.gitignore vendored
View File

@ -414,3 +414,7 @@ FodyWeavers.xsd
# for macOS # for macOS
**/.DS_Store **/.DS_Store
# JetBrains
.idea
cmake-build-*

5
.gitmodules vendored
View File

@ -102,3 +102,8 @@
[submodule "externals/LibAtrac9"] [submodule "externals/LibAtrac9"]
path = externals/LibAtrac9 path = externals/LibAtrac9
url = https://github.com/shadps4-emu/ext-LibAtrac9.git url = https://github.com/shadps4-emu/ext-LibAtrac9.git
shallow = true
[submodule "externals/libpng"]
path = externals/libpng
url = https://github.com/pnggroup/libpng
shallow = true

View File

@ -1,7 +1,8 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project # SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
cmake_minimum_required(VERSION 3.16.3) # Version 3.24 needed for FetchContent OVERRIDE_FIND_PACKAGE
cmake_minimum_required(VERSION 3.24)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_STANDARD_REQUIRED True)
@ -110,18 +111,20 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_
find_package(Boost 1.84.0 CONFIG) find_package(Boost 1.84.0 CONFIG)
find_package(FFmpeg 5.1.2 MODULE) find_package(FFmpeg 5.1.2 MODULE)
find_package(fmt 10.2.0 CONFIG) find_package(fmt 10.2.0 CONFIG)
find_package(glslang 14.2.0 CONFIG) find_package(glslang 15 CONFIG)
find_package(half 1.12.0 MODULE) find_package(half 1.12.0 MODULE)
find_package(magic_enum 0.9.6 CONFIG) find_package(magic_enum 0.9.7 CONFIG)
find_package(PNG 1.6 MODULE)
find_package(RenderDoc 1.6.0 MODULE) find_package(RenderDoc 1.6.0 MODULE)
find_package(SDL3 3.1.2 CONFIG) find_package(SDL3 3.1.2 CONFIG)
find_package(stb MODULE)
find_package(toml11 4.2.0 CONFIG) find_package(toml11 4.2.0 CONFIG)
find_package(tsl-robin-map 1.3.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG)
find_package(VulkanHeaders 1.3.289 CONFIG) find_package(VulkanHeaders 1.4.303 CONFIG)
find_package(VulkanMemoryAllocator 3.1.0 CONFIG) find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
find_package(xbyak 7.07 CONFIG) find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE) find_package(xxHash 0.8.2 MODULE)
find_package(zlib-ng 2.1.7 MODULE) find_package(ZLIB 1.3 MODULE)
find_package(Zydis 5.0.0 CONFIG) find_package(Zydis 5.0.0 CONFIG)
find_package(pugixml 1.14 CONFIG) find_package(pugixml 1.14 CONFIG)
@ -176,10 +179,6 @@ if(ENABLE_QT_GUI)
qt_add_resources(TRANSLATIONS ${TRANSLATIONS_QRC}) qt_add_resources(TRANSLATIONS ${TRANSLATIONS_QRC})
endif() endif()
set(AUDIO_CORE src/audio_core/sdl_audio.cpp
src/audio_core/sdl_audio.h
)
set(AJM_LIB src/core/libraries/ajm/ajm.cpp set(AJM_LIB src/core/libraries/ajm/ajm.cpp
src/core/libraries/ajm/ajm.h src/core/libraries/ajm/ajm.h
src/core/libraries/ajm/ajm_at9.cpp src/core/libraries/ajm/ajm_at9.cpp
@ -199,6 +198,9 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
src/core/libraries/audio/audioin.h src/core/libraries/audio/audioin.h
src/core/libraries/audio/audioout.cpp src/core/libraries/audio/audioout.cpp
src/core/libraries/audio/audioout.h src/core/libraries/audio/audioout.h
src/core/libraries/audio/sdl_audio.cpp
src/core/libraries/audio/sdl_audio.h
src/core/libraries/audio/audioout_error.h
src/core/libraries/ngs2/ngs2.cpp src/core/libraries/ngs2/ngs2.cpp
src/core/libraries/ngs2/ngs2.h src/core/libraries/ngs2/ngs2.h
) )
@ -208,30 +210,43 @@ set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp
src/core/libraries/gnmdriver/gnm_error.h src/core/libraries/gnmdriver/gnm_error.h
) )
set(KERNEL_LIB src/core/libraries/kernel/event_flag/event_flag.cpp set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp
src/core/libraries/kernel/event_flag/event_flag.h src/core/libraries/kernel/sync/mutex.h
src/core/libraries/kernel/event_flag/event_flag_obj.cpp src/core/libraries/kernel/sync/semaphore.h
src/core/libraries/kernel/event_flag/event_flag_obj.h src/core/libraries/kernel/threads/condvar.cpp
src/core/libraries/kernel/threads/event_flag.cpp
src/core/libraries/kernel/threads/exception.cpp
src/core/libraries/kernel/threads/exception.h
src/core/libraries/kernel/threads/mutex.cpp
src/core/libraries/kernel/threads/pthread_attr.cpp
src/core/libraries/kernel/threads/pthread_clean.cpp
src/core/libraries/kernel/threads/pthread.cpp
src/core/libraries/kernel/threads/pthread_spec.cpp
src/core/libraries/kernel/threads/rwlock.cpp src/core/libraries/kernel/threads/rwlock.cpp
src/core/libraries/kernel/threads/semaphore.cpp src/core/libraries/kernel/threads/semaphore.cpp
src/core/libraries/kernel/threads/keys.cpp src/core/libraries/kernel/threads/sleepq.cpp
src/core/libraries/kernel/threads/threads.h src/core/libraries/kernel/threads/sleepq.h
src/core/libraries/kernel/cpu_management.cpp src/core/libraries/kernel/threads/stack.cpp
src/core/libraries/kernel/cpu_management.h src/core/libraries/kernel/threads/tcb.cpp
src/core/libraries/kernel/event_queue.cpp src/core/libraries/kernel/threads/pthread.h
src/core/libraries/kernel/event_queue.h src/core/libraries/kernel/threads/thread_state.cpp
src/core/libraries/kernel/event_queues.cpp src/core/libraries/kernel/threads/thread_state.h
src/core/libraries/kernel/event_queues.h src/core/libraries/kernel/process.cpp
src/core/libraries/kernel/process.h
src/core/libraries/kernel/equeue.cpp
src/core/libraries/kernel/equeue.h
src/core/libraries/kernel/file_system.cpp src/core/libraries/kernel/file_system.cpp
src/core/libraries/kernel/file_system.h src/core/libraries/kernel/file_system.h
src/core/libraries/kernel/libkernel.cpp src/core/libraries/kernel/kernel.cpp
src/core/libraries/kernel/libkernel.h src/core/libraries/kernel/kernel.h
src/core/libraries/kernel/memory_management.cpp src/core/libraries/kernel/memory.cpp
src/core/libraries/kernel/memory_management.h src/core/libraries/kernel/memory.h
src/core/libraries/kernel/thread_management.cpp src/core/libraries/kernel/threads.cpp
src/core/libraries/kernel/thread_management.h src/core/libraries/kernel/threads.h
src/core/libraries/kernel/time_management.cpp src/core/libraries/kernel/time.cpp
src/core/libraries/kernel/time_management.h src/core/libraries/kernel/time.h
src/core/libraries/kernel/orbis_error.h
src/core/libraries/kernel/posix_error.h
) )
set(NETWORK_LIBS src/core/libraries/network/http.cpp set(NETWORK_LIBS src/core/libraries/network/http.cpp
@ -247,6 +262,21 @@ set(NETWORK_LIBS src/core/libraries/network/http.cpp
src/core/libraries/network/ssl.h src/core/libraries/network/ssl.h
) )
set(AVPLAYER_LIB src/core/libraries/avplayer/avplayer_common.cpp
src/core/libraries/avplayer/avplayer_common.h
src/core/libraries/avplayer/avplayer_file_streamer.cpp
src/core/libraries/avplayer/avplayer_file_streamer.h
src/core/libraries/avplayer/avplayer_impl.cpp
src/core/libraries/avplayer/avplayer_impl.h
src/core/libraries/avplayer/avplayer_source.cpp
src/core/libraries/avplayer/avplayer_source.h
src/core/libraries/avplayer/avplayer_state.cpp
src/core/libraries/avplayer/avplayer_state.h
src/core/libraries/avplayer/avplayer.cpp
src/core/libraries/avplayer/avplayer.h
src/core/libraries/avplayer/avplayer_error.h
)
set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/system/commondialog.h src/core/libraries/system/commondialog.h
src/core/libraries/system/msgdialog.cpp src/core/libraries/system/msgdialog.cpp
@ -268,30 +298,22 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/save_data/dialog/savedatadialog_ui.h src/core/libraries/save_data/dialog/savedatadialog_ui.h
src/core/libraries/system/sysmodule.cpp src/core/libraries/system/sysmodule.cpp
src/core/libraries/system/sysmodule.h src/core/libraries/system/sysmodule.h
src/core/libraries/system/system_error.h
src/core/libraries/system/systemservice.cpp src/core/libraries/system/systemservice.cpp
src/core/libraries/system/systemservice.h src/core/libraries/system/systemservice.h
src/core/libraries/system/systemservice_error.h
src/core/libraries/system/userservice.cpp src/core/libraries/system/userservice.cpp
src/core/libraries/system/userservice.h src/core/libraries/system/userservice.h
src/core/libraries/system/userservice_error.h
src/core/libraries/app_content/app_content.cpp src/core/libraries/app_content/app_content.cpp
src/core/libraries/app_content/app_content.h src/core/libraries/app_content/app_content.h
src/core/libraries/app_content/app_content_error.h
src/core/libraries/rtc/rtc.cpp src/core/libraries/rtc/rtc.cpp
src/core/libraries/rtc/rtc.h src/core/libraries/rtc/rtc.h
src/core/libraries/rtc/rtc_error.h src/core/libraries/rtc/rtc_error.h
src/core/libraries/disc_map/disc_map.cpp src/core/libraries/disc_map/disc_map.cpp
src/core/libraries/disc_map/disc_map.h src/core/libraries/disc_map/disc_map.h
src/core/libraries/disc_map/disc_map_codes.h src/core/libraries/disc_map/disc_map_codes.h
src/core/libraries/avplayer/avplayer_common.cpp
src/core/libraries/avplayer/avplayer_common.h
src/core/libraries/avplayer/avplayer_file_streamer.cpp
src/core/libraries/avplayer/avplayer_file_streamer.h
src/core/libraries/avplayer/avplayer_impl.cpp
src/core/libraries/avplayer/avplayer_impl.h
src/core/libraries/avplayer/avplayer_source.cpp
src/core/libraries/avplayer/avplayer_source.h
src/core/libraries/avplayer/avplayer_state.cpp
src/core/libraries/avplayer/avplayer_state.h
src/core/libraries/avplayer/avplayer.cpp
src/core/libraries/avplayer/avplayer.h
src/core/libraries/ngs2/ngs2.cpp src/core/libraries/ngs2/ngs2.cpp
src/core/libraries/ngs2/ngs2.h src/core/libraries/ngs2/ngs2.h
src/core/libraries/ngs2/ngs2_error.h src/core/libraries/ngs2/ngs2_error.h
@ -309,6 +331,8 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/remote_play/remoteplay.h src/core/libraries/remote_play/remoteplay.h
src/core/libraries/share_play/shareplay.cpp src/core/libraries/share_play/shareplay.cpp
src/core/libraries/share_play/shareplay.h src/core/libraries/share_play/shareplay.h
src/core/libraries/razor_cpu/razor_cpu.cpp
src/core/libraries/razor_cpu/razor_cpu.h
) )
set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
@ -316,6 +340,7 @@ set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
src/core/libraries/videoout/driver.h src/core/libraries/videoout/driver.h
src/core/libraries/videoout/video_out.cpp src/core/libraries/videoout/video_out.cpp
src/core/libraries/videoout/video_out.h src/core/libraries/videoout/video_out.h
src/core/libraries/videoout/videoout_error.h
) )
set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp
@ -333,18 +358,28 @@ set(IME_LIB src/core/libraries/ime/error_dialog.cpp
src/core/libraries/ime/ime_ui.h src/core/libraries/ime/ime_ui.h
src/core/libraries/ime/ime.cpp src/core/libraries/ime/ime.cpp
src/core/libraries/ime/ime.h src/core/libraries/ime/ime.h
src/core/libraries/ime/ime_error.h
) )
set(PAD_LIB src/core/libraries/pad/pad.cpp set(PAD_LIB src/core/libraries/pad/pad.cpp
src/core/libraries/pad/pad.h src/core/libraries/pad/pad.h
src/core/libraries/pad/pad_errors.h
) )
set(PNG_LIB src/core/libraries/libpng/pngdec.cpp set(PNG_LIB src/core/libraries/libpng/pngdec.cpp
src/core/libraries/libpng/pngdec.h src/core/libraries/libpng/pngdec.h
src/core/libraries/libpng/pngdec_error.h
)
set(JPEG_LIB src/core/libraries/jpeg/jpeg_error.h
src/core/libraries/jpeg/jpegenc.cpp
src/core/libraries/jpeg/jpegenc.h
) )
set(PLAYGO_LIB src/core/libraries/playgo/playgo.cpp set(PLAYGO_LIB src/core/libraries/playgo/playgo.cpp
src/core/libraries/playgo/playgo.h src/core/libraries/playgo/playgo.h
src/core/libraries/playgo/playgo_dialog.cpp
src/core/libraries/playgo/playgo_dialog.h
src/core/libraries/playgo/playgo_types.h src/core/libraries/playgo/playgo_types.h
) )
@ -359,6 +394,7 @@ set(USBD_LIB src/core/libraries/usbd/usbd.cpp
set(FIBER_LIB src/core/libraries/fiber/fiber.cpp set(FIBER_LIB src/core/libraries/fiber/fiber.cpp
src/core/libraries/fiber/fiber.h src/core/libraries/fiber/fiber.h
src/core/libraries/fiber/fiber_error.h
) )
set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp
@ -368,6 +404,7 @@ set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp
src/core/libraries/videodec/videodec2_avc.h src/core/libraries/videodec/videodec2_avc.h
src/core/libraries/videodec/videodec.cpp src/core/libraries/videodec/videodec.cpp
src/core/libraries/videodec/videodec.h src/core/libraries/videodec/videodec.h
src/core/libraries/videodec/videodec_error.h
src/core/libraries/videodec/videodec_impl.cpp src/core/libraries/videodec/videodec_impl.cpp
src/core/libraries/videodec/videodec_impl.h src/core/libraries/videodec/videodec_impl.h
) )
@ -380,6 +417,7 @@ set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
src/core/libraries/np_trophy/np_trophy.h src/core/libraries/np_trophy/np_trophy.h
src/core/libraries/np_trophy/trophy_ui.cpp src/core/libraries/np_trophy/trophy_ui.cpp
src/core/libraries/np_trophy/trophy_ui.h src/core/libraries/np_trophy/trophy_ui.h
src/core/libraries/np_trophy/np_trophy_error.h
) )
set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
@ -401,10 +439,14 @@ set(DEV_TOOLS src/core/devtools/layer.cpp
src/core/devtools/widget/frame_graph.cpp src/core/devtools/widget/frame_graph.cpp
src/core/devtools/widget/frame_graph.h src/core/devtools/widget/frame_graph.h
src/core/devtools/widget/imgui_memory_editor.h src/core/devtools/widget/imgui_memory_editor.h
src/core/devtools/widget/memory_map.cpp
src/core/devtools/widget/memory_map.h
src/core/devtools/widget/reg_popup.cpp src/core/devtools/widget/reg_popup.cpp
src/core/devtools/widget/reg_popup.h src/core/devtools/widget/reg_popup.h
src/core/devtools/widget/reg_view.cpp src/core/devtools/widget/reg_view.cpp
src/core/devtools/widget/reg_view.h src/core/devtools/widget/reg_view.h
src/core/devtools/widget/shader_list.cpp
src/core/devtools/widget/shader_list.h
src/core/devtools/widget/text_editor.cpp src/core/devtools/widget/text_editor.cpp
src/core/devtools/widget/text_editor.h src/core/devtools/widget/text_editor.h
) )
@ -453,7 +495,12 @@ set(COMMON src/common/logging/backend.cpp
src/common/signal_context.h src/common/signal_context.h
src/common/signal_context.cpp src/common/signal_context.cpp
src/common/singleton.h src/common/singleton.h
src/common/slab_heap.h
src/common/slot_vector.h src/common/slot_vector.h
src/common/spin_lock.cpp
src/common/spin_lock.h
src/common/stb.cpp
src/common/stb.h
src/common/string_util.cpp src/common/string_util.cpp
src/common/string_util.h src/common/string_util.h
src/common/thread.cpp src/common/thread.cpp
@ -461,6 +508,7 @@ set(COMMON src/common/logging/backend.cpp
src/common/types.h src/common/types.h
src/common/uint128.h src/common/uint128.h
src/common/unique_function.h src/common/unique_function.h
src/common/va_ctx.h
src/common/version.h src/common/version.h
src/common/ntapi.h src/common/ntapi.h
src/common/ntapi.cpp src/common/ntapi.cpp
@ -485,6 +533,12 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/crypto/crypto.cpp src/core/crypto/crypto.cpp
src/core/crypto/crypto.h src/core/crypto/crypto.h
src/core/crypto/keys.h src/core/crypto/keys.h
src/core/devices/base_device.cpp
src/core/devices/base_device.h
src/core/devices/ioccom.h
src/core/devices/logger.cpp
src/core/devices/logger.h
src/core/devices/nop_device.h
src/core/file_format/pfs.h src/core/file_format/pfs.h
src/core/file_format/pkg.cpp src/core/file_format/pkg.cpp
src/core/file_format/pkg.h src/core/file_format/pkg.h
@ -508,10 +562,10 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/loader/elf.h src/core/loader/elf.h
src/core/loader/symbols_resolver.h src/core/loader/symbols_resolver.h
src/core/loader/symbols_resolver.cpp src/core/loader/symbols_resolver.cpp
src/core/libraries/error_codes.h
src/core/libraries/libs.h src/core/libraries/libs.h
src/core/libraries/libs.cpp src/core/libraries/libs.cpp
${AJM_LIB} ${AJM_LIB}
${AVPLAYER_LIB}
${AUDIO_LIB} ${AUDIO_LIB}
${GNM_LIB} ${GNM_LIB}
${KERNEL_LIB} ${KERNEL_LIB}
@ -522,6 +576,7 @@ set(CORE src/core/aerolib/stubs.cpp
${VIDEOOUT_LIB} ${VIDEOOUT_LIB}
${NP_LIBS} ${NP_LIBS}
${PNG_LIB} ${PNG_LIB}
${JPEG_LIB}
${PLAYGO_LIB} ${PLAYGO_LIB}
${RANDOM_LIB} ${RANDOM_LIB}
${USBD_LIB} ${USBD_LIB}
@ -541,10 +596,10 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/platform.h src/core/platform.h
src/core/signals.cpp src/core/signals.cpp
src/core/signals.h src/core/signals.h
src/core/thread.cpp
src/core/thread.h
src/core/tls.cpp src/core/tls.cpp
src/core/tls.h src/core/tls.h
src/core/virtual_memory.cpp
src/core/virtual_memory.h
) )
if (ARCHITECTURE STREQUAL "x86_64") if (ARCHITECTURE STREQUAL "x86_64")
@ -657,8 +712,6 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/buffer_cache/word_manager.h src/video_core/buffer_cache/word_manager.h
src/video_core/renderer_vulkan/liverpool_to_vk.cpp src/video_core/renderer_vulkan/liverpool_to_vk.cpp
src/video_core/renderer_vulkan/liverpool_to_vk.h src/video_core/renderer_vulkan/liverpool_to_vk.h
src/video_core/renderer_vulkan/renderer_vulkan.cpp
src/video_core/renderer_vulkan/renderer_vulkan.h
src/video_core/renderer_vulkan/vk_common.cpp src/video_core/renderer_vulkan/vk_common.cpp
src/video_core/renderer_vulkan/vk_common.h src/video_core/renderer_vulkan/vk_common.h
src/video_core/renderer_vulkan/vk_compute_pipeline.cpp src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@ -677,6 +730,8 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/renderer_vulkan/vk_pipeline_common.h src/video_core/renderer_vulkan/vk_pipeline_common.h
src/video_core/renderer_vulkan/vk_platform.cpp src/video_core/renderer_vulkan/vk_platform.cpp
src/video_core/renderer_vulkan/vk_platform.h src/video_core/renderer_vulkan/vk_platform.h
src/video_core/renderer_vulkan/vk_presenter.cpp
src/video_core/renderer_vulkan/vk_presenter.h
src/video_core/renderer_vulkan/vk_rasterizer.cpp src/video_core/renderer_vulkan/vk_rasterizer.cpp
src/video_core/renderer_vulkan/vk_rasterizer.h src/video_core/renderer_vulkan/vk_rasterizer.h
src/video_core/renderer_vulkan/vk_resource_pool.cpp src/video_core/renderer_vulkan/vk_resource_pool.cpp
@ -820,12 +875,20 @@ endif()
create_target_directory_groups(shadps4) 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) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers)
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(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") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
if (ENABLE_DISCORD_RPC)
target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC)
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD)
endif()
if (APPLE) if (APPLE)
option(USE_SYSTEM_VULKAN_LOADER "Enables using the system Vulkan loader instead of directly linking with MoltenVK. Useful for loading validation layers." OFF) option(USE_SYSTEM_VULKAN_LOADER "Enables using the system Vulkan loader instead of directly linking with MoltenVK. Useful for loading validation layers." OFF)
if (USE_SYSTEM_VULKAN_LOADER) if (USE_SYSTEM_VULKAN_LOADER)
@ -850,9 +913,9 @@ if (NOT ENABLE_QT_GUI)
endif() endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
target_link_libraries(shadps4 PRIVATE cryptoppwin zlib-ng::zlib) target_link_libraries(shadps4 PRIVATE cryptoppwin)
else() else()
target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp zlib-ng::zlib) target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp)
endif() endif()
if (ENABLE_QT_GUI) if (ENABLE_QT_GUI)
@ -924,7 +987,10 @@ if (ENABLE_QT_GUI)
set_target_properties(shadps4 PROPERTIES set_target_properties(shadps4 PROPERTIES
# WIN32_EXECUTABLE ON # WIN32_EXECUTABLE ON
MACOSX_BUNDLE ON MACOSX_BUNDLE ON
MACOSX_BUNDLE_ICON_FILE shadPS4.icns) MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in"
MACOSX_BUNDLE_ICON_FILE "shadPS4.icns"
MACOSX_BUNDLE_SHORT_VERSION_STRING "0.4.1"
)
set_source_files_properties(src/images/shadPS4.icns PROPERTIES set_source_files_properties(src/images/shadPS4.icns PROPERTIES
MACOSX_PACKAGE_LOCATION Resources) MACOSX_PACKAGE_LOCATION Resources)

View File

@ -7,6 +7,7 @@ path = [
".github/FUNDING.yml", ".github/FUNDING.yml",
".github/shadps4.png", ".github/shadps4.png",
".gitmodules", ".gitmodules",
"dist/MacOSBundleInfo.plist.in",
"dist/net.shadps4.shadPS4.desktop", "dist/net.shadps4.shadPS4.desktop",
"dist/net.shadps4.shadPS4_metadata.pot", "dist/net.shadps4.shadPS4_metadata.pot",
"dist/net.shadps4.shadPS4.metainfo.xml", "dist/net.shadps4.shadPS4.metainfo.xml",
@ -41,6 +42,7 @@ path = [
"src/images/refresh_icon.png", "src/images/refresh_icon.png",
"src/images/settings_icon.png", "src/images/settings_icon.png",
"src/images/stop_icon.png", "src/images/stop_icon.png",
"src/images/utils_icon.png",
"src/images/shadPS4.icns", "src/images/shadPS4.icns",
"src/images/shadps4.ico", "src/images/shadps4.ico",
"src/images/net.shadps4.shadPS4.svg", "src/images/net.shadps4.shadPS4.svg",
@ -68,7 +70,7 @@ SPDX-FileCopyrightText = "2019-2024 Baldur Karlsson"
SPDX-License-Identifier = "MIT" SPDX-License-Identifier = "MIT"
[[annotations]] [[annotations]]
path = "externals/stb_image.h" path = "externals/stb/**"
precedence = "aggregate" precedence = "aggregate"
SPDX-FileCopyrightText = "2017 Sean Barrett" SPDX-FileCopyrightText = "2017 Sean Barrett"
SPDX-License-Identifier = "MIT" SPDX-License-Identifier = "MIT"

19
cmake/Findstb.cmake Normal file
View File

@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
find_path(stb_image_INCLUDE_DIR stb_image.h PATH_SUFFIXES stb)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(stb
REQUIRED_VARS stb_image_INCLUDE_DIR
)
if (stb_FOUND AND NOT TARGET stb::headers)
add_library(stb::headers INTERFACE IMPORTED)
set_property(TARGET stb::headers PROPERTY
INTERFACE_INCLUDE_DIRECTORIES
"${stb_image_INCLUDE_DIR}"
)
endif()
mark_as_advanced(stb_image_INCLUDE_DIR)

View File

@ -1,15 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
find_package(PkgConfig QUIET)
pkg_search_module(ZLIB_NG QUIET IMPORTED_TARGET zlib-ng)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(zlib-ng
REQUIRED_VARS ZLIB_NG_LINK_LIBRARIES
VERSION_VAR ZLIB_NG_VERSION
)
if (zlib-ng_FOUND AND NOT TARGET zlib-ng::zlib)
add_library(zlib-ng::zlib ALIAS PkgConfig::ZLIB_NG)
endif()

46
dist/MacOSBundleInfo.plist.in vendored Normal file
View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>shadps4</string>
<key>CFBundleIdentifier</key>
<string>com.shadps4-emu.shadps4</string>
<key>CFBundleExecutable</key>
<string>shadps4</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>LSMinimumSystemVersion</key>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>GCSupportsGameMode</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string></string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>

View File

@ -25,8 +25,8 @@ Once you are within the installer:
Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead. Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead.
1. Under the current, non beta version of Qt (at the time of writing 6.7.2), select the option `MSVC 2019 64-bit` or similar. 1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar.
If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2019 ARM64` instead. 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. Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space.
@ -35,7 +35,7 @@ Beware, this requires you to create a Qt account. If you do not want to do this,
Once you are finished, you will have to configure Qt within Visual Studio: Once you are finished, you will have to configure Qt within Visual Studio:
1. Tools -> Options -> Qt -> Versions 1. Tools -> Options -> Qt -> Versions
2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.2\msvc2019_64` 2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.3\msvc2022_64`
3. Enable the default checkmark on the new version you just created. 3. Enable the default checkmark on the new version you just created.
### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win) ### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win)
@ -55,16 +55,16 @@ Go through the Git for Windows installation as normal
3. If you want to build shadPS4 with the Qt Gui: 3. If you want to build shadPS4 with the Qt Gui:
1. Click x64-Clang-Release and select "Manage Configurations" 1. Click x64-Clang-Release and select "Manage Configurations"
2. Look for "CMake command arguments" and add to the text field 2. Look for "CMake command arguments" and add to the text field
`-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.2\msvc2019_64` `-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.3\msvc2022_64`
(Change Qt path if you've installed it to non-default path) (Change Qt path if you've installed it to non-default path)
3. Press CTRL+S to save and wait a moment for CMake generation 3. Press CTRL+S to save and wait a moment for CMake generation
4. Change the project to build to shadps4.exe 4. Change the project to build to shadps4.exe
5. Build -> Build All 5. Build -> Build All
Your shadps4.exe will be in `c:\path\to\source\Build\x64-Clang-Release\` Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\`
To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal: To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal:
`C:\Qt\6.7.2\msvc2019_64\bin\windeployqt.exe "c:\path\to\shadps4.exe"` `C:\Qt\6.7.3\msvc2022_64\bin\windeployqt.exe "C:\path\to\shadps4.exe"`
(Change Qt path if you've installed it to non-default path) (Change Qt path if you've installed it to non-default path)
## Option 2: MSYS2/MinGW ## Option 2: MSYS2/MinGW
@ -79,7 +79,7 @@ Normal x86-based computers, follow:
1. Open "MSYS2 MINGW64" from your new applications 1. Open "MSYS2 MINGW64" from your new applications
2. Run `pacman -Syu`, let it complete; 2. Run `pacman -Syu`, let it complete;
3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg` 3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-rapidjson mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg`
1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools mingw-w64-x86_64-qt6-multimedia` 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` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4` 5. Run `cd shadPS4`
@ -93,7 +93,7 @@ ARM64-based computers, follow:
1. Open "MSYS2 CLANGARM64" from your new applications 1. Open "MSYS2 CLANGARM64" from your new applications
2. Run `pacman -Syu`, let it complete; 2. Run `pacman -Syu`, let it complete;
3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg` 3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-rapidjson mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg`
1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia` 1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia`
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4` 5. Run `cd shadPS4`

View File

@ -0,0 +1,19 @@
<!--
SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later
-->
### Install Vulkan SDK and \*ensure `spirv-cross` and `glslc` are in PATH\*.
1. Enable `dumpShaders` in config.toml
2. Run `spirv-cross -V fs_0x000000.spv --output fs_0x000000.glsl` to decompile the SPIR-V IR to GLSL.
3. Edit the GLSL file as you wish
4. To compile back to SPIR-V, run (change the _**-fshader-stage**_ to correct stage):
`glslc --target-env=vulkan1.3 --target-spv=spv1.6 -fshader-stage=frag fs_0x000000.glsl -o fs_0x000000.spv`
5. Put the updated .spv file to `shader/patch` folder with the same name as the original shader
6. Enable `patchShaders` in config.toml

View File

@ -35,10 +35,11 @@ else()
if (NOT TARGET cryptopp::cryptopp) if (NOT TARGET cryptopp::cryptopp)
set(CRYPTOPP_INSTALL OFF) set(CRYPTOPP_INSTALL OFF)
set(CRYPTOPP_BUILD_TESTING OFF) set(CRYPTOPP_BUILD_TESTING OFF)
set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp/) set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp)
add_subdirectory(cryptopp-cmake) add_subdirectory(cryptopp-cmake)
file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h") file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h")
target_include_directories(cryptopp INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/cryptopp") # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file
set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp")
endif() endif()
endif() endif()
@ -52,14 +53,23 @@ file(GLOB LIBATRAC9_SOURCES LibAtrac9/C/src/*.c)
add_library(LibAtrac9 STATIC ${LIBATRAC9_SOURCES}) add_library(LibAtrac9 STATIC ${LIBATRAC9_SOURCES})
target_include_directories(LibAtrac9 INTERFACE LibAtrac9/C/src) target_include_directories(LibAtrac9 INTERFACE LibAtrac9/C/src)
# Zlib-Ng # zlib
if (NOT TARGET zlib-ng::zlib) if (NOT TARGET ZLIB::ZLIB)
set(ZLIB_ENABLE_TESTS OFF) set(ZLIB_ENABLE_TESTS OFF)
set(WITH_GTEST OFF) set(WITH_GTEST OFF)
set(WITH_NEW_STRATEGIES ON) set(WITH_NEW_STRATEGIES ON)
set(WITH_NATIVE_INSTRUCTIONS ON) set(WITH_NATIVE_INSTRUCTIONS ON)
add_subdirectory(zlib-ng) set(ZLIB_COMPAT ON CACHE BOOL "" FORCE)
add_library(zlib-ng::zlib ALIAS zlib) include(FetchContent)
FetchContent_Declare(
ZLIB
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib-ng"
OVERRIDE_FIND_PACKAGE
)
FetchContent_MakeAvailable(ZLIB)
add_library(ZLIB::ZLIB ALIAS zlib)
# libpng expects this variable to exist after its find_package(ZLIB)
set(ZLIB_INCLUDE_DIRS "${FETCHCONTENT_BASE_DIR}/zlib-build")
endif() endif()
# SDL3 # SDL3
@ -153,6 +163,17 @@ if (NOT TARGET half::half)
add_library(half::half ALIAS half) add_library(half::half ALIAS half)
endif() endif()
# libpng
if (NOT TARGET PNG::PNG)
set(PNG_SHARED OFF CACHE BOOL "" FORCE)
set(PNG_STATIC ON CACHE BOOL "" FORCE)
set(PNG_TESTS OFF CACHE BOOL "" FORCE)
set(PNG_TOOLS OFF CACHE BOOL "" FORCE)
set(SKIP_INSTALL_ALL OFF CACHE BOOL "" FORCE)
add_subdirectory(libpng)
add_library(PNG::PNG ALIAS png_static)
endif()
if (APPLE) if (APPLE)
# date # date
if (NOT TARGET date::date-tz) if (NOT TARGET date::date-tz)
@ -195,9 +216,16 @@ endif()
# Discord RPC # Discord RPC
if (ENABLE_DISCORD_RPC) if (ENABLE_DISCORD_RPC)
set(BUILD_EXAMPLES OFF) set(BUILD_EXAMPLES OFF)
add_subdirectory(discord-rpc/) add_subdirectory(discord-rpc)
target_include_directories(discord-rpc INTERFACE discord-rpc/include) target_include_directories(discord-rpc INTERFACE discord-rpc/include)
endif() endif()
# GCN Headers # GCN Headers
add_subdirectory(gcn) add_subdirectory(gcn)
# stb
if (NOT TARGET stb::headers)
add_library(stb INTERFACE)
target_include_directories(stb INTERFACE stb)
add_library(stb::headers ALIAS stb)
endif()

2
externals/LibAtrac9 vendored

@ -1 +1 @@
Subproject commit 3acdcdc78f129c2e6145331ff650fa76dd88d62c Subproject commit 9640129dc6f2afbca6ceeca3019856e8653a5fb2

2
externals/date vendored

@ -1 +1 @@
Subproject commit dd8affc6de5755e07638bf0a14382d29549d6ee9 Subproject commit 28b7b232521ace2c8ef3f2ad4126daec3569c14f

2
externals/ext-boost vendored

@ -1 +1 @@
Subproject commit f2474e1b584fb7a3ed6f85ba875e6eacd742ec8a Subproject commit ca6f230e67be7cc45fc919057f07b2aee64dadc1

@ -1 +1 @@
Subproject commit e30b7d7fe228bfb3f6e41ce1040b44a15eb7d5e0 Subproject commit 27de97c826b6b40c255891c37ac046a25836a575

2
externals/glslang vendored

@ -1 +1 @@
Subproject commit e61d7bb3006f451968714e2f653412081871e1ee Subproject commit a0995c49ebcaca2c6d3b03efbabf74f3843decdb

1
externals/libpng vendored Submodule

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

@ -1 +1 @@
Subproject commit 126539e13cccdc2e75ce770e94f3c26403099fa5 Subproject commit 1a1824df7ac798177a521eed952720681b0bf482

2
externals/pugixml vendored

@ -1 +1 @@
Subproject commit 3b17184379fcaaeb7f1fbe08018b7fedf2640b3b Subproject commit 4bc14418d12d289dd9978fdce9490a45deeb653e

2
externals/sdl3 vendored

@ -1 +1 @@
Subproject commit 54e622c2e6af456bfef382fae44c17682d5ac88a Subproject commit 3a1d76d298db023f6cf37fb08ee766f20a4e12ab

2
externals/toml11 vendored

@ -1 +1 @@
Subproject commit f925e7f287c0008813c2294798cf9ca167fd9ffd Subproject commit 7f6c574ff5aa1053534e7e19c0a4f22bf4c6aaca

2
externals/tracy vendored

@ -1 +1 @@
Subproject commit b8061982cad0210b649541016c88ff5faa90733c Subproject commit 143a53d1985b8e52a7590a0daca30a0a7c653b42

2
externals/vma vendored

@ -1 +1 @@
Subproject commit 1c35ba99ce775f8342d87a83a3f0f696f99c2a39 Subproject commit 5a53a198945ba8260fbc58fadb788745ce6aa263

@ -1 +1 @@
Subproject commit d91597a82f881d473887b560a03a7edf2720b72c Subproject commit 6a74a7d65cafa19e38ec116651436cce6efd5b2e

2
externals/xbyak vendored

@ -1 +1 @@
Subproject commit d067f0d3f55696ae8bc9a25ad7012ee80f221d54 Subproject commit 4e44f4614ddbf038f2a6296f5b906d5c72691e0f

2
externals/xxhash vendored

@ -1 +1 @@
Subproject commit d4ad85e4afaad5c780f54db1dc967fff5a869ffd Subproject commit 2bf8313b934633b2a5b7e8fd239645b85e10c852

2
externals/zydis vendored

@ -1 +1 @@
Subproject commit 9d298eb8067ff62a237203d1e1470785033e185c Subproject commit bffbb610cfea643b98e87658b9058382f7522807

View File

@ -1,166 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "sdl_audio.h"
#include "common/assert.h"
#include "core/libraries/error_codes.h"
#include <SDL3/SDL_audio.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_timer.h>
#include <mutex> // std::unique_lock
namespace Audio {
constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold
s32 SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
Libraries::AudioOut::OrbisAudioOutParamFormat format) {
using Libraries::AudioOut::OrbisAudioOutParamFormat;
std::unique_lock lock{m_mutex};
for (int id = 0; id < portsOut.size(); id++) {
auto& port = portsOut[id];
if (!port.isOpen) {
port.isOpen = true;
port.type = type;
port.samples_num = samples_num;
port.freq = freq;
port.format = format;
SDL_AudioFormat sampleFormat;
switch (format) {
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO:
sampleFormat = SDL_AUDIO_S16;
port.channels_num = 1;
port.sample_size = 2;
break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO:
sampleFormat = SDL_AUDIO_F32;
port.channels_num = 1;
port.sample_size = 4;
break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO:
sampleFormat = SDL_AUDIO_S16;
port.channels_num = 2;
port.sample_size = 2;
break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO:
sampleFormat = SDL_AUDIO_F32;
port.channels_num = 2;
port.sample_size = 4;
break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH:
sampleFormat = SDL_AUDIO_S16;
port.channels_num = 8;
port.sample_size = 2;
break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH:
sampleFormat = SDL_AUDIO_F32;
port.channels_num = 8;
port.sample_size = 4;
break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD:
sampleFormat = SDL_AUDIO_S16;
port.channels_num = 8;
port.sample_size = 2;
break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD:
sampleFormat = SDL_AUDIO_F32;
port.channels_num = 8;
port.sample_size = 4;
break;
default:
UNREACHABLE_MSG("Unknown format");
}
for (int i = 0; i < port.channels_num; i++) {
port.volume[i] = Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB;
}
SDL_AudioSpec fmt;
SDL_zero(fmt);
fmt.format = sampleFormat;
fmt.channels = port.channels_num;
fmt.freq = freq; // Set frequency from the argument
port.stream =
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL);
SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port.stream));
return id + 1;
}
}
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL; // all ports are used
}
s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) {
std::shared_lock lock{m_mutex};
auto& port = portsOut[handle - 1];
if (!port.isOpen) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size);
lock.unlock(); // Unlock only after necessary operations
while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
SDL_Delay(0);
}
return result ? ORBIS_OK : -1;
}
s32 SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) {
using Libraries::AudioOut::OrbisAudioOutParamFormat;
std::shared_lock lock{m_mutex};
auto& port = portsOut[handle - 1];
if (!port.isOpen) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) {
auto bit = bitflag & 0x1u;
if (bit == 1) {
int src_index = i;
if (port.format ==
OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD ||
port.format == OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD) {
switch (i) {
case 4:
src_index = 6;
break;
case 5:
src_index = 7;
break;
case 6:
src_index = 4;
break;
case 7:
src_index = 5;
break;
default:
break;
}
}
port.volume[i] = volume[src_index];
}
}
return ORBIS_OK;
}
s32 SDLAudio::AudioOutGetStatus(s32 handle, int* type, int* channels_num) {
std::shared_lock lock{m_mutex};
auto& port = portsOut[handle - 1];
*type = port.type;
*channels_num = port.channels_num;
return ORBIS_OK;
}
} // namespace Audio

View File

@ -1,39 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <shared_mutex>
#include <SDL3/SDL_audio.h>
#include "core/libraries/audio/audioout.h"
namespace Audio {
class SDLAudio {
public:
SDLAudio() = default;
virtual ~SDLAudio() = default;
s32 AudioOutOpen(int type, u32 samples_num, u32 freq,
Libraries::AudioOut::OrbisAudioOutParamFormat format);
s32 AudioOutOutput(s32 handle, const void* ptr);
s32 AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume);
s32 AudioOutGetStatus(s32 handle, int* type, int* channels_num);
private:
struct PortOut {
SDL_AudioStream* stream = nullptr;
u32 samples_num = 0;
u32 freq = 0;
u32 format = -1;
int type = 0;
int channels_num = 0;
int volume[8] = {};
u8 sample_size = 0;
bool isOpen = false;
};
std::shared_mutex m_mutex;
std::array<PortOut, Libraries::AudioOut::SCE_AUDIO_OUT_NUM_PORTS> portsOut;
};
} // namespace Audio

View File

@ -22,6 +22,12 @@ template <typename T>
return static_cast<T>(value - value % size); return static_cast<T>(value - value % size);
} }
template <typename T>
requires std::is_integral_v<T>
[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
return (value & (alignment - 1)) == 0;
}
template <typename T> template <typename T>
requires std::is_integral_v<T> requires std::is_integral_v<T>
[[nodiscard]] constexpr bool Is16KBAligned(T value) { [[nodiscard]] constexpr bool Is16KBAligned(T value) {

View File

@ -3,13 +3,14 @@
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <common/version.h>
#include <fmt/core.h> #include <fmt/core.h>
#include <fmt/xchar.h> // for wstring support #include <fmt/xchar.h> // for wstring support
#include <toml.hpp> #include <toml.hpp>
#include "common/logging/formatter.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "config.h" #include "config.h"
#include "logging/formatter.h"
#include "version.h"
namespace toml { namespace toml {
template <typename TC, typename K> template <typename TC, typename K>
@ -46,11 +47,13 @@ static std::string backButtonBehavior = "left";
static bool useSpecialPad = false; static bool useSpecialPad = false;
static int specialPadClass = 1; static int specialPadClass = 1;
static bool isDebugDump = false; static bool isDebugDump = false;
static bool isShaderDebug = false;
static bool isShowSplash = false; static bool isShowSplash = false;
static bool isAutoUpdate = false; static bool isAutoUpdate = false;
static bool isNullGpu = false; static bool isNullGpu = false;
static bool shouldCopyGPUBuffers = false; static bool shouldCopyGPUBuffers = false;
static bool shouldDumpShaders = false; static bool shouldDumpShaders = false;
static bool shouldPatchShaders = true;
static u32 vblankDivider = 1; static u32 vblankDivider = 1;
static bool vkValidation = false; static bool vkValidation = false;
static bool vkValidationSync = false; static bool vkValidationSync = false;
@ -81,7 +84,8 @@ std::vector<std::string> m_pkg_viewer;
std::vector<std::string> m_elf_viewer; std::vector<std::string> m_elf_viewer;
std::vector<std::string> m_recent_files; std::vector<std::string> m_recent_files;
std::string emulator_language = "en"; std::string emulator_language = "en";
// Settings
// Language
u32 m_language = 1; // english u32 m_language = 1; // english
bool isNeoMode() { bool isNeoMode() {
@ -156,6 +160,10 @@ bool debugDump() {
return isDebugDump; return isDebugDump;
} }
bool collectShadersForDebug() {
return isShaderDebug;
}
bool showSplash() { bool showSplash() {
return isShowSplash; return isShowSplash;
} }
@ -176,6 +184,10 @@ bool dumpShaders() {
return shouldDumpShaders; return shouldDumpShaders;
} }
bool patchShaders() {
return shouldPatchShaders;
}
bool isRdocEnabled() { bool isRdocEnabled() {
return rdocEnable; return rdocEnable;
} }
@ -228,6 +240,10 @@ void setDebugDump(bool enable) {
isDebugDump = enable; isDebugDump = enable;
} }
void setCollectShaderForDebug(bool enable) {
isShaderDebug = enable;
}
void setShowSplash(bool enable) { void setShowSplash(bool enable) {
isShowSplash = enable; isShowSplash = enable;
} }
@ -334,6 +350,7 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
main_window_geometry_w = w; main_window_geometry_w = w;
main_window_geometry_h = h; main_window_geometry_h = h;
} }
bool addGameInstallDir(const std::filesystem::path& dir) { bool addGameInstallDir(const std::filesystem::path& dir) {
if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) == if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) ==
settings_install_dirs.end()) { settings_install_dirs.end()) {
@ -342,47 +359,60 @@ bool addGameInstallDir(const std::filesystem::path& dir) {
} }
return false; return false;
} }
void removeGameInstallDir(const std::filesystem::path& dir) { void removeGameInstallDir(const std::filesystem::path& dir) {
auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir); auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir);
if (iterator != settings_install_dirs.end()) { if (iterator != settings_install_dirs.end()) {
settings_install_dirs.erase(iterator); settings_install_dirs.erase(iterator);
} }
} }
void setAddonInstallDir(const std::filesystem::path& dir) { void setAddonInstallDir(const std::filesystem::path& dir) {
settings_addon_install_dir = dir; settings_addon_install_dir = dir;
} }
void setMainWindowTheme(u32 theme) { void setMainWindowTheme(u32 theme) {
mw_themes = theme; mw_themes = theme;
} }
void setIconSize(u32 size) { void setIconSize(u32 size) {
m_icon_size = size; m_icon_size = size;
} }
void setIconSizeGrid(u32 size) { void setIconSizeGrid(u32 size) {
m_icon_size_grid = size; m_icon_size_grid = size;
} }
void setSliderPosition(u32 pos) { void setSliderPosition(u32 pos) {
m_slider_pos = pos; m_slider_pos = pos;
} }
void setSliderPositionGrid(u32 pos) { void setSliderPositionGrid(u32 pos) {
m_slider_pos_grid = pos; m_slider_pos_grid = pos;
} }
void setTableMode(u32 mode) { void setTableMode(u32 mode) {
m_table_mode = mode; m_table_mode = mode;
} }
void setMainWindowWidth(u32 width) { void setMainWindowWidth(u32 width) {
m_window_size_W = width; m_window_size_W = width;
} }
void setMainWindowHeight(u32 height) { void setMainWindowHeight(u32 height) {
m_window_size_H = height; m_window_size_H = height;
} }
void setPkgViewer(const std::vector<std::string>& pkgList) { void setPkgViewer(const std::vector<std::string>& pkgList) {
m_pkg_viewer.resize(pkgList.size()); m_pkg_viewer.resize(pkgList.size());
m_pkg_viewer = pkgList; m_pkg_viewer = pkgList;
} }
void setElfViewer(const std::vector<std::string>& elfList) { void setElfViewer(const std::vector<std::string>& elfList) {
m_elf_viewer.resize(elfList.size()); m_elf_viewer.resize(elfList.size());
m_elf_viewer = elfList; m_elf_viewer = elfList;
} }
void setRecentFiles(const std::vector<std::string>& recentFiles) { void setRecentFiles(const std::vector<std::string>& recentFiles) {
m_recent_files.resize(recentFiles.size()); m_recent_files.resize(recentFiles.size());
m_recent_files = recentFiles; m_recent_files = recentFiles;
@ -395,18 +425,23 @@ void setEmulatorLanguage(std::string language) {
u32 getMainWindowGeometryX() { u32 getMainWindowGeometryX() {
return main_window_geometry_x; return main_window_geometry_x;
} }
u32 getMainWindowGeometryY() { u32 getMainWindowGeometryY() {
return main_window_geometry_y; return main_window_geometry_y;
} }
u32 getMainWindowGeometryW() { u32 getMainWindowGeometryW() {
return main_window_geometry_w; return main_window_geometry_w;
} }
u32 getMainWindowGeometryH() { u32 getMainWindowGeometryH() {
return main_window_geometry_h; return main_window_geometry_h;
} }
const std::vector<std::filesystem::path>& getGameInstallDirs() { const std::vector<std::filesystem::path>& getGameInstallDirs() {
return settings_install_dirs; return settings_install_dirs;
} }
std::filesystem::path getAddonInstallDir() { std::filesystem::path getAddonInstallDir() {
if (settings_addon_install_dir.empty()) { if (settings_addon_install_dir.empty()) {
// Default for users without a config file or a config file from before this option existed // Default for users without a config file or a config file from before this option existed
@ -414,36 +449,47 @@ std::filesystem::path getAddonInstallDir() {
} }
return settings_addon_install_dir; return settings_addon_install_dir;
} }
u32 getMainWindowTheme() { u32 getMainWindowTheme() {
return mw_themes; return mw_themes;
} }
u32 getIconSize() { u32 getIconSize() {
return m_icon_size; return m_icon_size;
} }
u32 getIconSizeGrid() { u32 getIconSizeGrid() {
return m_icon_size_grid; return m_icon_size_grid;
} }
u32 getSliderPosition() { u32 getSliderPosition() {
return m_slider_pos; return m_slider_pos;
} }
u32 getSliderPositionGrid() { u32 getSliderPositionGrid() {
return m_slider_pos_grid; return m_slider_pos_grid;
} }
u32 getTableMode() { u32 getTableMode() {
return m_table_mode; return m_table_mode;
} }
u32 getMainWindowWidth() { u32 getMainWindowWidth() {
return m_window_size_W; return m_window_size_W;
} }
u32 getMainWindowHeight() { u32 getMainWindowHeight() {
return m_window_size_H; return m_window_size_H;
} }
std::vector<std::string> getPkgViewer() { std::vector<std::string> getPkgViewer() {
return m_pkg_viewer; return m_pkg_viewer;
} }
std::vector<std::string> getElfViewer() { std::vector<std::string> getElfViewer() {
return m_elf_viewer; return m_elf_viewer;
} }
std::vector<std::string> getRecentFiles() { std::vector<std::string> getRecentFiles() {
return m_recent_files; return m_recent_files;
} }
@ -455,6 +501,7 @@ std::string getEmulatorLanguage() {
u32 GetLanguage() { u32 GetLanguage() {
return m_language; return m_language;
} }
void load(const std::filesystem::path& path) { void load(const std::filesystem::path& path) {
// If the configuration file does not exist, create it and return // If the configuration file does not exist, create it and return
std::error_code error; std::error_code error;
@ -513,6 +560,7 @@ void load(const std::filesystem::path& path) {
isNullGpu = toml::find_or<bool>(gpu, "nullGpu", false); isNullGpu = toml::find_or<bool>(gpu, "nullGpu", false);
shouldCopyGPUBuffers = toml::find_or<bool>(gpu, "copyGPUBuffers", false); shouldCopyGPUBuffers = toml::find_or<bool>(gpu, "copyGPUBuffers", false);
shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", false); shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", false);
shouldPatchShaders = toml::find_or<bool>(gpu, "patchShaders", true);
vblankDivider = toml::find_or<int>(gpu, "vblankDivider", 1); vblankDivider = toml::find_or<int>(gpu, "vblankDivider", 1);
} }
@ -532,6 +580,7 @@ void load(const std::filesystem::path& path) {
const toml::value& debug = data.at("Debug"); const toml::value& debug = data.at("Debug");
isDebugDump = toml::find_or<bool>(debug, "DebugDump", false); isDebugDump = toml::find_or<bool>(debug, "DebugDump", false);
isShaderDebug = toml::find_or<bool>(debug, "CollectShader", false);
} }
if (data.contains("GUI")) { if (data.contains("GUI")) {
@ -545,16 +594,10 @@ void load(const std::filesystem::path& path) {
m_window_size_W = toml::find_or<int>(gui, "mw_width", 0); m_window_size_W = toml::find_or<int>(gui, "mw_width", 0);
m_window_size_H = toml::find_or<int>(gui, "mw_height", 0); m_window_size_H = toml::find_or<int>(gui, "mw_height", 0);
// TODO Migration code, after a major release this should be removed. const auto install_dir_array =
auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {}); toml::find_or<std::vector<std::string>>(gui, "installDirs", {});
if (!old_game_install_dir.empty()) { for (const auto& dir : install_dir_array) {
addGameInstallDir(std::filesystem::path{old_game_install_dir}); addGameInstallDir(std::filesystem::path{dir});
} else {
const auto install_dir_array =
toml::find_or<std::vector<std::string>>(gui, "installDirs", {});
for (const auto& dir : install_dir_array) {
addGameInstallDir(std::filesystem::path{dir});
}
} }
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {});
@ -575,6 +618,7 @@ void load(const std::filesystem::path& path) {
m_language = toml::find_or<int>(settings, "consoleLanguage", 1); m_language = toml::find_or<int>(settings, "consoleLanguage", 1);
} }
} }
void save(const std::filesystem::path& path) { void save(const std::filesystem::path& path) {
toml::value data; toml::value data;
@ -618,6 +662,7 @@ void save(const std::filesystem::path& path) {
data["GPU"]["nullGpu"] = isNullGpu; data["GPU"]["nullGpu"] = isNullGpu;
data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers; data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers;
data["GPU"]["dumpShaders"] = shouldDumpShaders; data["GPU"]["dumpShaders"] = shouldDumpShaders;
data["GPU"]["patchShaders"] = shouldPatchShaders;
data["GPU"]["vblankDivider"] = vblankDivider; data["GPU"]["vblankDivider"] = vblankDivider;
data["Vulkan"]["gpuId"] = gpuId; data["Vulkan"]["gpuId"] = gpuId;
data["Vulkan"]["validation"] = vkValidation; data["Vulkan"]["validation"] = vkValidation;
@ -627,6 +672,7 @@ void save(const std::filesystem::path& path) {
data["Vulkan"]["rdocMarkersEnable"] = vkMarkers; data["Vulkan"]["rdocMarkersEnable"] = vkMarkers;
data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic; data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic;
data["Debug"]["DebugDump"] = isDebugDump; data["Debug"]["DebugDump"] = isDebugDump;
data["Debug"]["CollectShader"] = isShaderDebug;
data["GUI"]["theme"] = mw_themes; data["GUI"]["theme"] = mw_themes;
data["GUI"]["iconSize"] = m_icon_size; data["GUI"]["iconSize"] = m_icon_size;
data["GUI"]["sliderPos"] = m_slider_pos; data["GUI"]["sliderPos"] = m_slider_pos;
@ -655,9 +701,6 @@ void save(const std::filesystem::path& path) {
data["Settings"]["consoleLanguage"] = m_language; data["Settings"]["consoleLanguage"] = m_language;
// TODO Migration code, after a major release this should be removed.
data.at("GUI").as_table().erase("installDir");
std::ofstream file(path, std::ios::binary); std::ofstream file(path, std::ios::binary);
file << data; file << data;
file.close(); file.close();
@ -685,6 +728,7 @@ void setDefaultValues() {
useSpecialPad = false; useSpecialPad = false;
specialPadClass = 1; specialPadClass = 1;
isDebugDump = false; isDebugDump = false;
isShaderDebug = false;
isShowSplash = false; isShowSplash = false;
isAutoUpdate = false; isAutoUpdate = false;
isNullGpu = false; isNullGpu = false;

View File

@ -37,15 +37,18 @@ u32 getScreenHeight();
s32 getGpuId(); s32 getGpuId();
bool debugDump(); bool debugDump();
bool collectShadersForDebug();
bool showSplash(); bool showSplash();
bool autoUpdate(); bool autoUpdate();
bool nullGpu(); bool nullGpu();
bool copyGPUCmdBuffers(); bool copyGPUCmdBuffers();
bool dumpShaders(); bool dumpShaders();
bool patchShaders();
bool isRdocEnabled(); bool isRdocEnabled();
u32 vblankDiv(); u32 vblankDiv();
void setDebugDump(bool enable); void setDebugDump(bool enable);
void setCollectShaderForDebug(bool enable);
void setShowSplash(bool enable); void setShowSplash(bool enable);
void setAutoUpdate(bool enable); void setAutoUpdate(bool enable);
void setNullGpu(bool enable); void setNullGpu(bool enable);

View File

@ -41,7 +41,7 @@ enum MarkersPalette : int {
#define RENDERER_TRACE ZoneScopedC(RendererMarkerColor) #define RENDERER_TRACE ZoneScopedC(RendererMarkerColor)
#define HLE_TRACE ZoneScopedC(HleMarkerColor) #define HLE_TRACE ZoneScopedC(HleMarkerColor)
#define TRACE_HINT(str) ZoneText(str.c_str(), str.size()) #define TRACE_HINT(str) ZoneText(str.data(), str.size())
#define TRACE_WARN(msg) \ #define TRACE_WARN(msg) \
[](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::DarkOrange); }(msg); [](const auto& msg) { TracyMessageC(msg.c_str(), msg.size(), tracy::Color::DarkOrange); }(msg);

View File

@ -3,7 +3,7 @@
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include "src/common/discord_rpc_handler.h" #include "discord_rpc_handler.h"
namespace DiscordRPCHandler { namespace DiscordRPCHandler {
@ -17,7 +17,7 @@ void RPC::init() {
void RPC::setStatusIdling() { void RPC::setStatusIdling() {
DiscordRichPresence rpc{}; DiscordRichPresence rpc{};
rpc.largeImageKey = "https://github.com/shadps4-emu/shadPS4/raw/main/.github/shadps4.png"; rpc.largeImageKey = "https://cdn.jsdelivr.net/gh/shadps4-emu/shadPS4@main/.github/shadps4.png";
rpc.largeImageText = "shadPS4 is a PS4 emulator"; rpc.largeImageText = "shadPS4 is a PS4 emulator";
rpc.startTimestamp = startTimestamp; rpc.startTimestamp = startTimestamp;
rpc.details = "Idle"; rpc.details = "Idle";

View File

@ -377,16 +377,18 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
return false; return false;
} }
u64 size = GetSize(); if (False(file_access_mode & (FileAccessMode::Write | FileAccessMode::Append))) {
if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) { u64 size = GetSize();
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) {
return false; LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
} else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) { return false;
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); } else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) {
return false; LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
} else if (origin == SeekOrigin::End && offset > 0) { return false;
LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); } else if (origin == SeekOrigin::End && offset > 0) {
return false; LOG_ERROR(Common_Filesystem, "Seeking past the end of the file");
return false;
}
} }
errno = 0; errno = 0;

View File

@ -10,6 +10,7 @@
#include "common/concepts.h" #include "common/concepts.h"
#include "common/types.h" #include "common/types.h"
#include "enum.h"
namespace Common::FS { namespace Common::FS {
@ -42,6 +43,7 @@ enum class FileAccessMode {
*/ */
ReadAppend = Read | Append, ReadAppend = Read | Append,
}; };
DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode);
enum class FileType { enum class FileType {
BinaryFile, BinaryFile,

View File

@ -69,6 +69,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Common, Memory) \ SUB(Common, Memory) \
CLS(Core) \ CLS(Core) \
SUB(Core, Linker) \ SUB(Core, Linker) \
SUB(Core, Devices) \
CLS(Config) \ CLS(Config) \
CLS(Debug) \ CLS(Debug) \
CLS(Kernel) \ CLS(Kernel) \
@ -105,7 +106,9 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Rtc) \ SUB(Lib, Rtc) \
SUB(Lib, DiscMap) \ SUB(Lib, DiscMap) \
SUB(Lib, Png) \ SUB(Lib, Png) \
SUB(Lib, Jpeg) \
SUB(Lib, PlayGo) \ SUB(Lib, PlayGo) \
SUB(Lib, PlayGoDialog) \
SUB(Lib, Random) \ SUB(Lib, Random) \
SUB(Lib, Usbd) \ SUB(Lib, Usbd) \
SUB(Lib, Ajm) \ SUB(Lib, Ajm) \
@ -121,6 +124,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Fiber) \ SUB(Lib, Fiber) \
SUB(Lib, Vdec2) \ SUB(Lib, Vdec2) \
SUB(Lib, Videodec) \ SUB(Lib, Videodec) \
SUB(Lib, RazorCpu) \
CLS(Frontend) \ CLS(Frontend) \
CLS(Render) \ CLS(Render) \
SUB(Render, Vulkan) \ SUB(Render, Vulkan) \

View File

@ -35,6 +35,7 @@ enum class Class : u8 {
Common_Memory, ///< Memory mapping and management functions Common_Memory, ///< Memory mapping and management functions
Core, ///< LLE emulation core Core, ///< LLE emulation core
Core_Linker, ///< The module linker Core_Linker, ///< The module linker
Core_Devices, ///< Devices emulation
Config, ///< Emulator configuration (including commandline) Config, ///< Emulator configuration (including commandline)
Debug, ///< Debugging tools Debug, ///< Debugging tools
Kernel, ///< The HLE implementation of the PS4 kernel. Kernel, ///< The HLE implementation of the PS4 kernel.
@ -72,7 +73,9 @@ enum class Class : u8 {
Lib_Rtc, ///< The LibSceRtc implementation. Lib_Rtc, ///< The LibSceRtc implementation.
Lib_DiscMap, ///< The LibSceDiscMap implementation. Lib_DiscMap, ///< The LibSceDiscMap implementation.
Lib_Png, ///< The LibScePng implementation. Lib_Png, ///< The LibScePng implementation.
Lib_Jpeg, ///< The LibSceJpeg implementation.
Lib_PlayGo, ///< The LibScePlayGo implementation. Lib_PlayGo, ///< The LibScePlayGo implementation.
Lib_PlayGoDialog, ///< The LibScePlayGoDialog implementation.
Lib_Random, ///< The libSceRandom implementation. Lib_Random, ///< The libSceRandom implementation.
Lib_Usbd, ///< The LibSceUsbd implementation. Lib_Usbd, ///< The LibSceUsbd implementation.
Lib_Ajm, ///< The LibSceAjm implementation. Lib_Ajm, ///< The LibSceAjm implementation.
@ -88,6 +91,7 @@ enum class Class : u8 {
Lib_Fiber, ///< The LibSceFiber implementation. Lib_Fiber, ///< The LibSceFiber implementation.
Lib_Vdec2, ///< The LibSceVideodec2 implementation. Lib_Vdec2, ///< The LibSceVideodec2 implementation.
Lib_Videodec, ///< The LibSceVideodec implementation. Lib_Videodec, ///< The LibSceVideodec implementation.
Lib_RazorCpu, ///< The LibRazorCpu implementation.
Frontend, ///< Emulator UI Frontend, ///< Emulator UI
Render, ///< Video Core Render, ///< Video Core
Render_Vulkan, ///< Vulkan backend Render_Vulkan, ///< Vulkan backend

View File

@ -28,7 +28,7 @@ std::string g_game_serial;
std::string patchFile; std::string patchFile;
std::vector<patchInfo> pending_patches; std::vector<patchInfo> pending_patches;
std::string toHex(unsigned long long value, size_t byteSize) { std::string toHex(u64 value, size_t byteSize) {
std::stringstream ss; std::stringstream ss;
ss << std::hex << std::setfill('0') << std::setw(byteSize * 2) << value; ss << std::hex << std::setfill('0') << std::setw(byteSize * 2) << value;
return ss.str(); return ss.str();
@ -38,16 +38,16 @@ std::string convertValueToHex(const std::string type, const std::string valueStr
std::string result; std::string result;
if (type == "byte") { if (type == "byte") {
unsigned int value = std::stoul(valueStr, nullptr, 16); const u32 value = std::stoul(valueStr, nullptr, 16);
result = toHex(value, 1); result = toHex(value, 1);
} else if (type == "bytes16") { } else if (type == "bytes16") {
unsigned int value = std::stoul(valueStr, nullptr, 16); const u32 value = std::stoul(valueStr, nullptr, 16);
result = toHex(value, 2); result = toHex(value, 2);
} else if (type == "bytes32") { } else if (type == "bytes32") {
unsigned long value = std::stoul(valueStr, nullptr, 16); const u32 value = std::stoul(valueStr, nullptr, 16);
result = toHex(value, 4); result = toHex(value, 4);
} else if (type == "bytes64") { } else if (type == "bytes64") {
unsigned long long value = std::stoull(valueStr, nullptr, 16); const u64 value = std::stoull(valueStr, nullptr, 16);
result = toHex(value, 8); result = toHex(value, 8);
} else if (type == "float32") { } else if (type == "float32") {
union { union {

View File

@ -5,8 +5,11 @@
#include "ntapi.h" #include "ntapi.h"
NtDelayExecution_t NtDelayExecution = nullptr; NtClose_t NtClose = nullptr;
NtSetInformationFile_t NtSetInformationFile = nullptr; NtSetInformationFile_t NtSetInformationFile = nullptr;
NtCreateThread_t NtCreateThread = nullptr;
NtTerminateThread_t NtTerminateThread = nullptr;
NtQueueApcThreadEx_t NtQueueApcThreadEx = nullptr;
namespace Common::NtApi { namespace Common::NtApi {
@ -14,9 +17,12 @@ void Initialize() {
HMODULE nt_handle = GetModuleHandleA("ntdll.dll"); HMODULE nt_handle = GetModuleHandleA("ntdll.dll");
// http://stackoverflow.com/a/31411628/4725495 // http://stackoverflow.com/a/31411628/4725495
NtDelayExecution = (NtDelayExecution_t)GetProcAddress(nt_handle, "NtDelayExecution"); NtClose = (NtClose_t)GetProcAddress(nt_handle, "NtClose");
NtSetInformationFile = NtSetInformationFile =
(NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile"); (NtSetInformationFile_t)GetProcAddress(nt_handle, "NtSetInformationFile");
NtCreateThread = (NtCreateThread_t)GetProcAddress(nt_handle, "NtCreateThread");
NtTerminateThread = (NtTerminateThread_t)GetProcAddress(nt_handle, "NtTerminateThread");
NtQueueApcThreadEx = (NtQueueApcThreadEx_t)GetProcAddress(nt_handle, "NtQueueApcThreadEx");
} }
} // namespace Common::NtApi } // namespace Common::NtApi

View File

@ -108,14 +108,444 @@ typedef struct _FILE_DISPOSITION_INFORMATION {
BOOLEAN DeleteFile; BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; } FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
typedef u32(__stdcall* NtDelayExecution_t)(BOOL Alertable, PLARGE_INTEGER DelayInterval); typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWCH Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef u32(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, typedef const UNICODE_STRING* PCUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PCUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR;
PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
typedef const OBJECT_ATTRIBUTES* PCOBJECT_ATTRIBUTES;
typedef struct _CLIENT_ID {
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
typedef struct _INITIAL_TEB {
struct {
PVOID OldStackBase;
PVOID OldStackLimit;
} OldInitialTeb;
PVOID StackBase;
PVOID StackLimit;
PVOID StackAllocationBase;
} INITIAL_TEB, *PINITIAL_TEB;
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID EntryInProgress;
BOOLEAN ShutdownInProgress;
HANDLE ShutdownThreadId;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
typedef struct _CURDIR {
UNICODE_STRING DosPath;
PVOID Handle;
} CURDIR, *PCURDIR;
typedef struct RTL_DRIVE_LETTER_CURDIR {
USHORT Flags;
USHORT Length;
ULONG TimeStamp;
UNICODE_STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
typedef struct _RTL_USER_PROCESS_PARAMETERS {
ULONG AllocationSize;
ULONG Size;
ULONG Flags;
ULONG DebugFlags;
HANDLE ConsoleHandle;
ULONG ConsoleFlags;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
CURDIR CurrentDirectory;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
PWSTR Environment;
ULONG dwX;
ULONG dwY;
ULONG dwXSize;
ULONG dwYSize;
ULONG dwXCountChars;
ULONG dwYCountChars;
ULONG dwFillAttribute;
ULONG dwFlags;
ULONG wShowWindow;
UNICODE_STRING WindowTitle;
UNICODE_STRING Desktop;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeInfo;
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
ULONG_PTR EnvironmentSize;
ULONG_PTR EnvironmentVersion;
PVOID PackageDependencyData;
ULONG ProcessGroupId;
ULONG LoaderThreads;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
typedef struct tagRTL_BITMAP {
ULONG SizeOfBitMap;
PULONG Buffer;
} RTL_BITMAP, *PRTL_BITMAP;
typedef struct {
UINT next;
UINT id;
ULONGLONG addr;
ULONGLONG size;
UINT args[4];
} CROSS_PROCESS_WORK_ENTRY;
typedef union {
struct {
UINT first;
UINT counter;
};
volatile LONGLONG hdr;
} CROSS_PROCESS_WORK_HDR;
typedef struct {
CROSS_PROCESS_WORK_HDR free_list;
CROSS_PROCESS_WORK_HDR work_list;
ULONGLONG unknown[4];
CROSS_PROCESS_WORK_ENTRY entries[1];
} CROSS_PROCESS_WORK_LIST;
typedef struct _CHPEV2_PROCESS_INFO {
ULONG Wow64ExecuteFlags; /* 000 */
USHORT NativeMachineType; /* 004 */
USHORT EmulatedMachineType; /* 006 */
HANDLE SectionHandle; /* 008 */
CROSS_PROCESS_WORK_LIST* CrossProcessWorkList; /* 010 */
void* unknown; /* 018 */
} CHPEV2_PROCESS_INFO, *PCHPEV2_PROCESS_INFO;
typedef u64(__stdcall* KERNEL_CALLBACK_PROC)(void*, ULONG);
typedef struct _PEB { /* win32/win64 */
BOOLEAN InheritedAddressSpace; /* 000/000 */
BOOLEAN ReadImageFileExecOptions; /* 001/001 */
BOOLEAN BeingDebugged; /* 002/002 */
UCHAR ImageUsedLargePages : 1; /* 003/003 */
UCHAR IsProtectedProcess : 1;
UCHAR IsImageDynamicallyRelocated : 1;
UCHAR SkipPatchingUser32Forwarders : 1;
UCHAR IsPackagedProcess : 1;
UCHAR IsAppContainer : 1;
UCHAR IsProtectedProcessLight : 1;
UCHAR IsLongPathAwareProcess : 1;
HANDLE Mutant; /* 004/008 */
HMODULE ImageBaseAddress; /* 008/010 */
PPEB_LDR_DATA LdrData; /* 00c/018 */
RTL_USER_PROCESS_PARAMETERS* ProcessParameters; /* 010/020 */
PVOID SubSystemData; /* 014/028 */
HANDLE ProcessHeap; /* 018/030 */
PRTL_CRITICAL_SECTION FastPebLock; /* 01c/038 */
PVOID AtlThunkSListPtr; /* 020/040 */
PVOID IFEOKey; /* 024/048 */
ULONG ProcessInJob : 1; /* 028/050 */
ULONG ProcessInitializing : 1;
ULONG ProcessUsingVEH : 1;
ULONG ProcessUsingVCH : 1;
ULONG ProcessUsingFTH : 1;
ULONG ProcessPreviouslyThrottled : 1;
ULONG ProcessCurrentlyThrottled : 1;
ULONG ProcessImagesHotPatched : 1;
ULONG ReservedBits0 : 24;
KERNEL_CALLBACK_PROC* KernelCallbackTable; /* 02c/058 */
ULONG Reserved; /* 030/060 */
ULONG AtlThunkSListPtr32; /* 034/064 */
PVOID ApiSetMap; /* 038/068 */
ULONG TlsExpansionCounter; /* 03c/070 */
PRTL_BITMAP TlsBitmap; /* 040/078 */
ULONG TlsBitmapBits[2]; /* 044/080 */
PVOID ReadOnlySharedMemoryBase; /* 04c/088 */
PVOID SharedData; /* 050/090 */
PVOID* ReadOnlyStaticServerData; /* 054/098 */
PVOID AnsiCodePageData; /* 058/0a0 */
PVOID OemCodePageData; /* 05c/0a8 */
PVOID UnicodeCaseTableData; /* 060/0b0 */
ULONG NumberOfProcessors; /* 064/0b8 */
ULONG NtGlobalFlag; /* 068/0bc */
LARGE_INTEGER CriticalSectionTimeout; /* 070/0c0 */
SIZE_T HeapSegmentReserve; /* 078/0c8 */
SIZE_T HeapSegmentCommit; /* 07c/0d0 */
SIZE_T HeapDeCommitTotalFreeThreshold; /* 080/0d8 */
SIZE_T HeapDeCommitFreeBlockThreshold; /* 084/0e0 */
ULONG NumberOfHeaps; /* 088/0e8 */
ULONG MaximumNumberOfHeaps; /* 08c/0ec */
PVOID* ProcessHeaps; /* 090/0f0 */
PVOID GdiSharedHandleTable; /* 094/0f8 */
PVOID ProcessStarterHelper; /* 098/100 */
PVOID GdiDCAttributeList; /* 09c/108 */
PVOID LoaderLock; /* 0a0/110 */
ULONG OSMajorVersion; /* 0a4/118 */
ULONG OSMinorVersion; /* 0a8/11c */
ULONG OSBuildNumber; /* 0ac/120 */
ULONG OSPlatformId; /* 0b0/124 */
ULONG ImageSubSystem; /* 0b4/128 */
ULONG ImageSubSystemMajorVersion; /* 0b8/12c */
ULONG ImageSubSystemMinorVersion; /* 0bc/130 */
KAFFINITY ActiveProcessAffinityMask; /* 0c0/138 */
#ifdef _WIN64
ULONG GdiHandleBuffer[60]; /* /140 */
#else
ULONG GdiHandleBuffer[34]; /* 0c4/ */
#endif
PVOID PostProcessInitRoutine; /* 14c/230 */
PRTL_BITMAP TlsExpansionBitmap; /* 150/238 */
ULONG TlsExpansionBitmapBits[32]; /* 154/240 */
ULONG SessionId; /* 1d4/2c0 */
ULARGE_INTEGER AppCompatFlags; /* 1d8/2c8 */
ULARGE_INTEGER AppCompatFlagsUser; /* 1e0/2d0 */
PVOID ShimData; /* 1e8/2d8 */
PVOID AppCompatInfo; /* 1ec/2e0 */
UNICODE_STRING CSDVersion; /* 1f0/2e8 */
PVOID ActivationContextData; /* 1f8/2f8 */
PVOID ProcessAssemblyStorageMap; /* 1fc/300 */
PVOID SystemDefaultActivationData; /* 200/308 */
PVOID SystemAssemblyStorageMap; /* 204/310 */
SIZE_T MinimumStackCommit; /* 208/318 */
PVOID* FlsCallback; /* 20c/320 */
LIST_ENTRY FlsListHead; /* 210/328 */
union {
PRTL_BITMAP FlsBitmap; /* 218/338 */
#ifdef _WIN64
CHPEV2_PROCESS_INFO* ChpeV2ProcessInfo; /* /338 */
#endif
};
ULONG FlsBitmapBits[4]; /* 21c/340 */
ULONG FlsHighIndex; /* 22c/350 */
PVOID WerRegistrationData; /* 230/358 */
PVOID WerShipAssertPtr; /* 234/360 */
PVOID EcCodeBitMap; /* 238/368 */
PVOID pImageHeaderHash; /* 23c/370 */
ULONG HeapTracingEnabled : 1; /* 240/378 */
ULONG CritSecTracingEnabled : 1;
ULONG LibLoaderTracingEnabled : 1;
ULONG SpareTracingBits : 29;
ULONGLONG CsrServerReadOnlySharedMemoryBase; /* 248/380 */
ULONG TppWorkerpListLock; /* 250/388 */
LIST_ENTRY TppWorkerpList; /* 254/390 */
PVOID WaitOnAddressHashTable[0x80]; /* 25c/3a0 */
PVOID TelemetryCoverageHeader; /* 45c/7a0 */
ULONG CloudFileFlags; /* 460/7a8 */
ULONG CloudFileDiagFlags; /* 464/7ac */
CHAR PlaceholderCompatibilityMode; /* 468/7b0 */
CHAR PlaceholderCompatibilityModeReserved[7]; /* 469/7b1 */
PVOID LeapSecondData; /* 470/7b8 */
ULONG LeapSecondFlags; /* 474/7c0 */
ULONG NtGlobalFlag2; /* 478/7c4 */
} PEB, *PPEB;
typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME {
struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous;
struct _ACTIVATION_CONTEXT* ActivationContext;
ULONG Flags;
} RTL_ACTIVATION_CONTEXT_STACK_FRAME, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME;
typedef struct _ACTIVATION_CONTEXT_STACK {
RTL_ACTIVATION_CONTEXT_STACK_FRAME* ActiveFrame;
LIST_ENTRY FrameListCache;
ULONG Flags;
ULONG NextCookieSequenceNumber;
ULONG_PTR StackId;
} ACTIVATION_CONTEXT_STACK, *PACTIVATION_CONTEXT_STACK;
typedef struct _GDI_TEB_BATCH {
ULONG Offset;
HANDLE HDC;
ULONG Buffer[0x136];
} GDI_TEB_BATCH;
typedef struct _TEB_ACTIVE_FRAME_CONTEXT {
ULONG Flags;
const char* FrameName;
} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT;
typedef struct _TEB_ACTIVE_FRAME {
ULONG Flags;
struct _TEB_ACTIVE_FRAME* Previous;
TEB_ACTIVE_FRAME_CONTEXT* Context;
} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME;
typedef struct _TEB { /* win32/win64 */
NT_TIB Tib; /* 000/0000 */
PVOID EnvironmentPointer; /* 01c/0038 */
CLIENT_ID ClientId; /* 020/0040 */
PVOID ActiveRpcHandle; /* 028/0050 */
PVOID ThreadLocalStoragePointer; /* 02c/0058 */
PPEB Peb; /* 030/0060 */
ULONG LastErrorValue; /* 034/0068 */
ULONG CountOfOwnedCriticalSections; /* 038/006c */
PVOID CsrClientThread; /* 03c/0070 */
PVOID Win32ThreadInfo; /* 040/0078 */
ULONG User32Reserved[26]; /* 044/0080 */
ULONG UserReserved[5]; /* 0ac/00e8 */
PVOID WOW32Reserved; /* 0c0/0100 */
ULONG CurrentLocale; /* 0c4/0108 */
ULONG FpSoftwareStatusRegister; /* 0c8/010c */
PVOID ReservedForDebuggerInstrumentation[16]; /* 0cc/0110 */
#ifdef _WIN64
PVOID SystemReserved1[30]; /* /0190 */
#else
PVOID SystemReserved1[26]; /* 10c/ */
#endif
char PlaceholderCompatibilityMode; /* 174/0280 */
BOOLEAN PlaceholderHydrationAlwaysExplicit; /* 175/0281 */
char PlaceholderReserved[10]; /* 176/0282 */
DWORD ProxiedProcessId; /* 180/028c */
ACTIVATION_CONTEXT_STACK ActivationContextStack; /* 184/0290 */
UCHAR WorkingOnBehalfOfTicket[8]; /* 19c/02b8 */
LONG ExceptionCode; /* 1a4/02c0 */
ACTIVATION_CONTEXT_STACK* ActivationContextStackPointer; /* 1a8/02c8 */
ULONG_PTR InstrumentationCallbackSp; /* 1ac/02d0 */
ULONG_PTR InstrumentationCallbackPreviousPc; /* 1b0/02d8 */
ULONG_PTR InstrumentationCallbackPreviousSp; /* 1b4/02e0 */
#ifdef _WIN64
ULONG TxFsContext; /* /02e8 */
BOOLEAN InstrumentationCallbackDisabled; /* /02ec */
BOOLEAN UnalignedLoadStoreExceptions; /* /02ed */
#else
BOOLEAN InstrumentationCallbackDisabled; /* 1b8/ */
BYTE SpareBytes1[23]; /* 1b9/ */
ULONG TxFsContext; /* 1d0/ */
#endif
GDI_TEB_BATCH GdiTebBatch; /* 1d4/02f0 */
CLIENT_ID RealClientId; /* 6b4/07d8 */
HANDLE GdiCachedProcessHandle; /* 6bc/07e8 */
ULONG GdiClientPID; /* 6c0/07f0 */
ULONG GdiClientTID; /* 6c4/07f4 */
PVOID GdiThreadLocaleInfo; /* 6c8/07f8 */
ULONG_PTR Win32ClientInfo[62]; /* 6cc/0800 */
PVOID glDispatchTable[233]; /* 7c4/09f0 */
PVOID glReserved1[29]; /* b68/1138 */
PVOID glReserved2; /* bdc/1220 */
PVOID glSectionInfo; /* be0/1228 */
PVOID glSection; /* be4/1230 */
PVOID glTable; /* be8/1238 */
PVOID glCurrentRC; /* bec/1240 */
PVOID glContext; /* bf0/1248 */
ULONG LastStatusValue; /* bf4/1250 */
UNICODE_STRING StaticUnicodeString; /* bf8/1258 */
WCHAR StaticUnicodeBuffer[261]; /* c00/1268 */
PVOID DeallocationStack; /* e0c/1478 */
PVOID TlsSlots[64]; /* e10/1480 */
LIST_ENTRY TlsLinks; /* f10/1680 */
PVOID Vdm; /* f18/1690 */
PVOID ReservedForNtRpc; /* f1c/1698 */
PVOID DbgSsReserved[2]; /* f20/16a0 */
ULONG HardErrorMode; /* f28/16b0 */
#ifdef _WIN64
PVOID Instrumentation[11]; /* /16b8 */
#else
PVOID Instrumentation[9]; /* f2c/ */
#endif
GUID ActivityId; /* f50/1710 */
PVOID SubProcessTag; /* f60/1720 */
PVOID PerflibData; /* f64/1728 */
PVOID EtwTraceData; /* f68/1730 */
PVOID WinSockData; /* f6c/1738 */
ULONG GdiBatchCount; /* f70/1740 */
ULONG IdealProcessorValue; /* f74/1744 */
ULONG GuaranteedStackBytes; /* f78/1748 */
PVOID ReservedForPerf; /* f7c/1750 */
PVOID ReservedForOle; /* f80/1758 */
ULONG WaitingOnLoaderLock; /* f84/1760 */
PVOID SavedPriorityState; /* f88/1768 */
ULONG_PTR ReservedForCodeCoverage; /* f8c/1770 */
PVOID ThreadPoolData; /* f90/1778 */
PVOID* TlsExpansionSlots; /* f94/1780 */
#ifdef _WIN64
union {
PVOID DeallocationBStore; /* /1788 */
PVOID* ChpeV2CpuAreaInfo; /* /1788 */
} DUMMYUNIONNAME;
PVOID BStoreLimit; /* /1790 */
#endif
ULONG MuiGeneration; /* f98/1798 */
ULONG IsImpersonating; /* f9c/179c */
PVOID NlsCache; /* fa0/17a0 */
PVOID ShimData; /* fa4/17a8 */
ULONG HeapVirtualAffinity; /* fa8/17b0 */
PVOID CurrentTransactionHandle; /* fac/17b8 */
TEB_ACTIVE_FRAME* ActiveFrame; /* fb0/17c0 */
PVOID* FlsSlots; /* fb4/17c8 */
PVOID PreferredLanguages; /* fb8/17d0 */
PVOID UserPrefLanguages; /* fbc/17d8 */
PVOID MergedPrefLanguages; /* fc0/17e0 */
ULONG MuiImpersonation; /* fc4/17e8 */
USHORT CrossTebFlags; /* fc8/17ec */
USHORT SameTebFlags; /* fca/17ee */
PVOID TxnScopeEnterCallback; /* fcc/17f0 */
PVOID TxnScopeExitCallback; /* fd0/17f8 */
PVOID TxnScopeContext; /* fd4/1800 */
ULONG LockCount; /* fd8/1808 */
LONG WowTebOffset; /* fdc/180c */
PVOID ResourceRetValue; /* fe0/1810 */
PVOID ReservedForWdf; /* fe4/1818 */
ULONGLONG ReservedForCrt; /* fe8/1820 */
GUID EffectiveContainerId; /* ff0/1828 */
} TEB, *PTEB;
static_assert(offsetof(TEB, DeallocationStack) ==
0x1478); /* The only member we care about at the moment */
typedef enum _QUEUE_USER_APC_FLAGS {
QueueUserApcFlagsNone,
QueueUserApcFlagsSpecialUserApc,
QueueUserApcFlagsMaxValue
} QUEUE_USER_APC_FLAGS;
typedef union _USER_APC_OPTION {
ULONG_PTR UserApcFlags;
HANDLE MemoryReserveHandle;
} USER_APC_OPTION, *PUSER_APC_OPTION;
using PPS_APC_ROUTINE = void (*)(PVOID ApcArgument1, PVOID ApcArgument2, PVOID ApcArgument3,
PCONTEXT Context);
typedef u64(__stdcall* NtClose_t)(HANDLE Handle);
typedef u64(__stdcall* NtSetInformationFile_t)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
PVOID FileInformation, ULONG Length, PVOID FileInformation, ULONG Length,
FILE_INFORMATION_CLASS FileInformationClass); FILE_INFORMATION_CLASS FileInformationClass);
extern NtDelayExecution_t NtDelayExecution; typedef u64(__stdcall* NtCreateThread_t)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess,
PCOBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle,
PCLIENT_ID ClientId, PCONTEXT ThreadContext,
PINITIAL_TEB InitialTeb, BOOLEAN CreateSuspended);
typedef u64(__stdcall* NtTerminateThread_t)(HANDLE ThreadHandle, u64 ExitStatus);
typedef u64(__stdcall* NtQueueApcThreadEx_t)(HANDLE ThreadHandle,
USER_APC_OPTION UserApcReserveHandle,
PPS_APC_ROUTINE ApcRoutine, PVOID ApcArgument1,
PVOID ApcArgument2, PVOID ApcArgument3);
extern NtClose_t NtClose;
extern NtSetInformationFile_t NtSetInformationFile; extern NtSetInformationFile_t NtSetInformationFile;
extern NtCreateThread_t NtCreateThread;
extern NtTerminateThread_t NtTerminateThread;
extern NtQueueApcThreadEx_t NtQueueApcThreadEx;
namespace Common::NtApi { namespace Common::NtApi {
void Initialize(); void Initialize();

163
src/common/slab_heap.h Normal file
View File

@ -0,0 +1,163 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "common/assert.h"
#include "common/spin_lock.h"
namespace Common {
class SlabHeapImpl {
public:
struct Node {
Node* next{};
};
public:
constexpr SlabHeapImpl() = default;
void Initialize() {
ASSERT(m_head == nullptr);
}
Node* GetHead() const {
return m_head;
}
void* Allocate() {
m_lock.lock();
Node* ret = m_head;
if (ret != nullptr) {
m_head = ret->next;
}
m_lock.unlock();
return ret;
}
void Free(void* obj) {
m_lock.lock();
Node* node = static_cast<Node*>(obj);
node->next = m_head;
m_head = node;
m_lock.unlock();
}
private:
std::atomic<Node*> m_head{};
Common::SpinLock m_lock;
};
class SlabHeapBase : protected SlabHeapImpl {
private:
size_t m_obj_size{};
uintptr_t m_peak{};
uintptr_t m_start{};
uintptr_t m_end{};
public:
constexpr SlabHeapBase() = default;
bool Contains(uintptr_t address) const {
return m_start <= address && address < m_end;
}
void Initialize(size_t obj_size, void* memory, size_t memory_size) {
// Ensure we don't initialize a slab using null memory.
ASSERT(memory != nullptr);
// Set our object size.
m_obj_size = obj_size;
// Initialize the base allocator.
SlabHeapImpl::Initialize();
// Set our tracking variables.
const size_t num_obj = (memory_size / obj_size);
m_start = reinterpret_cast<uintptr_t>(memory);
m_end = m_start + num_obj * obj_size;
m_peak = m_start;
// Free the objects.
u8* cur = reinterpret_cast<u8*>(m_end);
for (size_t i = 0; i < num_obj; i++) {
cur -= obj_size;
SlabHeapImpl::Free(cur);
}
}
size_t GetSlabHeapSize() const {
return (m_end - m_start) / this->GetObjectSize();
}
size_t GetObjectSize() const {
return m_obj_size;
}
void* Allocate() {
void* obj = SlabHeapImpl::Allocate();
return obj;
}
void Free(void* obj) {
// Don't allow freeing an object that wasn't allocated from this heap.
const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
ASSERT(contained);
SlabHeapImpl::Free(obj);
}
size_t GetObjectIndex(const void* obj) const {
return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
}
size_t GetPeakIndex() const {
return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
}
uintptr_t GetSlabHeapAddress() const {
return m_start;
}
size_t GetNumRemaining() const {
// Only calculate the number of remaining objects under debug configuration.
return 0;
}
};
template <typename T>
class SlabHeap final : public SlabHeapBase {
private:
using BaseHeap = SlabHeapBase;
public:
constexpr SlabHeap() = default;
void Initialize(void* memory, size_t memory_size) {
BaseHeap::Initialize(sizeof(T), memory, memory_size);
}
T* Allocate() {
T* obj = static_cast<T*>(BaseHeap::Allocate());
if (obj != nullptr) [[likely]] {
std::construct_at(obj);
}
return obj;
}
void Free(T* obj) {
BaseHeap::Free(obj);
}
size_t GetObjectIndex(const T* obj) const {
return BaseHeap::GetObjectIndex(obj);
}
};
} // namespace Common

View File

@ -3,10 +3,7 @@
#pragma once #pragma once
#include <bit>
#include <compare>
#include <numeric> #include <numeric>
#include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "common/assert.h" #include "common/assert.h"

53
src/common/spin_lock.cpp Executable file
View File

@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/spin_lock.h"
#if _MSC_VER
#include <intrin.h>
#if _M_AMD64
#define __x86_64__ 1
#endif
#if _M_ARM64
#define __aarch64__ 1
#endif
#else
#if __x86_64__
#include <xmmintrin.h>
#endif
#endif
namespace {
void ThreadPause() {
#if __x86_64__
_mm_pause();
#elif __aarch64__ && _MSC_VER
__yield();
#elif __aarch64__
asm("yield");
#endif
}
} // Anonymous namespace
namespace Common {
void SpinLock::lock() {
while (lck.test_and_set(std::memory_order_acquire)) {
ThreadPause();
}
}
void SpinLock::unlock() {
lck.clear(std::memory_order_release);
}
bool SpinLock::try_lock() {
if (lck.test_and_set(std::memory_order_acquire)) {
return false;
}
return true;
}
} // namespace Common

33
src/common/spin_lock.h Executable file
View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
namespace Common {
/**
* SpinLock class
* a lock similar to mutex that forces a thread to spin wait instead calling the
* supervisor. Should be used on short sequences of code.
*/
class SpinLock {
public:
SpinLock() = default;
SpinLock(const SpinLock&) = delete;
SpinLock& operator=(const SpinLock&) = delete;
SpinLock(SpinLock&&) = delete;
SpinLock& operator=(SpinLock&&) = delete;
void lock();
void unlock();
[[nodiscard]] bool try_lock();
private:
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
} // namespace Common

7
src/common/stb.cpp Normal file
View File

@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#define STBI_NO_STDIO
#include "common/stb.h"

6
src/common/stb.h Normal file
View File

@ -0,0 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stb_image.h>

View File

@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// support header file for libav
// The av_err2str macro in libavutil/error.h does not play nice with C++
#ifdef av_err2str
#undef av_err2str
#include <string>
av_always_inline std::string av_err2string(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum);
}
#define av_err2str(err) av_err2string(err).c_str()
#endif // av_err2str

View File

@ -147,6 +147,10 @@ void SetCurrentThreadName(const char* name) {
SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data()); SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data());
} }
void SetThreadName(void* thread, const char* name) {
SetThreadDescription(thread, UTF8ToUTF16W(name).data());
}
#else // !MSVC_VER, so must be POSIX threads #else // !MSVC_VER, so must be POSIX threads
// MinGW with the POSIX threading model does not support pthread_setname_np // MinGW with the POSIX threading model does not support pthread_setname_np
@ -170,11 +174,19 @@ void SetCurrentThreadName(const char* name) {
pthread_setname_np(pthread_self(), name); pthread_setname_np(pthread_self(), name);
#endif #endif
} }
void SetThreadName(void* thread, const char* name) {
// TODO
}
#endif #endif
#if defined(_WIN32) #if defined(_WIN32)
void SetCurrentThreadName(const char*) { void SetCurrentThreadName(const char*) {
// Do Nothing on MingW // Do Nothing on MinGW
}
void SetThreadName(void* thread, const char* name) {
// Do Nothing on MinGW
} }
#endif #endif

View File

@ -23,6 +23,8 @@ void SetCurrentThreadPriority(ThreadPriority new_priority);
void SetCurrentThreadName(const char* name); void SetCurrentThreadName(const char* name);
void SetThreadName(void* thread, const char* name);
class AccurateTimer { class AccurateTimer {
std::chrono::nanoseconds target_interval{}; std::chrono::nanoseconds target_interval{};
std::chrono::nanoseconds total_wait{}; std::chrono::nanoseconds total_wait{};

111
src/common/va_ctx.h Normal file
View File

@ -0,0 +1,111 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <xmmintrin.h>
#include "common/types.h"
#define VA_ARGS \
uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, \
uint64_t overflow_arg_area, __m128 xmm0, __m128 xmm1, __m128 xmm2, __m128 xmm3, \
__m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7, ...
#define VA_CTX(ctx) \
alignas(16)::Common::VaCtx ctx{}; \
(ctx).reg_save_area.gp[0] = rdi; \
(ctx).reg_save_area.gp[1] = rsi; \
(ctx).reg_save_area.gp[2] = rdx; \
(ctx).reg_save_area.gp[3] = rcx; \
(ctx).reg_save_area.gp[4] = r8; \
(ctx).reg_save_area.gp[5] = r9; \
(ctx).reg_save_area.fp[0] = xmm0; \
(ctx).reg_save_area.fp[1] = xmm1; \
(ctx).reg_save_area.fp[2] = xmm2; \
(ctx).reg_save_area.fp[3] = xmm3; \
(ctx).reg_save_area.fp[4] = xmm4; \
(ctx).reg_save_area.fp[5] = xmm5; \
(ctx).reg_save_area.fp[6] = xmm6; \
(ctx).reg_save_area.fp[7] = xmm7; \
(ctx).va_list.reg_save_area = &(ctx).reg_save_area; \
(ctx).va_list.gp_offset = offsetof(::Common::VaRegSave, gp); \
(ctx).va_list.fp_offset = offsetof(::Common::VaRegSave, fp); \
(ctx).va_list.overflow_arg_area = &overflow_arg_area;
namespace Common {
// https://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure
struct VaList {
u32 gp_offset;
u32 fp_offset;
void* overflow_arg_area;
void* reg_save_area;
};
struct VaRegSave {
u64 gp[6];
__m128 fp[8];
};
struct VaCtx {
VaRegSave reg_save_area;
VaList va_list;
};
template <class T, uint32_t Size>
T vaArgRegSaveAreaGp(VaList* l) {
auto* addr = reinterpret_cast<T*>(static_cast<u8*>(l->reg_save_area) + l->gp_offset);
l->gp_offset += Size;
return *addr;
}
template <class T, u64 Align, u64 Size>
T vaArgOverflowArgArea(VaList* l) {
auto ptr = ((reinterpret_cast<u64>(l->overflow_arg_area) + (Align - 1)) & ~(Align - 1));
auto* addr = reinterpret_cast<T*>(ptr);
l->overflow_arg_area = reinterpret_cast<void*>(ptr + Size);
return *addr;
}
template <class T, uint32_t Size>
T vaArgRegSaveAreaFp(VaList* l) {
auto* addr = reinterpret_cast<T*>(static_cast<u8*>(l->reg_save_area) + l->fp_offset);
l->fp_offset += Size;
return *addr;
}
inline int vaArgInteger(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<int, 8>(l);
}
return vaArgOverflowArgArea<int, 1, 8>(l);
}
inline long long vaArgLongLong(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<long long, 8>(l);
}
return vaArgOverflowArgArea<long long, 1, 8>(l);
}
inline long vaArgLong(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<long, 8>(l);
}
return vaArgOverflowArgArea<long, 1, 8>(l);
}
inline double vaArgDouble(VaList* l) {
if (l->fp_offset <= 160) {
return vaArgRegSaveAreaFp<double, 16>(l);
}
return vaArgOverflowArgArea<double, 1, 8>(l);
}
template <class T>
T* vaArgPtr(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<T*, 8>(l);
}
return vaArgOverflowArgArea<T*, 1, 8>(l);
}
} // namespace Common

View File

@ -1,13 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <map>
#include <boost/icl/separate_interval_set.hpp> #include <boost/icl/separate_interval_set.hpp>
#include "common/alignment.h" #include "common/alignment.h"
#include "common/arch.h" #include "common/arch.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/error.h" #include "common/error.h"
#include "core/address_space.h" #include "core/address_space.h"
#include "core/libraries/kernel/memory_management.h" #include "core/libraries/kernel/memory.h"
#include "core/memory.h" #include "core/memory.h"
#include "libraries/error_codes.h" #include "libraries/error_codes.h"
@ -40,6 +41,12 @@ static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO;
} }
} }
struct MemoryRegion {
VAddr base;
size_t size;
bool is_mapped;
};
struct AddressSpace::Impl { struct AddressSpace::Impl {
Impl() : process{GetCurrentProcess()} { Impl() : process{GetCurrentProcess()} {
// Allocate virtual address placeholder for our address space. // Allocate virtual address placeholder for our address space.
@ -75,6 +82,7 @@ struct AddressSpace::Impl {
Common::GetLastErrorMsg()); Common::GetLastErrorMsg());
// Take the reduction off of the system managed area, and leave the others unchanged. // Take the reduction off of the system managed area, and leave the others unchanged.
reduction = size_t(virtual_base - SYSTEM_MANAGED_MIN);
system_managed_base = virtual_base; system_managed_base = virtual_base;
system_managed_size = SystemManagedSize - reduction; system_managed_size = SystemManagedSize - reduction;
system_reserved_base = reinterpret_cast<u8*>(SYSTEM_RESERVED_MIN); system_reserved_base = reinterpret_cast<u8*>(SYSTEM_RESERVED_MIN);
@ -95,7 +103,8 @@ struct AddressSpace::Impl {
const uintptr_t system_managed_addr = reinterpret_cast<uintptr_t>(system_managed_base); const uintptr_t system_managed_addr = reinterpret_cast<uintptr_t>(system_managed_base);
const uintptr_t system_reserved_addr = reinterpret_cast<uintptr_t>(system_reserved_base); const uintptr_t system_reserved_addr = reinterpret_cast<uintptr_t>(system_reserved_base);
const uintptr_t user_addr = reinterpret_cast<uintptr_t>(user_base); const uintptr_t user_addr = reinterpret_cast<uintptr_t>(user_base);
placeholders.insert({system_managed_addr, virtual_size - reduction}); regions.emplace(system_managed_addr,
MemoryRegion{system_managed_addr, virtual_size - reduction, false});
// Allocate backing file that represents the total physical memory. // Allocate backing file that represents the total physical memory.
backing_handle = backing_handle =
@ -132,42 +141,15 @@ struct AddressSpace::Impl {
} }
void* Map(VAddr virtual_addr, PAddr phys_addr, size_t size, ULONG prot, uintptr_t fd = 0) { void* Map(VAddr virtual_addr, PAddr phys_addr, size_t size, ULONG prot, uintptr_t fd = 0) {
const size_t aligned_size = Common::AlignUp(size, 16_KB); // Before mapping we must carve a placeholder with the exact properties of our mapping.
const auto it = placeholders.find(virtual_addr); auto* region = EnsureSplitRegionForMapping(virtual_addr, size);
ASSERT_MSG(it != placeholders.end(), "Cannot map already mapped region"); region->is_mapped = true;
ASSERT_MSG(virtual_addr >= it->lower() && virtual_addr + aligned_size <= it->upper(),
"Map range must be fully contained in a placeholder");
// Windows only allows splitting a placeholder into two.
// This means that if the map range is fully
// contained the the placeholder we need to perform two split operations,
// one at the start and at the end.
const VAddr placeholder_start = it->lower();
const VAddr placeholder_end = it->upper();
const VAddr virtual_end = virtual_addr + aligned_size;
// If the placeholder doesn't exactly start at virtual_addr, split it at the start.
if (placeholder_start != virtual_addr) {
VirtualFreeEx(process, reinterpret_cast<LPVOID>(placeholder_start),
virtual_addr - placeholder_start, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER);
}
// If the placeholder doesn't exactly end at virtual_end, split it at the end.
if (placeholder_end != virtual_end) {
VirtualFreeEx(process, reinterpret_cast<LPVOID>(virtual_end),
placeholder_end - virtual_end, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER);
}
// Remove the placeholder.
placeholders.erase({virtual_addr, virtual_end});
// Perform the map.
void* ptr = nullptr; void* ptr = nullptr;
if (phys_addr != -1) { if (phys_addr != -1) {
HANDLE backing = fd ? reinterpret_cast<HANDLE>(fd) : backing_handle; HANDLE backing = fd ? reinterpret_cast<HANDLE>(fd) : backing_handle;
if (fd && prot == PAGE_READONLY) { if (fd && prot == PAGE_READONLY) {
DWORD resultvar; DWORD resultvar;
ptr = VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), aligned_size, ptr = VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), size,
MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER,
PAGE_READWRITE, nullptr, 0); PAGE_READWRITE, nullptr, 0);
bool ret = ReadFile(backing, ptr, size, &resultvar, NULL); bool ret = ReadFile(backing, ptr, size, &resultvar, NULL);
@ -176,12 +158,11 @@ struct AddressSpace::Impl {
ASSERT_MSG(ret, "VirtualProtect failed. {}", Common::GetLastErrorMsg()); ASSERT_MSG(ret, "VirtualProtect failed. {}", Common::GetLastErrorMsg());
} else { } else {
ptr = MapViewOfFile3(backing, process, reinterpret_cast<PVOID>(virtual_addr), ptr = MapViewOfFile3(backing, process, reinterpret_cast<PVOID>(virtual_addr),
phys_addr, aligned_size, MEM_REPLACE_PLACEHOLDER, prot, phys_addr, size, MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0);
nullptr, 0);
} }
} else { } else {
ptr = ptr =
VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), aligned_size, VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), size,
MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0); MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0);
} }
ASSERT_MSG(ptr, "{}", Common::GetLastErrorMsg()); ASSERT_MSG(ptr, "{}", Common::GetLastErrorMsg());
@ -202,33 +183,118 @@ struct AddressSpace::Impl {
// The unmap call will create a new placeholder region. We need to see if we can coalesce it // The unmap call will create a new placeholder region. We need to see if we can coalesce it
// with neighbors. // with neighbors.
VAddr placeholder_start = virtual_addr; JoinRegionsAfterUnmap(virtual_addr, size);
VAddr placeholder_end = virtual_addr + size; }
// The following code is inspired from Dolphin's MemArena
// https://github.com/dolphin-emu/dolphin/blob/deee3ee4/Source/Core/Common/MemArenaWin.cpp#L212
MemoryRegion* EnsureSplitRegionForMapping(VAddr address, size_t size) {
// Find closest region that is <= the given address by using upper bound and decrementing
auto it = regions.upper_bound(address);
ASSERT_MSG(it != regions.begin(), "Invalid address {:#x}", address);
--it;
ASSERT_MSG(!it->second.is_mapped,
"Attempt to map {:#x} with size {:#x} which overlaps with {:#x} mapping",
address, size, it->second.base);
auto& [base, region] = *it;
const VAddr mapping_address = region.base;
const size_t region_size = region.size;
if (mapping_address == address) {
// If this region is already split up correctly we don't have to do anything
if (region_size == size) {
return &region;
}
ASSERT_MSG(region_size >= size,
"Region with address {:#x} and size {:#x} can't fit {:#x}", mapping_address,
region_size, size);
// Split the placeholder.
if (!VirtualFreeEx(process, LPVOID(address), size,
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg());
return nullptr;
}
// Update tracked mappings and return the first of the two
region.size = size;
const VAddr new_mapping_start = address + size;
regions.emplace_hint(std::next(it), new_mapping_start,
MemoryRegion(new_mapping_start, region_size - size, false));
return &region;
}
ASSERT(mapping_address < address);
// Is there enough space to map this?
const size_t offset_in_region = address - mapping_address;
const size_t minimum_size = size + offset_in_region;
ASSERT(region_size >= minimum_size);
// Split the placeholder.
if (!VirtualFreeEx(process, LPVOID(address), size,
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
UNREACHABLE_MSG("Region splitting failed: {}", Common::GetLastErrorMsg());
return nullptr;
}
// Do we now have two regions or three regions?
if (region_size == minimum_size) {
// Split into two; update tracked mappings and return the second one
region.size = offset_in_region;
it = regions.emplace_hint(std::next(it), address, MemoryRegion(address, size, false));
return &it->second;
} else {
// Split into three; update tracked mappings and return the middle one
region.size = offset_in_region;
const VAddr middle_mapping_start = address;
const size_t middle_mapping_size = size;
const VAddr after_mapping_start = address + size;
const size_t after_mapping_size = region_size - minimum_size;
it = regions.emplace_hint(std::next(it), after_mapping_start,
MemoryRegion(after_mapping_start, after_mapping_size, false));
it = regions.emplace_hint(
it, middle_mapping_start,
MemoryRegion(middle_mapping_start, middle_mapping_size, false));
return &it->second;
}
}
void JoinRegionsAfterUnmap(VAddr address, size_t size) {
// There should be a mapping that matches the request exactly, find it
auto it = regions.find(address);
ASSERT_MSG(it != regions.end() && it->second.size == size,
"Invalid address/size given to unmap.");
auto& [base, region] = *it;
region.is_mapped = false;
// Check if a placeholder exists right before us. // Check if a placeholder exists right before us.
const auto left_it = placeholders.find(virtual_addr - 1); auto it_prev = it != regions.begin() ? std::prev(it) : regions.end();
if (left_it != placeholders.end()) { if (it_prev != regions.end() && !it_prev->second.is_mapped) {
ASSERT_MSG(left_it->upper() == virtual_addr, const size_t total_size = it_prev->second.size + size;
"Left placeholder does not end at virtual_addr!"); if (!VirtualFreeEx(process, LPVOID(it_prev->first), total_size,
placeholder_start = left_it->lower(); MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
VirtualFreeEx(process, reinterpret_cast<LPVOID>(placeholder_start), UNREACHABLE_MSG("Region coalescing failed: {}", Common::GetLastErrorMsg());
placeholder_end - placeholder_start, }
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS);
it_prev->second.size = total_size;
regions.erase(it);
it = it_prev;
} }
// Check if a placeholder exists right after us. // Check if a placeholder exists right after us.
const auto right_it = placeholders.find(placeholder_end + 1); auto it_next = std::next(it);
if (right_it != placeholders.end()) { if (it_next != regions.end() && !it_next->second.is_mapped) {
ASSERT_MSG(right_it->lower() == placeholder_end, const size_t total_size = it->second.size + it_next->second.size;
"Right placeholder does not start at virtual_end!"); if (!VirtualFreeEx(process, LPVOID(it->first), total_size,
placeholder_end = right_it->upper(); MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) {
VirtualFreeEx(process, reinterpret_cast<LPVOID>(placeholder_start), UNREACHABLE_MSG("Region coalescing failed: {}", Common::GetLastErrorMsg());
placeholder_end - placeholder_start, }
MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS);
}
// Insert the new placeholder. it->second.size = total_size;
placeholders.insert({placeholder_start, placeholder_end}); regions.erase(it_next);
}
} }
void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) { void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) {
@ -251,18 +317,22 @@ struct AddressSpace::Impl {
return; return;
} }
DWORD old_flags{}; const VAddr virtual_end = virtual_addr + size;
bool success = auto it = --regions.upper_bound(virtual_addr);
VirtualProtect(reinterpret_cast<void*>(virtual_addr), size, new_flags, &old_flags); for (; it->first < virtual_end; it++) {
if (!it->second.is_mapped) {
if (!success) { continue;
LOG_ERROR(Common_Memory, }
"Failed to change virtual memory protection for address {:#x}, size {}", const auto& region = it->second;
virtual_addr, size); const size_t range_addr = std::max(region.base, virtual_addr);
const size_t range_size = std::min(region.base + region.size, virtual_end) - range_addr;
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 {}",
range_addr, range_size);
}
} }
// Use assert to ensure success in debug builds
DEBUG_ASSERT(success && "Failed to change virtual memory protection");
} }
HANDLE process{}; HANDLE process{};
@ -275,7 +345,7 @@ struct AddressSpace::Impl {
size_t system_reserved_size{}; size_t system_reserved_size{};
u8* user_base{}; u8* user_base{};
size_t user_size{}; size_t user_size{};
boost::icl::separate_interval_set<uintptr_t> placeholders; std::map<VAddr, MemoryRegion> regions;
}; };
#else #else

View File

@ -3,6 +3,8 @@
#pragma once #pragma once
#include "common/types.h"
namespace Core { namespace Core {
/// Initializes a stack for the current thread for use by patch implementations. /// Initializes a stack for the current thread for use by patch implementations.

View File

@ -8,7 +8,7 @@
#include "common/singleton.h" #include "common/singleton.h"
#include "debug_state.h" #include "debug_state.h"
#include "devtools/widget/common.h" #include "devtools/widget/common.h"
#include "libraries/kernel/time_management.h" #include "libraries/kernel/time.h"
#include "libraries/system/msgdialog.h" #include "libraries/system/msgdialog.h"
#include "video_core/amdgpu/pm4_cmds.h" #include "video_core/amdgpu/pm4_cmds.h"
@ -157,7 +157,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
if (is_compute) { if (is_compute) {
dump.is_compute = true; dump.is_compute = true;
const auto& cs = dump.regs.cs_program; const auto& cs = dump.regs.cs_program;
dump.cs_data = ComputerShaderDump{ dump.cs_data = PipelineComputerProgramDump{
.cs_program = cs, .cs_program = cs,
.code = std::vector<u32>{cs.Code().begin(), cs.Code().end()}, .code = std::vector<u32>{cs.Code().begin(), cs.Code().end()},
}; };
@ -167,7 +167,7 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
auto stage = regs.ProgramForStage(i); auto stage = regs.ProgramForStage(i);
if (stage->address_lo != 0) { if (stage->address_lo != 0) {
auto code = stage->Code(); auto code = stage->Code();
dump.stages[i] = ShaderDump{ dump.stages[i] = PipelineShaderProgramDump{
.user_data = *stage, .user_data = *stage,
.code = std::vector<u32>{code.begin(), code.end()}, .code = std::vector<u32>{code.begin(), code.end()},
}; };
@ -176,3 +176,10 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
} }
} }
} }
void DebugStateImpl::CollectShader(const std::string& name, std::span<const u32> spv,
std::span<const u32> raw_code) {
shader_dump_list.emplace_back(name, std::vector<u32>{spv.begin(), spv.end()},
std::vector<u32>{raw_code.begin(), raw_code.end()});
std::ranges::sort(shader_dump_list, {}, &ShaderDump::name);
}

View File

@ -30,7 +30,8 @@ namespace Core::Devtools {
class Layer; class Layer;
namespace Widget { namespace Widget {
class FrameGraph; class FrameGraph;
} class ShaderList;
} // namespace Widget
} // namespace Core::Devtools } // namespace Core::Devtools
namespace DebugStateType { namespace DebugStateType {
@ -49,12 +50,12 @@ struct QueueDump {
uintptr_t base_addr; uintptr_t base_addr;
}; };
struct ShaderDump { struct PipelineShaderProgramDump {
Vulkan::Liverpool::ShaderProgram user_data{}; Vulkan::Liverpool::ShaderProgram user_data{};
std::vector<u32> code{}; std::vector<u32> code{};
}; };
struct ComputerShaderDump { struct PipelineComputerProgramDump {
Vulkan::Liverpool::ComputeProgram cs_program{}; Vulkan::Liverpool::ComputeProgram cs_program{};
std::vector<u32> code{}; std::vector<u32> code{};
}; };
@ -63,8 +64,8 @@ struct RegDump {
bool is_compute{false}; bool is_compute{false};
static constexpr size_t MaxShaderStages = 5; static constexpr size_t MaxShaderStages = 5;
Vulkan::Liverpool::Regs regs{}; Vulkan::Liverpool::Regs regs{};
std::array<ShaderDump, MaxShaderStages> stages{}; std::array<PipelineShaderProgramDump, MaxShaderStages> stages{};
ComputerShaderDump cs_data{}; PipelineComputerProgramDump cs_data{};
}; };
struct FrameDump { struct FrameDump {
@ -73,9 +74,41 @@ struct FrameDump {
std::unordered_map<uintptr_t, RegDump> regs; // address -> reg dump std::unordered_map<uintptr_t, RegDump> regs; // address -> reg dump
}; };
struct ShaderDump {
std::string name;
std::vector<u32> spv;
std::vector<u32> raw_code;
std::string cache_spv_disasm{};
std::string cache_raw_disasm{};
ShaderDump(std::string name, std::vector<u32> spv, std::vector<u32> raw_code)
: name(std::move(name)), spv(std::move(spv)), raw_code(std::move(raw_code)) {}
ShaderDump(const ShaderDump& other) = delete;
ShaderDump(ShaderDump&& other) noexcept
: name{std::move(other.name)}, spv{std::move(other.spv)},
raw_code{std::move(other.raw_code)}, cache_spv_disasm{std::move(other.cache_spv_disasm)},
cache_raw_disasm{std::move(other.cache_raw_disasm)} {}
ShaderDump& operator=(const ShaderDump& other) = delete;
ShaderDump& operator=(ShaderDump&& other) noexcept {
if (this == &other)
return *this;
name = std::move(other.name);
spv = std::move(other.spv);
raw_code = std::move(other.raw_code);
cache_spv_disasm = std::move(other.cache_spv_disasm);
cache_raw_disasm = std::move(other.cache_raw_disasm);
return *this;
}
};
class DebugStateImpl { class DebugStateImpl {
friend class Core::Devtools::Layer; friend class Core::Devtools::Layer;
friend class Core::Devtools::Widget::FrameGraph; friend class Core::Devtools::Widget::FrameGraph;
friend class Core::Devtools::Widget::ShaderList;
std::queue<std::string> debug_message_popup;
std::mutex guest_threads_mutex{}; std::mutex guest_threads_mutex{};
std::vector<ThreadID> guest_threads{}; std::vector<ThreadID> guest_threads{};
@ -94,7 +127,7 @@ class DebugStateImpl {
std::shared_mutex frame_dump_list_mutex; std::shared_mutex frame_dump_list_mutex;
std::vector<FrameDump> frame_dump_list{}; std::vector<FrameDump> frame_dump_list{};
std::queue<std::string> debug_message_popup; std::vector<ShaderDump> shader_dump_list{};
public: public:
void ShowDebugMessage(std::string message) { void ShowDebugMessage(std::string message) {
@ -152,6 +185,9 @@ public:
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr, void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
const AmdGpu::Liverpool::Regs& regs, bool is_compute = false); const AmdGpu::Liverpool::Regs& regs, bool is_compute = false);
void CollectShader(const std::string& name, std::span<const u32> spv,
std::span<const u32> raw_code);
}; };
} // namespace DebugStateType } // namespace DebugStateType

View File

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "base_device.h"
namespace Core::Devices {
BaseDevice::BaseDevice() = default;
BaseDevice::~BaseDevice() = default;
} // namespace Core::Devices

View File

@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <core/libraries/kernel/orbis_error.h>
#include "common/types.h"
#include "common/va_ctx.h"
namespace Libraries::Kernel {
struct OrbisKernelStat;
struct SceKernelIovec;
} // namespace Libraries::Kernel
namespace Core::Devices {
class BaseDevice {
public:
explicit BaseDevice();
virtual ~BaseDevice() = 0;
virtual int ioctl(u64 cmd, Common::VaCtx* args) {
return ORBIS_KERNEL_ERROR_ENOTTY;
}
virtual s64 write(const void* buf, size_t nbytes) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 lseek(s64 offset, int whence) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 read(void* buf, size_t nbytes) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual int fstat(Libraries::Kernel::OrbisKernelStat* sb) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s32 fsync() {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual int ftruncate(s64 length) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual int getdents(void* buf, u32 nbytes, s64* basep) {
return ORBIS_KERNEL_ERROR_EBADF;
}
virtual s64 pwrite(const void* buf, size_t nbytes, u64 offset) {
return ORBIS_KERNEL_ERROR_EBADF;
}
};
} // namespace Core::Devices

67
src/core/devices/ioccom.h Normal file
View File

@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
/*-
* Copyright (c) 1982, 1986, 1990, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ioccom.h 8.2 (Berkeley) 3/28/94
* $FreeBSD$
*/
#define IOCPARM_SHIFT 13 /* number of bits for ioctl size */
#define IOCPARM_MASK ((1 << IOCPARM_SHIFT) - 1) /* parameter length mask */
#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
#define IOCBASECMD(x) ((x) & ~(IOCPARM_MASK << 16))
#define IOCGROUP(x) (((x) >> 8) & 0xff)
#define IOCPARM_MAX (1 << IOCPARM_SHIFT) /* max size of ioctl */
#define IOC_VOID 0x20000000 /* no parameters */
#define IOC_OUT 0x40000000 /* copy out parameters */
#define IOC_IN 0x80000000 /* copy in parameters */
#define IOC_INOUT (IOC_IN | IOC_OUT)
#define IOC_DIRMASK (IOC_VOID | IOC_OUT | IOC_IN)
#define _IOC(inout, group, num, len) \
((unsigned long)((inout) | (((len) & IOCPARM_MASK) << 16) | ((group) << 8) | (num)))
#define _IO(g, n) _IOC(IOC_VOID, (g), (n), 0)
#define _IOWINT(g, n) _IOC(IOC_VOID, (g), (n), sizeof(int))
#define _IOR(g, n, t) _IOC(IOC_OUT, (g), (n), sizeof(t))
#define _IOW(g, n, t) _IOC(IOC_IN, (g), (n), sizeof(t))
/* this should be _IORW, but stdio got there first */
#define _IOWR(g, n, t) _IOC(IOC_INOUT, (g), (n), sizeof(t))
/*
# Simple parse of ioctl cmd
def parse(v):
print('inout', (v >> 24 & 0xFF))
print('len', hex(v >> 16 & 0xFF))
print('group', chr(v >> 8 & 0xFF))
print('num', hex(v & 0xFF))
*/

View File

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/libraries/kernel/file_system.h"
#include "logger.h"
namespace Core::Devices {
Logger::Logger(std::string prefix, bool is_err) : prefix(std::move(prefix)), is_err(is_err) {}
Logger::~Logger() = default;
s64 Logger::write(const void* buf, size_t nbytes) {
log(static_cast<const char*>(buf), nbytes);
return nbytes;
}
size_t Logger::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
for (int i = 0; i < iovcnt; i++) {
log(static_cast<const char*>(iov[i].iov_base), iov[i].iov_len);
}
return iovcnt;
}
s64 Logger::pwrite(const void* buf, size_t nbytes, u64 offset) {
log(static_cast<const char*>(buf), nbytes);
return nbytes;
}
s32 Logger::fsync() {
log_flush();
return 0;
}
void Logger::log(const char* buf, size_t nbytes) {
std::scoped_lock lock{mtx};
const char* end = buf + nbytes;
for (const char* it = buf; it < end; ++it) {
char c = *it;
if (c == '\r') {
continue;
}
if (c == '\n') {
log_flush();
continue;
}
buffer.push_back(c);
}
}
void Logger::log_flush() {
std::scoped_lock lock{mtx};
if (buffer.empty()) {
return;
}
if (is_err) {
LOG_ERROR(Tty, "[{}] {}", prefix, std::string_view{buffer});
} else {
LOG_INFO(Tty, "[{}] {}", prefix, std::string_view{buffer});
}
buffer.clear();
}
} // namespace Core::Devices

37
src/core/devices/logger.h Normal file
View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "base_device.h"
#include <mutex>
#include <string>
#include <vector>
namespace Core::Devices {
class Logger final : BaseDevice {
std::string prefix;
bool is_err;
std::recursive_mutex mtx;
std::vector<char> buffer;
public:
explicit Logger(std::string prefix, bool is_err);
~Logger() override;
s64 write(const void* buf, size_t nbytes) override;
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
s32 fsync() override;
private:
void log(const char* buf, size_t nbytes);
void log_flush();
};
} // namespace Core::Devices

View File

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "base_device.h"
namespace Core::Devices {
class NopDevice final : BaseDevice {
u32 handle;
public:
explicit NopDevice(u32 handle) : handle(handle) {}
~NopDevice() override = default;
int ioctl(u64 cmd, Common::VaCtx* args) override {
return 0;
}
s64 write(const void* buf, size_t nbytes) override {
return 0;
}
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override {
return 0;
}
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override {
return 0;
}
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override {
return 0;
}
s64 lseek(s64 offset, int whence) override {
return 0;
}
s64 read(void* buf, size_t nbytes) override {
return 0;
}
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override {
return 0;
}
s32 fsync() override {
return 0;
}
int ftruncate(s64 length) override {
return 0;
}
int getdents(void* buf, u32 nbytes, s64* basep) override {
return 0;
}
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override {
return 0;
}
};
} // namespace Core::Devices

View File

@ -289,6 +289,16 @@ const char* GetContextRegName(u32 reg_offset) {
return "mmSPI_PS_INPUT_CNTL_2"; return "mmSPI_PS_INPUT_CNTL_2";
case mmSPI_PS_INPUT_CNTL_3: case mmSPI_PS_INPUT_CNTL_3:
return "mmSPI_PS_INPUT_CNTL_3"; return "mmSPI_PS_INPUT_CNTL_3";
case mmPA_SU_POLY_OFFSET_FRONT_SCALE:
return "mmPA_SU_POLY_OFFSET_FRONT_SCALE";
case mmPA_SU_POLY_OFFSET_FRONT_OFFSET:
return "mmPA_SU_POLY_OFFSET_FRONT_OFFSET";
case mmPA_SU_POLY_OFFSET_BACK_SCALE:
return "mmPA_SU_POLY_OFFSET_BACK_SCALE";
case mmPA_SU_POLY_OFFSET_BACK_OFFSET:
return "mmPA_SU_POLY_OFFSET_BACK_OFFSET";
case mmPA_SU_POLY_OFFSET_CLAMP:
return "mmPA_SU_POLY_OFFSET_CLAMP";
default: default:
break; break;
} }

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "layer.h"
#include <imgui.h> #include <imgui.h>
#include "common/config.h" #include "common/config.h"
@ -9,10 +11,14 @@
#include "core/debug_state.h" #include "core/debug_state.h"
#include "imgui/imgui_std.h" #include "imgui/imgui_std.h"
#include "imgui_internal.h" #include "imgui_internal.h"
#include "layer.h"
#include "options.h" #include "options.h"
#include "video_core/renderer_vulkan/vk_presenter.h"
#include "widget/frame_dump.h" #include "widget/frame_dump.h"
#include "widget/frame_graph.h" #include "widget/frame_graph.h"
#include "widget/memory_map.h"
#include "widget/shader_list.h"
extern std::unique_ptr<Vulkan::Presenter> presenter;
using namespace ImGui; using namespace ImGui;
using namespace Core::Devtools; using namespace Core::Devtools;
@ -32,6 +38,9 @@ static float debug_popup_timing = 3.0f;
static bool just_opened_options = false; static bool just_opened_options = false;
static Widget::MemoryMapViewer memory_map;
static Widget::ShaderList shader_list;
// clang-format off // clang-format off
static std::string help_text = static std::string help_text =
#include "help.txt" #include "help.txt"
@ -60,6 +69,7 @@ void L::DrawMenuBar() {
} }
if (BeginMenu("GPU Tools")) { if (BeginMenu("GPU Tools")) {
MenuItem("Show frame info", nullptr, &frame_graph.is_open); MenuItem("Show frame info", nullptr, &frame_graph.is_open);
MenuItem("Show loaded shaders", nullptr, &shader_list.open);
if (BeginMenu("Dump frames")) { if (BeginMenu("Dump frames")) {
SliderInt("Count", &dump_frame_count, 1, 5); SliderInt("Count", &dump_frame_count, 1, 5);
if (MenuItem("Dump", "Ctrl+Alt+F9", nullptr, !DebugState.DumpingCurrentFrame())) { if (MenuItem("Dump", "Ctrl+Alt+F9", nullptr, !DebugState.DumpingCurrentFrame())) {
@ -71,6 +81,19 @@ void L::DrawMenuBar() {
open_popup_help = MenuItem("Help & Tips"); open_popup_help = MenuItem("Help & Tips");
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (BeginMenu("Display")) {
if (BeginMenu("Brightness")) {
SliderFloat("Gamma", &presenter->GetGammaRef(), 0.1f, 2.0f);
ImGui::EndMenu();
}
ImGui::EndMenu();
}
if (BeginMenu("Debug")) {
if (MenuItem("Memory map")) {
memory_map.open = true;
}
ImGui::EndMenu();
}
EndMainMenuBar(); EndMainMenuBar();
} }
@ -165,19 +188,29 @@ void L::DrawAdvanced() {
bool close_popup_options = true; bool close_popup_options = true;
if (BeginPopupModal("GPU Tools Options", &close_popup_options, if (BeginPopupModal("GPU Tools Options", &close_popup_options,
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) {
static char disassembly_cli[512]; static char disassembler_cli_isa[512];
static char disassembler_cli_spv[512];
static bool frame_dump_render_on_collapse; static bool frame_dump_render_on_collapse;
if (just_opened_options) { if (just_opened_options) {
just_opened_options = false; just_opened_options = false;
auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1); auto s = Options.disassembler_cli_isa.copy(disassembler_cli_isa,
disassembly_cli[s] = '\0'; sizeof(disassembler_cli_isa) - 1);
disassembler_cli_isa[s] = '\0';
s = Options.disassembler_cli_spv.copy(disassembler_cli_spv,
sizeof(disassembler_cli_spv) - 1);
disassembler_cli_spv[s] = '\0';
frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse; frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse;
} }
InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli)); InputText("Shader isa disassembler: ", disassembler_cli_isa, sizeof(disassembler_cli_isa));
if (IsItemHovered()) { if (IsItemHovered()) {
SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")"); SetTooltip(R"(Command to disassemble shaders. Example: dis.exe --raw "{src}")");
}
InputText("Shader SPIRV disassembler: ", disassembler_cli_spv,
sizeof(disassembler_cli_spv));
if (IsItemHovered()) {
SetTooltip(R"(Command to disassemble shaders. Example: spirv-cross -V "{src}")");
} }
Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse); Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse);
if (IsItemHovered()) { if (IsItemHovered()) {
@ -186,7 +219,8 @@ void L::DrawAdvanced() {
} }
if (Button("Save")) { if (Button("Save")) {
Options.disassembly_cli = disassembly_cli; Options.disassembler_cli_isa = disassembler_cli_isa;
Options.disassembler_cli_spv = disassembler_cli_spv;
Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse; Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse;
SaveIniSettingsToDisk(io.IniFilename); SaveIniSettingsToDisk(io.IniFilename);
CloseCurrentPopup(); CloseCurrentPopup();
@ -209,6 +243,13 @@ void L::DrawAdvanced() {
EndPopup(); EndPopup();
} }
if (memory_map.open) {
memory_map.Draw();
}
if (shader_list.open) {
shader_list.Draw();
}
} }
void L::DrawSimple() { void L::DrawSimple() {

View File

@ -12,8 +12,12 @@ TOptions Options;
void LoadOptionsConfig(const char* line) { void LoadOptionsConfig(const char* line) {
char str[512]; char str[512];
int i; int i;
if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) { if (sscanf(line, "disassembler_cli_isa=%511[^\n]", str) == 1) {
Options.disassembly_cli = str; Options.disassembler_cli_isa = str;
return;
}
if (sscanf(line, "disassembler_cli_spv=%511[^\n]", str) == 1) {
Options.disassembler_cli_spv = str;
return; return;
} }
if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) { if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) {
@ -23,7 +27,8 @@ void LoadOptionsConfig(const char* line) {
} }
void SerializeOptionsConfig(ImGuiTextBuffer* buf) { void SerializeOptionsConfig(ImGuiTextBuffer* buf) {
buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str()); buf->appendf("disassembler_cli_isa=%s\n", Options.disassembler_cli_isa.c_str());
buf->appendf("disassembler_cli_spv=%s\n", Options.disassembler_cli_spv.c_str());
buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse); buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse);
} }

View File

@ -10,7 +10,8 @@ struct ImGuiTextBuffer;
namespace Core::Devtools { namespace Core::Devtools {
struct TOptions { struct TOptions {
std::string disassembly_cli{}; std::string disassembler_cli_isa{"clrxdisasm --raw \"{src}\""};
std::string disassembler_cli_spv{"spirv-cross -V \"{src}\""};
bool frame_dump_render_on_collapse{false}; bool frame_dump_render_on_collapse{false};
}; };

View File

@ -3,6 +3,7 @@
// Credits to https://github.com/psucien/tlg-emu-tools/ // Credits to https://github.com/psucien/tlg-emu-tools/
#include <cinttypes>
#include <string> #include <string>
#include <gcn/si_ci_vi_merged_offset.h> #include <gcn/si_ci_vi_merged_offset.h>
#include <imgui.h> #include <imgui.h>
@ -1224,12 +1225,12 @@ void CmdListViewer::Draw(bool only_batches_view) {
} }
Text("queue : %s", queue_name); Text("queue : %s", queue_name);
Text("base addr: %08llX", cmdb_addr); Text("base addr: %08" PRIXPTR, cmdb_addr);
SameLine(); SameLine();
if (SmallButton("Memory >")) { if (SmallButton("Memory >")) {
cmdb_view.Open ^= true; cmdb_view.Open ^= true;
} }
Text("size : %04llX", cmdb_size); Text("size : %04zX", cmdb_size);
Separator(); Separator();
{ {
@ -1292,12 +1293,12 @@ void CmdListViewer::Draw(bool only_batches_view) {
if (batch.type == static_cast<AmdGpu::PM4ItOpcode>(0xFF)) { if (batch.type == static_cast<AmdGpu::PM4ItOpcode>(0xFF)) {
ignore_header = true; ignore_header = true;
} else if (!batch.marker.empty()) { } else if (!batch.marker.empty()) {
snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s | %s", snprintf(batch_hdr, sizeof(batch_hdr), "%08" PRIXPTR ": batch-%03d %s | %s",
cmdb_addr + batch.start_addr, batch.id, cmdb_addr + batch.start_addr, batch.id,
Gcn::GetOpCodeName(static_cast<u32>(batch.type)), Gcn::GetOpCodeName(static_cast<u32>(batch.type)),
batch.marker.c_str()); batch.marker.c_str());
} else { } else {
snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s", snprintf(batch_hdr, sizeof(batch_hdr), "%08" PRIXPTR ": batch-%03d %s",
cmdb_addr + batch.start_addr, batch.id, cmdb_addr + batch.start_addr, batch.id,
Gcn::GetOpCodeName(static_cast<u32>(batch.type))); Gcn::GetOpCodeName(static_cast<u32>(batch.type)));
} }
@ -1305,7 +1306,7 @@ void CmdListViewer::Draw(bool only_batches_view) {
if (batch.id == batch_bp) { // highlight batch at breakpoint if (batch.id == batch_bp) { // highlight batch at breakpoint
PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f}); PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f});
} }
if (batch.id == highlight_batch) { if (batch.id == highlight_batch && !group_batches) {
PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f}); PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
} }
@ -1348,7 +1349,7 @@ void CmdListViewer::Draw(bool only_batches_view) {
} }
if (show_batch_content) { if (show_batch_content) {
auto processed_size = 0ull; size_t processed_size = 0;
auto bb = ctx.LastItemData.Rect; auto bb = ctx.LastItemData.Rect;
if (group_batches && !ignore_header) { if (group_batches && !ignore_header) {
Indent(); Indent();
@ -1364,9 +1365,9 @@ void CmdListViewer::Draw(bool only_batches_view) {
op = pm4_t3->opcode; op = pm4_t3->opcode;
char header_name[128]; char header_name[128];
sprintf(header_name, "%08llX: %s", snprintf(header_name, sizeof(header_name), "%08" PRIXPTR ": %s",
cmdb_addr + batch.start_addr + processed_size, cmdb_addr + batch.start_addr + processed_size,
Gcn::GetOpCodeName((u32)op)); Gcn::GetOpCodeName(static_cast<u32>(op)));
bool open_pm4 = TreeNode(header_name); bool open_pm4 = TreeNode(header_name);
if (!group_batches) { if (!group_batches) {
@ -1458,7 +1459,7 @@ void CmdListViewer::Draw(bool only_batches_view) {
} }
} }
if (batch.id == highlight_batch) { if (batch.id == highlight_batch && !group_batches) {
PopStyleColor(); PopStyleColor();
} }

View File

@ -3,16 +3,24 @@
#pragma once #pragma once
#include <filesystem>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <variant> #include <variant>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/io_file.h"
#include "common/types.h" #include "common/types.h"
#include "core/debug_state.h"
#include "video_core/amdgpu/pm4_opcodes.h" #include "video_core/amdgpu/pm4_opcodes.h"
#if defined(_WIN32)
#define popen _popen
#define pclose _pclose
#endif
namespace Core::Devtools::Widget { namespace Core::Devtools::Widget {
/* /*
* Generic PM4 header * Generic PM4 header
@ -106,4 +114,53 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
} }
} }
inline std::optional<std::string> exec_cli(const char* cli) {
std::array<char, 64> buffer{};
std::string output;
const auto f = popen(cli, "r");
if (!f) {
pclose(f);
return {};
}
while (fgets(buffer.data(), buffer.size(), f)) {
output += buffer.data();
}
pclose(f);
return output;
}
inline std::string RunDisassembler(const std::string& disassembler_cli,
const std::vector<u32>& shader_code) {
std::string shader_dis;
if (disassembler_cli.empty()) {
shader_dis = "No disassembler set";
} else {
auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin";
constexpr std::string_view src_arg = "{src}";
std::string cli = disassembler_cli;
const auto pos = cli.find(src_arg);
if (pos == std::string::npos) {
DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument\n" +
disassembler_cli);
} else {
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
file.Write(shader_code);
file.Close();
auto result = exec_cli(cli.c_str());
shader_dis = result.value_or("Could not disassemble shader");
if (shader_dis.empty()) {
shader_dis = "Disassembly empty or failed";
}
std::filesystem::remove(bin_path);
}
}
return shader_dis;
}
} // namespace Core::Devtools::Widget } // namespace Core::Devtools::Widget

View File

@ -4,7 +4,7 @@
#include <cstdio> #include <cstdio>
#include <fmt/chrono.h> #include <fmt/chrono.h>
#include <imgui.h> #include <imgui.h>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "common/io_file.h" #include "common/io_file.h"
#include "core/devtools/options.h" #include "core/devtools/options.h"

View File

@ -458,7 +458,7 @@ struct MemoryEditor {
data_write = data_next = true; data_write = data_next = true;
if (data_editing_addr_next != (size_t)-1) if (data_editing_addr_next != (size_t)-1)
data_write = data_next = false; data_write = data_next = false;
unsigned int data_input_value = 0; u32 data_input_value = 0;
if (!ReadOnly && data_write && if (!ReadOnly && data_write &&
sscanf(DataInputBuf, "%X", &data_input_value) == 1) { sscanf(DataInputBuf, "%X", &data_input_value) == 1) {
if (WriteFn) if (WriteFn)
@ -929,7 +929,7 @@ struct MemoryEditor {
default: default:
case ImGuiDataType_COUNT: case ImGuiDataType_COUNT:
break; break;
} // Switch } // Switch
IM_ASSERT(0); // Shouldn't reach IM_ASSERT(0); // Shouldn't reach
} }
}; };

View File

@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <imgui.h>
#include <magic_enum/magic_enum.hpp>
#include "core/debug_state.h"
#include "core/memory.h"
#include "memory_map.h"
using namespace ImGui;
namespace Core::Devtools::Widget {
bool MemoryMapViewer::Iterator::DrawLine() {
if (is_vma) {
if (vma.it == vma.end) {
return false;
}
auto m = vma.it->second;
if (m.type == VMAType::Free) {
++vma.it;
return DrawLine();
}
TableNextColumn();
Text("%" PRIXPTR, m.base);
TableNextColumn();
Text("%zX", m.size);
TableNextColumn();
Text("%s", magic_enum::enum_name(m.type).data());
TableNextColumn();
Text("%s", magic_enum::enum_name(m.prot).data());
TableNextColumn();
if (m.is_exec) {
Text("X");
}
TableNextColumn();
Text("%s", m.name.c_str());
++vma.it;
return true;
}
if (dmem.it == dmem.end) {
return false;
}
auto m = dmem.it->second;
if (m.is_free) {
++dmem.it;
return DrawLine();
}
TableNextColumn();
Text("%" PRIXPTR, m.base);
TableNextColumn();
Text("%zX", m.size);
TableNextColumn();
auto type = static_cast<::Libraries::Kernel::MemoryTypes>(m.memory_type);
Text("%s", magic_enum::enum_name(type).data());
TableNextColumn();
Text("%d", m.is_pooled);
++dmem.it;
return true;
}
void MemoryMapViewer::Draw() {
SetNextWindowSize({600.0f, 500.0f}, ImGuiCond_FirstUseEver);
if (!Begin("Memory map", &open)) {
End();
return;
}
auto mem = Memory::Instance();
std::scoped_lock lck{mem->mutex};
{
bool next_showing_vma = showing_vma;
if (showing_vma) {
PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
}
if (Button("VMem")) {
next_showing_vma = true;
}
if (showing_vma) {
PopStyleColor();
}
SameLine();
if (!showing_vma) {
PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
}
if (Button("DMem")) {
next_showing_vma = false;
}
if (!showing_vma) {
PopStyleColor();
}
showing_vma = next_showing_vma;
}
Iterator it{};
if (showing_vma) {
it.is_vma = true;
it.vma.it = mem->vma_map.begin();
it.vma.end = mem->vma_map.end();
} else {
it.is_vma = false;
it.dmem.it = mem->dmem_map.begin();
it.dmem.end = mem->dmem_map.end();
}
if (BeginTable("memory_view_table", showing_vma ? 6 : 4,
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg |
ImGuiTableFlags_SizingFixedFit)) {
if (showing_vma) {
TableSetupColumn("Address");
TableSetupColumn("Size");
TableSetupColumn("Type");
TableSetupColumn("Prot");
TableSetupColumn("Is Exec");
TableSetupColumn("Name");
} else {
TableSetupColumn("Address");
TableSetupColumn("Size");
TableSetupColumn("Type");
TableSetupColumn("Pooled");
}
TableHeadersRow();
while (it.DrawLine())
;
EndTable();
}
End();
}
} // namespace Core::Devtools::Widget

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/memory.h"
namespace Core::Devtools::Widget {
class MemoryMapViewer {
struct Iterator {
bool is_vma;
struct {
MemoryManager::DMemMap::iterator it;
MemoryManager::DMemMap::iterator end;
} dmem;
struct {
MemoryManager::VMAMap::iterator it;
MemoryManager::VMAMap::iterator end;
} vma;
bool DrawLine();
};
bool showing_vma = true;
public:
bool open = false;
void Draw();
};
} // namespace Core::Devtools::Widget

View File

@ -5,7 +5,7 @@
#include <cstdio> #include <cstdio>
#include <imgui.h> #include <imgui.h>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "cmd_list.h" #include "cmd_list.h"
#include "common.h" #include "common.h"

View File

@ -5,7 +5,7 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <imgui.h> #include <imgui.h>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include <stdio.h> #include <stdio.h>
#include "common.h" #include "common.h"
@ -25,21 +25,6 @@ using magic_enum::enum_name;
constexpr auto depth_id = 0xF3; constexpr auto depth_id = 0xF3;
static std::optional<std::string> exec_cli(const char* cli) {
std::array<char, 64> buffer{};
std::string output;
const auto f = popen(cli, "r");
if (!f) {
pclose(f);
return {};
}
while (fgets(buffer.data(), buffer.size(), f)) {
output += buffer.data();
}
pclose(f);
return output;
}
namespace Core::Devtools::Widget { namespace Core::Devtools::Widget {
void RegView::ProcessShader(int shader_id) { void RegView::ProcessShader(int shader_id) {
@ -54,38 +39,12 @@ void RegView::ProcessShader(int shader_id) {
user_data = s.user_data.user_data; user_data = s.user_data.user_data;
} }
std::string shader_dis; std::string shader_dis = RunDisassembler(Options.disassembler_cli_isa, shader_code);
if (Options.disassembly_cli.empty()) {
shader_dis = "No disassembler set";
} else {
auto bin_path = std::filesystem::temp_directory_path() / "shadps4_tmp_shader.bin";
constexpr std::string_view src_arg = "{src}";
std::string cli = Options.disassembly_cli;
const auto pos = cli.find(src_arg);
if (pos == std::string::npos) {
DebugState.ShowDebugMessage("Disassembler CLI does not contain {src} argument");
} else {
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
file.Write(shader_code);
file.Close();
auto result = exec_cli(cli.c_str());
shader_dis = result.value_or("Could not disassemble shader");
if (shader_dis.empty()) {
shader_dis = "Disassembly empty or failed";
}
std::filesystem::remove(bin_path);
}
}
MemoryEditor hex_view; MemoryEditor hex_view;
hex_view.Open = true; hex_view.Open = true;
hex_view.ReadOnly = true; hex_view.ReadOnly = true;
hex_view.Cols = 8; hex_view.Cols = 16;
hex_view.OptShowAscii = false; hex_view.OptShowAscii = false;
hex_view.OptShowOptions = false; hex_view.OptShowOptions = false;
@ -376,7 +335,9 @@ void RegView::Draw() {
if (!shader) { if (!shader) {
Text("Stage not selected"); Text("Stage not selected");
} else { } else {
shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size()); shader->hex_view.DrawContents(shader->user_data.data(),
shader->user_data.size() *
sizeof(Vulkan::Liverpool::UserData::value_type));
} }
} }
End(); End();

View File

@ -0,0 +1,95 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_list.h"
#include <imgui.h>
#include "common.h"
#include "common/config.h"
#include "core/debug_state.h"
#include "core/devtools/options.h"
#include "imgui/imgui_std.h"
using namespace ImGui;
namespace Core::Devtools::Widget {
void ShaderList::DrawShader(DebugStateType::ShaderDump& value) {
if (!loaded_data) {
loaded_data = true;
if (value.cache_raw_disasm.empty()) {
value.cache_raw_disasm = RunDisassembler(Options.disassembler_cli_isa, value.raw_code);
}
isa_editor.SetText(value.cache_raw_disasm);
if (value.cache_spv_disasm.empty()) {
value.cache_spv_disasm = RunDisassembler(Options.disassembler_cli_spv, value.spv);
}
spv_editor.SetText(value.cache_spv_disasm);
}
if (SmallButton("<-")) {
selected_shader = -1;
}
SameLine();
Text("%s", value.name.c_str());
SameLine(0.0f, 7.0f);
if (BeginCombo("Shader type", showing_isa ? "ISA" : "SPIRV", ImGuiComboFlags_WidthFitPreview)) {
if (Selectable("SPIRV")) {
showing_isa = false;
}
if (Selectable("ISA")) {
showing_isa = true;
}
EndCombo();
}
if (showing_isa) {
isa_editor.Render("ISA", GetContentRegionAvail());
} else {
spv_editor.Render("SPIRV", GetContentRegionAvail());
}
}
ShaderList::ShaderList() {
isa_editor.SetPalette(TextEditor::GetDarkPalette());
isa_editor.SetReadOnly(true);
spv_editor.SetPalette(TextEditor::GetDarkPalette());
spv_editor.SetReadOnly(true);
spv_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
}
void ShaderList::Draw() {
SetNextWindowSize({500.0f, 600.0f}, ImGuiCond_FirstUseEver);
if (!Begin("Shader list", &open)) {
End();
return;
}
if (!Config::collectShadersForDebug()) {
DrawCenteredText("Enable 'CollectShader' in config to see shaders");
End();
return;
}
if (selected_shader >= 0) {
DrawShader(DebugState.shader_dump_list[selected_shader]);
End();
return;
}
auto width = GetContentRegionAvail().x;
int i = 0;
for (const auto& shader : DebugState.shader_dump_list) {
if (ButtonEx(shader.name.c_str(), {width, 20.0f}, ImGuiButtonFlags_NoHoveredOnFocus)) {
selected_shader = i;
loaded_data = false;
}
i++;
}
End();
}
} // namespace Core::Devtools::Widget

View File

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/debug_state.h"
#include "text_editor.h"
namespace Core::Devtools::Widget {
class ShaderList {
int selected_shader = -1;
TextEditor isa_editor{};
TextEditor spv_editor{};
bool loaded_data = false;
bool showing_isa = false;
void DrawShader(DebugStateType::ShaderDump& value);
public:
ShaderList();
bool open = false;
void Draw();
};
} // namespace Core::Devtools::Widget

View File

@ -131,7 +131,7 @@ static int UTF8CharLength(TextEditor::Char c) {
} }
// "Borrowed" from ImGui source // "Borrowed" from ImGui source
static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) { static inline int ImTextCharToUtf8(char* buf, int buf_size, u32 c) {
if (c < 0x80) { if (c < 0x80) {
buf[0] = (char)c; buf[0] = (char)c;
return 1; return 1;

View File

@ -1,31 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <zlib-ng.h> #include <zlib.h>
#include "common/io_file.h" #include "common/io_file.h"
#include "common/logging/formatter.h" #include "common/logging/formatter.h"
#include "core/file_format/pkg.h" #include "core/file_format/pkg.h"
#include "core/file_format/pkg_type.h" #include "core/file_format/pkg_type.h"
static void DecompressPFSC(std::span<const char> compressed_data, static void DecompressPFSC(std::span<char> compressed_data, std::span<char> decompressed_data) {
std::span<char> decompressed_data) { z_stream decompressStream;
zng_stream decompressStream;
decompressStream.zalloc = Z_NULL; decompressStream.zalloc = Z_NULL;
decompressStream.zfree = Z_NULL; decompressStream.zfree = Z_NULL;
decompressStream.opaque = Z_NULL; decompressStream.opaque = Z_NULL;
if (zng_inflateInit(&decompressStream) != Z_OK) { if (inflateInit(&decompressStream) != Z_OK) {
// std::cerr << "Error initializing zlib for deflation." << std::endl; // std::cerr << "Error initializing zlib for deflation." << std::endl;
} }
decompressStream.avail_in = compressed_data.size(); decompressStream.avail_in = compressed_data.size();
decompressStream.next_in = reinterpret_cast<const Bytef*>(compressed_data.data()); decompressStream.next_in = reinterpret_cast<unsigned char*>(compressed_data.data());
decompressStream.avail_out = decompressed_data.size(); decompressStream.avail_out = decompressed_data.size();
decompressStream.next_out = reinterpret_cast<Bytef*>(decompressed_data.data()); decompressStream.next_out = reinterpret_cast<unsigned char*>(decompressed_data.data());
if (zng_inflate(&decompressStream, Z_FINISH)) { if (inflate(&decompressStream, Z_FINISH)) {
} }
if (zng_inflateEnd(&decompressStream) != Z_OK) { if (inflateEnd(&decompressStream) != Z_OK) {
// std::cerr << "Error ending zlib inflate" << std::endl; // std::cerr << "Error ending zlib inflate" << std::endl;
} }
} }

View File

@ -97,24 +97,22 @@ struct PlaygoChunk {
class PlaygoFile { class PlaygoFile {
public: public:
bool initialized; OrbisPlayGoHandle handle = 0;
OrbisPlayGoHandle handle; OrbisPlayGoChunkId id = 0;
OrbisPlayGoChunkId id; OrbisPlayGoLocus locus = OrbisPlayGoLocus::NotDownloaded;
OrbisPlayGoLocus locus; OrbisPlayGoInstallSpeed speed = OrbisPlayGoInstallSpeed::Trickle;
OrbisPlayGoInstallSpeed speed; s64 speed_tick = 0;
s64 speed_tick; OrbisPlayGoEta eta = 0;
OrbisPlayGoEta eta; OrbisPlayGoLanguageMask langMask = 0;
OrbisPlayGoLanguageMask langMask;
std::vector<PlaygoChunk> chunks; std::vector<PlaygoChunk> chunks;
public: public:
PlaygoFile() explicit PlaygoFile() = default;
: initialized(false), handle(0), id(0), locus(0), speed(ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE),
speed_tick(0), eta(0), langMask(0), playgoHeader{0} {}
~PlaygoFile() = default; ~PlaygoFile() = default;
bool Open(const std::filesystem::path& filepath); bool Open(const std::filesystem::path& filepath);
bool LoadChunks(const Common::FS::IOFile& file); bool LoadChunks(const Common::FS::IOFile& file);
PlaygoHeader& GetPlaygoHeader() { PlaygoHeader& GetPlaygoHeader() {
return playgoHeader; return playgoHeader;
} }

View File

@ -21,8 +21,13 @@ static inline u32 get_max_size(std::string_view key, u32 default_value) {
} }
bool PSF::Open(const std::filesystem::path& filepath) { bool PSF::Open(const std::filesystem::path& filepath) {
using namespace std::chrono;
if (std::filesystem::exists(filepath)) { if (std::filesystem::exists(filepath)) {
last_write = std::filesystem::last_write_time(filepath); const auto t = std::filesystem::last_write_time(filepath);
const auto rel =
duration_cast<seconds>(t - std::filesystem::file_time_type::clock::now()).count();
const auto tp = system_clock::to_time_t(system_clock::now() + seconds{rel});
last_write = system_clock::from_time_t(tp);
} }
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
@ -99,7 +104,7 @@ bool PSF::Encode(const std::filesystem::path& filepath) const {
return false; return false;
} }
last_write = std::filesystem::file_time_type::clock::now(); last_write = std::chrono::system_clock::now();
const auto psf_buffer = Encode(); const auto psf_buffer = Encode();
const size_t written = file.Write(psf_buffer); const size_t written = file.Write(psf_buffer);

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include <chrono>
#include <filesystem> #include <filesystem>
#include <span> #include <span>
#include <string> #include <string>
@ -71,7 +72,7 @@ public:
void AddString(std::string key, std::string value, bool update = false); void AddString(std::string key, std::string value, bool update = false);
void AddInteger(std::string key, s32 value, bool update = false); void AddInteger(std::string key, s32 value, bool update = false);
[[nodiscard]] std::filesystem::file_time_type GetLastWrite() const { [[nodiscard]] std::chrono::system_clock::time_point GetLastWrite() const {
return last_write; return last_write;
} }
@ -80,7 +81,7 @@ public:
} }
private: private:
mutable std::filesystem::file_time_type last_write; mutable std::chrono::system_clock::time_point last_write;
std::vector<Entry> entry_list; std::vector<Entry> entry_list;

View File

@ -5,13 +5,9 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/io_file.h" #include "common/io_file.h"
#include "common/stb.h"
#include "splash.h" #include "splash.h"
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#define STBI_NO_STDIO
#include "externals/stb_image.h"
bool Splash::Open(const std::filesystem::path& filepath) { bool Splash::Open(const std::filesystem::path& filepath) {
ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed"); ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed");

114
src/core/file_sys/file.cpp Normal file
View File

@ -0,0 +1,114 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/error.h"
#include "core/file_sys/file.h"
#ifdef _WIN64
#include <io.h>
#include <share.h>
#include <windows.h>
#include "common/ntapi.h"
#endif
namespace Core::FileSys {
#ifdef _WIN64
int File::Open(const std::filesystem::path& path, Common::FS::FileAccessMode f_access) {
DWORD access{};
if (f_access == Common::FS::FileAccessMode::Read) {
access = GENERIC_READ;
} else if (f_access == Common::FS::FileAccessMode::Write) {
access = GENERIC_WRITE;
} else if (f_access == Common::FS::FileAccessMode::ReadWrite) {
access = GENERIC_READ | GENERIC_WRITE;
} else {
UNREACHABLE();
}
handle = CreateFileW(path.native().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE) {
return ENOENT;
}
}
s64 File::Read(void* buf, size_t nbytes) {
DWORD bytes_read;
if (!ReadFile(handle, buf, nbytes, &bytes_read, nullptr)) {
UNREACHABLE_MSG("ReadFile failed: {}", Common::GetLastErrorMsg());
}
return bytes_read;
}
s64 File::Pread(void* buf, size_t nbytes, s64 offset) {
OVERLAPPED ol{};
ol.Offset = offset;
ol.OffsetHigh = offset >> 32;
DWORD bytes_read;
if (!ReadFile(handle, buf, nbytes, &bytes_read, &ol)) {
UNREACHABLE_MSG("ReadFile failed: {}", Common::GetLastErrorMsg());
}
return bytes_read;
}
s64 File::Write(const void* buf, size_t nbytes) {
DWORD bytes_written;
if (!WriteFile(handle, buf, nbytes, &bytes_written, nullptr)) {
UNREACHABLE_MSG("WriteFile failed: {}", Common::GetLastErrorMsg());
}
return bytes_written;
}
s64 File::Pwrite(const void* buf, size_t nbytes, s64 offset) {
OVERLAPPED ol{};
ol.Offset = offset;
ol.OffsetHigh = offset >> 32;
DWORD bytes_written;
if (!WriteFile(handle, buf, nbytes, &bytes_written, &ol)) {
UNREACHABLE_MSG("WriteFile failed: {}", Common::GetLastErrorMsg());
}
return bytes_written;
}
void File::SetSize(s64 size) {
Lseek(size, 0);
if (!SetEndOfFile(handle)) {
UNREACHABLE_MSG("SetEndOfFile failed: {}", Common::GetLastErrorMsg());
}
}
void File::Flush() {
FlushFileBuffers(handle);
}
s64 File::Lseek(s64 offset, int whence) {
LARGE_INTEGER new_file_pointer;
DWORD origin{};
if (whence == 0) {
origin = FILE_BEGIN;
} else if (whence == 1) {
origin = FILE_CURRENT;
} else if (whence == 2) {
origin = FILE_END;
}
if (!SetFilePointerEx(handle, LARGE_INTEGER{.QuadPart = offset}, &new_file_pointer, origin)) {
UNREACHABLE_MSG("SetFilePointerEx failed: {}", Common::GetLastErrorMsg());
}
return new_file_pointer.QuadPart;
}
void File::Unlink() {
FILE_DISPOSITION_INFORMATION disposition;
IO_STATUS_BLOCK iosb;
disposition.DeleteFile = TRUE;
NtSetInformationFile(handle, &iosb, &disposition, sizeof(disposition),
FileDispositionInformation);
}
#else
#endif
} // namespace Core::FileSys

View File

@ -4,12 +4,12 @@
#include <algorithm> #include <algorithm>
#include "common/config.h" #include "common/config.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/devices/logger.h"
#include "core/devices/nop_device.h"
#include "core/file_sys/fs.h" #include "core/file_sys/fs.h"
namespace Core::FileSys { namespace Core::FileSys {
constexpr int RESERVED_HANDLES = 3; // First 3 handles are stdin,stdout,stderr
void MntPoints::Mount(const std::filesystem::path& host_folder, const std::string& guest_folder, void MntPoints::Mount(const std::filesystem::path& host_folder, const std::string& guest_folder,
bool read_only) { bool read_only) {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
@ -135,7 +135,6 @@ int HandleTable::CreateHandle() {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
auto* file = new File{}; auto* file = new File{};
file->is_directory = false;
file->is_opened = false; file->is_opened = false;
int existingFilesNum = m_files.size(); int existingFilesNum = m_files.size();
@ -143,23 +142,23 @@ int HandleTable::CreateHandle() {
for (int index = 0; index < existingFilesNum; index++) { for (int index = 0; index < existingFilesNum; index++) {
if (m_files.at(index) == nullptr) { if (m_files.at(index) == nullptr) {
m_files[index] = file; m_files[index] = file;
return index + RESERVED_HANDLES; return index;
} }
} }
m_files.push_back(file); m_files.push_back(file);
return m_files.size() + RESERVED_HANDLES - 1; return m_files.size() - 1;
} }
void HandleTable::DeleteHandle(int d) { void HandleTable::DeleteHandle(int d) {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
delete m_files.at(d - RESERVED_HANDLES); delete m_files.at(d);
m_files[d - RESERVED_HANDLES] = nullptr; m_files[d] = nullptr;
} }
File* HandleTable::GetFile(int d) { File* HandleTable::GetFile(int d) {
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
return m_files.at(d - RESERVED_HANDLES); return m_files.at(d);
} }
File* HandleTable::GetFile(const std::filesystem::path& host_name) { File* HandleTable::GetFile(const std::filesystem::path& host_name) {
@ -171,4 +170,20 @@ File* HandleTable::GetFile(const std::filesystem::path& host_name) {
return nullptr; return nullptr;
} }
void HandleTable::CreateStdHandles() {
auto setup = [this](const char* path, auto* device) {
int fd = CreateHandle();
auto* file = GetFile(fd);
file->is_opened = true;
file->type = FileType::Device;
file->m_guest_name = path;
file->device =
std::shared_ptr<Devices::BaseDevice>{reinterpret_cast<Devices::BaseDevice*>(device)};
};
// order matters
setup("/dev/stdin", new Devices::NopDevice(0)); // stdin
setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout
setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr
}
} // namespace Core::FileSys } // namespace Core::FileSys

View File

@ -9,6 +9,7 @@
#include <vector> #include <vector>
#include <tsl/robin_map.h> #include <tsl/robin_map.h>
#include "common/io_file.h" #include "common/io_file.h"
#include "core/devices/base_device.h"
namespace Core::FileSys { namespace Core::FileSys {
@ -55,15 +56,22 @@ struct DirEntry {
bool isFile; bool isFile;
}; };
enum class FileType {
Regular, // standard file
Directory,
Device,
};
struct File { struct File {
std::atomic_bool is_opened{}; std::atomic_bool is_opened{};
std::atomic_bool is_directory{}; std::atomic<FileType> type{FileType::Regular};
std::filesystem::path m_host_name; std::filesystem::path m_host_name;
std::string m_guest_name; std::string m_guest_name;
Common::FS::IOFile f; Common::FS::IOFile f;
std::vector<DirEntry> dirents; std::vector<DirEntry> dirents;
u32 dirents_index; u32 dirents_index;
std::mutex m_mutex; std::mutex m_mutex;
std::shared_ptr<Devices::BaseDevice> device; // only valid for type == Device
}; };
class HandleTable { class HandleTable {
@ -76,6 +84,8 @@ public:
File* GetFile(int d); File* GetFile(int d);
File* GetFile(const std::filesystem::path& host_name); File* GetFile(const std::filesystem::path& host_name);
void CreateStdHandles();
private: private:
std::vector<File*> m_files; std::vector<File*> m_files;
std::mutex m_mutex; std::mutex m_mutex;

View File

@ -9,7 +9,7 @@
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
namespace Libraries::Ajm { namespace Libraries::Ajm {
@ -19,7 +19,7 @@ constexpr int ORBIS_AJM_CHANNELMASK_QUAD = 0x0033;
constexpr int ORBIS_AJM_CHANNELMASK_5POINT1 = 0x060F; constexpr int ORBIS_AJM_CHANNELMASK_5POINT1 = 0x060F;
constexpr int ORBIS_AJM_CHANNELMASK_7POINT1 = 0x063F; constexpr int ORBIS_AJM_CHANNELMASK_7POINT1 = 0x063F;
static std::unique_ptr<AjmContext> context{}; static std::unordered_map<u32, std::unique_ptr<AjmContext>> contexts{};
u32 GetChannelMask(u32 num_channels) { u32 GetChannelMask(u32 num_channels) {
switch (num_channels) { switch (num_channels) {
@ -40,7 +40,13 @@ u32 GetChannelMask(u32 num_channels) {
int PS4_SYSV_ABI sceAjmBatchCancel(const u32 context_id, const u32 batch_id) { int PS4_SYSV_ABI sceAjmBatchCancel(const u32 context_id, const u32 batch_id) {
LOG_INFO(Lib_Ajm, "called context_id = {} batch_id = {}", context_id, batch_id); LOG_INFO(Lib_Ajm, "called context_id = {} batch_id = {}", context_id, batch_id);
return context->BatchCancel(batch_id);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->BatchCancel(batch_id);
} }
int PS4_SYSV_ABI sceAjmBatchErrorDump() { int PS4_SYSV_ABI sceAjmBatchErrorDump() {
@ -90,14 +96,26 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context_id, u8* p_batch, u32 batch_s
u32* out_batch_id) { u32* out_batch_id) {
LOG_TRACE(Lib_Ajm, "called context = {}, batch_size = {:#x}, priority = {}", context_id, LOG_TRACE(Lib_Ajm, "called context = {}, batch_size = {:#x}, priority = {}", context_id,
batch_size, priority); batch_size, priority);
return context->BatchStartBuffer(p_batch, batch_size, priority, batch_error, out_batch_id);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->BatchStartBuffer(p_batch, batch_size, priority, batch_error, out_batch_id);
} }
int PS4_SYSV_ABI sceAjmBatchWait(const u32 context_id, const u32 batch_id, const u32 timeout, int PS4_SYSV_ABI sceAjmBatchWait(const u32 context_id, const u32 batch_id, const u32 timeout,
AjmBatchError* const batch_error) { AjmBatchError* const batch_error) {
LOG_TRACE(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context_id, batch_id, LOG_TRACE(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context_id, batch_id,
timeout); timeout);
return context->BatchWait(batch_id, timeout, batch_error);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->BatchWait(batch_id, timeout, batch_error);
} }
int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() { int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() {
@ -117,12 +135,12 @@ int PS4_SYSV_ABI sceAjmFinalize() {
int PS4_SYSV_ABI sceAjmInitialize(s64 reserved, u32* p_context_id) { int PS4_SYSV_ABI sceAjmInitialize(s64 reserved, u32* p_context_id) {
LOG_INFO(Lib_Ajm, "called reserved = {}", reserved); LOG_INFO(Lib_Ajm, "called reserved = {}", reserved);
ASSERT_MSG(context == nullptr, "Multiple contexts are currently unsupported.");
if (p_context_id == nullptr || reserved != 0) { if (p_context_id == nullptr || reserved != 0) {
return ORBIS_AJM_ERROR_INVALID_PARAMETER; return ORBIS_AJM_ERROR_INVALID_PARAMETER;
} }
*p_context_id = 1; u32 id = contexts.size() + 1;
context = std::make_unique<AjmContext>(); *p_context_id = id;
contexts.emplace(id, std::make_unique<AjmContext>());
return ORBIS_OK; return ORBIS_OK;
} }
@ -135,12 +153,24 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context_id, AjmCodecType codec_type,
AjmInstanceFlags flags, u32* out_instance) { AjmInstanceFlags flags, u32* out_instance) {
LOG_INFO(Lib_Ajm, "called context = {}, codec_type = {}, flags = {:#x}", context_id, LOG_INFO(Lib_Ajm, "called context = {}, codec_type = {}, flags = {:#x}", context_id,
magic_enum::enum_name(codec_type), flags.raw); magic_enum::enum_name(codec_type), flags.raw);
return context->InstanceCreate(codec_type, flags, out_instance);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->InstanceCreate(codec_type, flags, out_instance);
} }
int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context_id, u32 instance_id) { int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context_id, u32 instance_id) {
LOG_INFO(Lib_Ajm, "called context = {}, instance = {}", context_id, instance_id); LOG_INFO(Lib_Ajm, "called context = {}, instance = {}", context_id, instance_id);
return context->InstanceDestroy(instance_id);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->InstanceDestroy(instance_id);
} }
int PS4_SYSV_ABI sceAjmInstanceExtend() { int PS4_SYSV_ABI sceAjmInstanceExtend() {
@ -168,7 +198,13 @@ int PS4_SYSV_ABI sceAjmModuleRegister(u32 context_id, AjmCodecType codec_type, s
if (reserved != 0) { if (reserved != 0) {
return ORBIS_AJM_ERROR_INVALID_PARAMETER; return ORBIS_AJM_ERROR_INVALID_PARAMETER;
} }
return context->ModuleRegister(codec_type);
auto it = contexts.find(context_id);
if (it == contexts.end()) {
return ORBIS_AJM_ERROR_INVALID_CONTEXT;
}
return it->second->ModuleRegister(codec_type);
} }
int PS4_SYSV_ABI sceAjmModuleUnregister() { int PS4_SYSV_ABI sceAjmModuleUnregister() {

View File

@ -3,6 +3,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/thread.h"
#include "core/libraries/ajm/ajm.h" #include "core/libraries/ajm/ajm.h"
#include "core/libraries/ajm/ajm_at9.h" #include "core/libraries/ajm/ajm_at9.h"
#include "core/libraries/ajm/ajm_context.h" #include "core/libraries/ajm/ajm_context.h"
@ -53,6 +54,7 @@ s32 AjmContext::ModuleRegister(AjmCodecType type) {
} }
void AjmContext::WorkerThread(std::stop_token stop) { void AjmContext::WorkerThread(std::stop_token stop) {
Common::SetCurrentThreadName("shadPS4:AjmWorker");
while (!stop.stop_requested()) { while (!stop.stop_requested()) {
auto batch = batch_queue.PopWait(stop); auto batch = batch_queue.PopWait(stop);
if (batch != nullptr) { if (batch != nullptr) {

View File

@ -5,7 +5,7 @@
#include "core/libraries/ajm/ajm_instance.h" #include "core/libraries/ajm/ajm_instance.h"
#include "core/libraries/ajm/ajm_mp3.h" #include "core/libraries/ajm/ajm_mp3.h"
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
namespace Libraries::Ajm { namespace Libraries::Ajm {

View File

@ -12,6 +12,8 @@ extern "C" {
#include <libswresample/swresample.h> #include <libswresample/swresample.h>
} }
#include "common/support/avdec.h"
namespace Libraries::Ajm { namespace Libraries::Ajm {
// Following tables have been reversed from AJM library // Following tables have been reversed from AJM library

View File

@ -6,34 +6,30 @@
#include "app_content.h" #include "app_content.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/config.h" #include "common/config.h"
#include "common/io_file.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/path_util.h"
#include "common/singleton.h" #include "common/singleton.h"
#include "common/string_util.h"
#include "core/file_format/psf.h" #include "core/file_format/psf.h"
#include "core/file_sys/fs.h" #include "core/file_sys/fs.h"
#include "core/libraries/error_codes.h" #include "core/libraries/app_content/app_content_error.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
namespace Libraries::AppContent { namespace Libraries::AppContent {
int32_t addcont_count = 0;
struct AddContInfo { struct AddContInfo {
char entitlementLabel[ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE]; char entitlement_label[ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE];
OrbisAppContentAddcontDownloadStatus status; OrbisAppContentAddcontDownloadStatus status;
OrbisAppContentGetEntitlementKey key; OrbisAppContentGetEntitlementKey key;
}; };
std::array<AddContInfo, ORBIS_APP_CONTENT_INFO_LIST_MAX_SIZE> addcont_info = {{ static std::array<AddContInfo, ORBIS_APP_CONTENT_INFO_LIST_MAX_SIZE> addcont_info = {{
{"0000000000000000", {"0000000000000000",
ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED, OrbisAppContentAddcontDownloadStatus::Installed,
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00}}, 0x00}},
}}; }};
std::string title_id; static s32 addcont_count = 0;
static std::string title_id;
int PS4_SYSV_ABI _Z5dummyv() { int PS4_SYSV_ABI _Z5dummyv() {
LOG_ERROR(Lib_AppContent, "(STUBBED) called"); LOG_ERROR(Lib_AppContent, "(STUBBED) called");
@ -64,12 +60,11 @@ int PS4_SYSV_ABI sceAppContentAddcontMount(u32 service_label,
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance(); auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
for (int i = 0; i < addcont_count; i++) { for (int i = 0; i < addcont_count; i++) {
if (strncmp(entitlement_label->data, addcont_info[i].entitlementLabel, if (strncmp(entitlement_label->data, addcont_info[i].entitlement_label,
ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) {
continue; continue;
} }
if (addcont_info[i].status != OrbisAppContentAddcontDownloadStatus::Installed) {
if (addcont_info[i].status != ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED) {
return ORBIS_APP_CONTENT_ERROR_NOT_FOUND; return ORBIS_APP_CONTENT_ERROR_NOT_FOUND;
} }
@ -170,14 +165,14 @@ int PS4_SYSV_ABI sceAppContentGetAddcontInfo(u32 service_label,
} }
for (auto i = 0; i < addcont_count; i++) { for (auto i = 0; i < addcont_count; i++) {
if (strncmp(entitlementLabel->data, addcont_info[i].entitlementLabel, if (strncmp(entitlementLabel->data, addcont_info[i].entitlement_label,
ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) {
continue; continue;
} }
LOG_INFO(Lib_AppContent, "found DLC {}", entitlementLabel->data); LOG_INFO(Lib_AppContent, "found DLC {}", entitlementLabel->data);
strncpy(info->entitlement_label.data, addcont_info[i].entitlementLabel, strncpy(info->entitlement_label.data, addcont_info[i].entitlement_label,
ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE); ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE);
info->status = addcont_info[i].status; info->status = addcont_info[i].status;
return ORBIS_OK; return ORBIS_OK;
@ -202,7 +197,7 @@ int PS4_SYSV_ABI sceAppContentGetAddcontInfoList(u32 service_label,
int dlcs_to_list = addcont_count < list_num ? addcont_count : list_num; int dlcs_to_list = addcont_count < list_num ? addcont_count : list_num;
for (int i = 0; i < dlcs_to_list; i++) { for (int i = 0; i < dlcs_to_list; i++) {
strncpy(list[i].entitlement_label.data, addcont_info[i].entitlementLabel, strncpy(list[i].entitlement_label.data, addcont_info[i].entitlement_label,
ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE); ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE);
list[i].status = addcont_info[i].status; list[i].status = addcont_info[i].status;
} }
@ -224,7 +219,7 @@ int PS4_SYSV_ABI sceAppContentGetEntitlementKey(
} }
for (int i = 0; i < addcont_count; i++) { for (int i = 0; i < addcont_count; i++) {
if (strncmp(entitlement_label->data, addcont_info[i].entitlementLabel, if (strncmp(entitlement_label->data, addcont_info[i].entitlement_label,
ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) { ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) {
continue; continue;
} }
@ -252,21 +247,19 @@ int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initPar
} else { } else {
UNREACHABLE_MSG("Failed to get TITLE_ID"); UNREACHABLE_MSG("Failed to get TITLE_ID");
} }
auto addon_path = addons_dir / title_id; const auto addon_path = addons_dir / title_id;
if (std::filesystem::exists(addon_path)) { if (!std::filesystem::exists(addon_path)) {
for (const auto& entry : std::filesystem::directory_iterator(addon_path)) { return ORBIS_OK;
if (entry.is_directory()) {
auto entitlement_label = entry.path().filename().string();
AddContInfo info{};
info.status = ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED;
strcpy(info.entitlementLabel, entitlement_label.c_str());
addcont_info[addcont_count++] = info;
}
}
} }
for (const auto& entry : std::filesystem::directory_iterator(addon_path)) {
if (entry.is_directory()) {
auto entitlement_label = entry.path().filename().string();
auto& info = addcont_info[addcont_count++];
info.status = OrbisAppContentAddcontDownloadStatus::Installed;
entitlement_label.copy(info.entitlement_label, sizeof(info.entitlement_label));
}
}
return ORBIS_OK; return ORBIS_OK;
} }
@ -314,9 +307,11 @@ int PS4_SYSV_ABI sceAppContentTemporaryDataMount() {
int PS4_SYSV_ABI sceAppContentTemporaryDataMount2(OrbisAppContentTemporaryDataOption option, int PS4_SYSV_ABI sceAppContentTemporaryDataMount2(OrbisAppContentTemporaryDataOption option,
OrbisAppContentMountPoint* mountPoint) { OrbisAppContentMountPoint* mountPoint) {
if (mountPoint == nullptr) if (mountPoint == nullptr) {
return ORBIS_APP_CONTENT_ERROR_PARAMETER; return ORBIS_APP_CONTENT_ERROR_PARAMETER;
strncpy(mountPoint->data, "/temp0", 16); }
static constexpr std::string_view TmpMount = "/temp0";
TmpMount.copy(mountPoint->data, sizeof(mountPoint->data));
LOG_INFO(Lib_AppContent, "sceAppContentTemporaryDataMount2: option = {}, mountPoint = {}", LOG_INFO(Lib_AppContent, "sceAppContentTemporaryDataMount2: option = {}, mountPoint = {}",
option, mountPoint->data); option, mountPoint->data);
return ORBIS_OK; return ORBIS_OK;

View File

@ -30,7 +30,7 @@ struct OrbisAppContentBootParam {
char reserved2[32]; char reserved2[32];
}; };
typedef u32 OrbisAppContentTemporaryDataOption; using OrbisAppContentTemporaryDataOption = u32;
constexpr int ORBIS_APP_CONTENT_MOUNTPOINT_DATA_MAXSIZE = 16; constexpr int ORBIS_APP_CONTENT_MOUNTPOINT_DATA_MAXSIZE = 16;
@ -44,12 +44,12 @@ constexpr int ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE = 17;
constexpr int ORBIS_APP_CONTENT_ENTITLEMENT_KEY_SIZE = 16; constexpr int ORBIS_APP_CONTENT_ENTITLEMENT_KEY_SIZE = 16;
constexpr int ORBIS_APP_CONTENT_INFO_LIST_MAX_SIZE = 2500; constexpr int ORBIS_APP_CONTENT_INFO_LIST_MAX_SIZE = 2500;
enum OrbisAppContentAddcontDownloadStatus : u32 { enum class OrbisAppContentAddcontDownloadStatus : u32 {
ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_NO_EXTRA_DATA = 0, NoExtraData = 0,
ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_NO_IN_QUEUE = 1, NoInQueue = 1,
ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_DOWNLOADING = 2, Downloading = 2,
ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_DOWNLOAD_SUSPENDED = 3, DownloadSuspended = 3,
ORBIS_APP_CONTENT_ADDCONT_DOWNLOAD_STATUS_INSTALLED = 4 Installed = 4
}; };
struct OrbisNpUnifiedEntitlementLabel { struct OrbisNpUnifiedEntitlementLabel {
@ -57,11 +57,11 @@ struct OrbisNpUnifiedEntitlementLabel {
char padding[3]; char padding[3];
}; };
typedef u32 OrbisAppContentAppParamId; using OrbisAppContentAppParamId = u32;
struct OrbisAppContentAddcontInfo { struct OrbisAppContentAddcontInfo {
OrbisNpUnifiedEntitlementLabel entitlement_label; OrbisNpUnifiedEntitlementLabel entitlement_label;
u32 status; OrbisAppContentAddcontDownloadStatus status;
}; };
struct OrbisAppContentGetEntitlementKey { struct OrbisAppContentGetEntitlementKey {

View File

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/libraries/error_codes.h"
// AppContent library
constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002;
constexpr int ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT = 0x80D90007;
constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005;

View File

@ -2,32 +2,32 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <memory> #include <memory>
#include <magic_enum.hpp> #include <magic_enum/magic_enum.hpp>
#include "audio_core/sdl_audio.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/audio/audioout.h" #include "core/libraries/audio/audioout.h"
#include "core/libraries/error_codes.h" #include "core/libraries/audio/audioout_error.h"
#include "core/libraries/audio/sdl_audio.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
namespace Libraries::AudioOut { namespace Libraries::AudioOut {
static std::unique_ptr<Audio::SDLAudio> audio; static std::unique_ptr<SDLAudioOut> audio;
static std::string_view GetAudioOutPort(u32 port) { static std::string_view GetAudioOutPort(OrbisAudioOutPort port) {
switch (port) { switch (port) {
case ORBIS_AUDIO_OUT_PORT_TYPE_MAIN: case OrbisAudioOutPort::Main:
return "MAIN"; return "MAIN";
case ORBIS_AUDIO_OUT_PORT_TYPE_BGM: case OrbisAudioOutPort::Bgm:
return "BGM"; return "BGM";
case ORBIS_AUDIO_OUT_PORT_TYPE_VOICE: case OrbisAudioOutPort::Voice:
return "VOICE"; return "VOICE";
case ORBIS_AUDIO_OUT_PORT_TYPE_PERSONAL: case OrbisAudioOutPort::Personal:
return "PERSONAL"; return "PERSONAL";
case ORBIS_AUDIO_OUT_PORT_TYPE_PADSPK: case OrbisAudioOutPort::Padspk:
return "PADSPK"; return "PADSPK";
case ORBIS_AUDIO_OUT_PORT_TYPE_AUX: case OrbisAudioOutPort::Aux:
return "AUX"; return "AUX";
default: default:
return "INVALID"; return "INVALID";
@ -36,21 +36,21 @@ static std::string_view GetAudioOutPort(u32 port) {
static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) { static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) {
switch (param) { switch (param) {
case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO: case OrbisAudioOutParamFormat::S16Mono:
return "S16_MONO"; return "S16_MONO";
case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO: case OrbisAudioOutParamFormat::S16Stereo:
return "S16_STEREO"; return "S16_STEREO";
case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH: case OrbisAudioOutParamFormat::S16_8CH:
return "S16_8CH"; return "S16_8CH";
case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO: case OrbisAudioOutParamFormat::FloatMono:
return "FLOAT_MONO"; return "FLOAT_MONO";
case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO: case OrbisAudioOutParamFormat::FloatStereo:
return "FLOAT_STEREO"; return "FLOAT_STEREO";
case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH: case OrbisAudioOutParamFormat::Float_8CH:
return "FLOAT_8CH"; return "FLOAT_8CH";
case ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD: case OrbisAudioOutParamFormat::S16_8CH_Std:
return "S16_8CH_STD"; return "S16_8CH_STD";
case ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD: case OrbisAudioOutParamFormat::Float_8CH_Std:
return "FLOAT_8CH_STD"; return "FLOAT_8CH_STD";
default: default:
return "INVALID"; return "INVALID";
@ -59,11 +59,11 @@ static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) {
static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) { static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) {
switch (attr) { switch (attr) {
case ORBIS_AUDIO_OUT_PARAM_ATTR_NONE: case OrbisAudioOutParamAttr::None:
return "NONE"; return "NONE";
case ORBIS_AUDIO_OUT_PARAM_ATTR_RESTRICTED: case OrbisAudioOutParamAttr::Restricted:
return "RESTRICTED"; return "RESTRICTED";
case ORBIS_AUDIO_OUT_PARAM_ATTR_MIX_TO_MAIN: case OrbisAudioOutParamAttr::MixToMain:
return "MIX_TO_MAIN"; return "MIX_TO_MAIN";
default: default:
return "INVALID"; return "INVALID";
@ -180,29 +180,23 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
} }
int type = 0; const auto [type, channels_num] = audio->GetStatus(handle);
int channels_num = 0;
if (const auto err = audio->AudioOutGetStatus(handle, &type, &channels_num); err != ORBIS_OK) {
return err;
}
state->rerouteCounter = 0; state->rerouteCounter = 0;
state->volume = 127; // max volume state->volume = 127;
switch (type) { switch (type) {
case ORBIS_AUDIO_OUT_PORT_TYPE_MAIN: case OrbisAudioOutPort::Main:
case ORBIS_AUDIO_OUT_PORT_TYPE_BGM: case OrbisAudioOutPort::Bgm:
case ORBIS_AUDIO_OUT_PORT_TYPE_VOICE: case OrbisAudioOutPort::Voice:
state->output = 1; state->output = 1;
state->channel = (channels_num > 2 ? 2 : channels_num); state->channel = (channels_num > 2 ? 2 : channels_num);
break; break;
case ORBIS_AUDIO_OUT_PORT_TYPE_PERSONAL: case OrbisAudioOutPort::Personal:
case ORBIS_AUDIO_OUT_PORT_TYPE_PADSPK: case OrbisAudioOutPort::Padspk:
state->output = 4; state->output = 4;
state->channel = 1; state->channel = 1;
break; break;
case ORBIS_AUDIO_OUT_PORT_TYPE_AUX: case OrbisAudioOutPort::Aux:
state->output = 0; state->output = 0;
state->channel = 0; state->channel = 0;
break; break;
@ -243,7 +237,7 @@ int PS4_SYSV_ABI sceAudioOutInit() {
if (audio != nullptr) { if (audio != nullptr) {
return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT; return ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT;
} }
audio = std::make_unique<Audio::SDLAudio>(); audio = std::make_unique<SDLAudioOut>();
return ORBIS_OK; return ORBIS_OK;
} }
@ -287,7 +281,8 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
user_id, GetAudioOutPort(port_type), index, length, sample_rate, user_id, GetAudioOutPort(port_type), index, length, sample_rate,
GetAudioOutParamFormat(param_type.data_format), GetAudioOutParamFormat(param_type.data_format),
GetAudioOutParamAttr(param_type.attributes)); GetAudioOutParamAttr(param_type.attributes));
if ((port_type < 0 || port_type > 4) && (port_type != 127)) { if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) &&
(port_type != OrbisAudioOutPort::Aux)) {
LOG_ERROR(Lib_AudioOut, "Invalid port type"); LOG_ERROR(Lib_AudioOut, "Invalid port type");
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE;
} }
@ -303,18 +298,19 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
if (index != 0) { if (index != 0) {
LOG_ERROR(Lib_AudioOut, "index is not valid !=0 {}", index); LOG_ERROR(Lib_AudioOut, "index is not valid !=0 {}", index);
} }
OrbisAudioOutParamFormat format = param_type.data_format; const auto format = param_type.data_format.Value();
if (format < 0 || format > 7) { if (format < OrbisAudioOutParamFormat::S16Mono ||
format > OrbisAudioOutParamFormat::Float_8CH_Std) {
LOG_ERROR(Lib_AudioOut, "Invalid format"); LOG_ERROR(Lib_AudioOut, "Invalid format");
return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT; return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT;
} }
OrbisAudioOutParamAttr attr = param_type.attributes; const auto attr = param_type.attributes;
if (attr < 0 || attr > 2) { if (attr < OrbisAudioOutParamAttr::None || attr > OrbisAudioOutParamAttr::MixToMain) {
// TODO Handle attributes in output audio device // TODO Handle attributes in output audio device
LOG_ERROR(Lib_AudioOut, "Invalid format attribute"); LOG_ERROR(Lib_AudioOut, "Invalid format attribute");
return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT; return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT;
} }
return audio->AudioOutOpen(port_type, length, sample_rate, format); return audio->Open(port_type, length, sample_rate, format);
} }
int PS4_SYSV_ABI sceAudioOutOpenEx() { int PS4_SYSV_ABI sceAudioOutOpenEx() {
@ -330,7 +326,7 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) {
// Nothing to output // Nothing to output
return ORBIS_OK; return ORBIS_OK;
} }
return audio->AudioOutOutput(handle, ptr); return audio->Output(handle, ptr);
} }
int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) { int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) {
@ -435,7 +431,7 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) { if (handle < 1 || handle > SCE_AUDIO_OUT_NUM_PORTS) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
} }
return audio->AudioOutSetVolume(handle, flag, vol); return audio->SetVolume(handle, flag, vol);
} }
int PS4_SYSV_ABI sceAudioOutSetVolumeDown() { int PS4_SYSV_ABI sceAudioOutSetVolumeDown() {

View File

@ -9,46 +9,36 @@
namespace Libraries::AudioOut { namespace Libraries::AudioOut {
constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value // Main up to 8 ports, BGM 1 port, voice up to 4 ports,
// main up to 8 ports, BGM 1 port, voice up to 4 ports,
// personal up to 4 ports, padspk up to 5 ports, aux 1 port // personal up to 4 ports, padspk up to 5 ports, aux 1 port
constexpr int SCE_AUDIO_OUT_NUM_PORTS = 22; constexpr int SCE_AUDIO_OUT_NUM_PORTS = 22;
constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value
enum OrbisAudioOutPort { enum class OrbisAudioOutPort { Main = 0, Bgm = 1, Voice = 2, Personal = 3, Padspk = 4, Aux = 127 };
ORBIS_AUDIO_OUT_PORT_TYPE_MAIN = 0,
ORBIS_AUDIO_OUT_PORT_TYPE_BGM = 1, enum class OrbisAudioOutParamFormat {
ORBIS_AUDIO_OUT_PORT_TYPE_VOICE = 2, S16Mono = 0,
ORBIS_AUDIO_OUT_PORT_TYPE_PERSONAL = 3, S16Stereo = 1,
ORBIS_AUDIO_OUT_PORT_TYPE_PADSPK = 4, S16_8CH = 2,
ORBIS_AUDIO_OUT_PORT_TYPE_AUX = 127 FloatMono = 3,
FloatStereo = 4,
Float_8CH = 5,
S16_8CH_Std = 6,
Float_8CH_Std = 7
}; };
enum OrbisAudioOutParamFormat { enum class OrbisAudioOutParamAttr {
ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO = 0, None = 0,
ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO = 1, Restricted = 1,
ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH = 2, MixToMain = 2,
ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO = 3,
ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO = 4,
ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH = 5,
ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD = 6,
ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD = 7
}; };
enum OrbisAudioOutParamAttr { union OrbisAudioOutParamExtendedInformation {
ORBIS_AUDIO_OUT_PARAM_ATTR_NONE = 0, BitField<0, 8, OrbisAudioOutParamFormat> data_format;
ORBIS_AUDIO_OUT_PARAM_ATTR_RESTRICTED = 1, BitField<8, 8, u32> reserve0;
ORBIS_AUDIO_OUT_PARAM_ATTR_MIX_TO_MAIN = 2, BitField<16, 4, OrbisAudioOutParamAttr> attributes;
}; BitField<20, 10, u32> reserve1;
BitField<31, 1, u32> unused;
struct OrbisAudioOutParamExtendedInformation {
union {
BitField<0, 8, OrbisAudioOutParamFormat> data_format;
BitField<8, 8, u32> reserve0;
BitField<16, 4, OrbisAudioOutParamAttr> attributes;
BitField<20, 10, u32> reserve1;
BitField<31, 1, u32> unused;
};
}; };
struct OrbisAudioOutOutputParam { struct OrbisAudioOutOutputParam {

View File

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/libraries/error_codes.h"
// AudioOut library
constexpr int ORBIS_AUDIO_OUT_ERROR_NOT_OPENED = 0x80260001;
constexpr int ORBIS_AUDIO_OUT_ERROR_BUSY = 0x80260002;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PORT = 0x80260003;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_POINTER = 0x80260004;
constexpr int ORBIS_AUDIO_OUT_ERROR_PORT_FULL = 0x80260005;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_SIZE = 0x80260006;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT = 0x80260007;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_SAMPLE_FREQ = 0x80260008;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_VOLUME = 0x80260009;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE = 0x8026000A;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_CONF_TYPE = 0x8026000C;
constexpr int ORBIS_AUDIO_OUT_ERROR_OUT_OF_MEMORY = 0x8026000D;
constexpr int ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT = 0x8026000E;
constexpr int ORBIS_AUDIO_OUT_ERROR_NOT_INIT = 0x8026000F;
constexpr int ORBIS_AUDIO_OUT_ERROR_MEMORY = 0x80260010;
constexpr int ORBIS_AUDIO_OUT_ERROR_SYSTEM_RESOURCE = 0x80260011;
constexpr int ORBIS_AUDIO_OUT_ERROR_TRANS_EVENT = 0x80260012;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_FLAG = 0x80260013;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_MIXLEVEL = 0x80260014;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_ARG = 0x80260015;
constexpr int ORBIS_AUDIO_OUT_ERROR_INVALID_PARAM = 0x80260016;
constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_FATAL = 0x80260200;
constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_API_PARAM = 0x80260201;
constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_CONFIG = 0x80260202;
constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_NOT_INITIALIZED = 0x80260203;
constexpr int ORBIS_AUDIO_OUT_ERROR_MASTERING_INVALID_STATES_ID = 0x80260204;

View File

@ -0,0 +1,141 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include <SDL3/SDL_audio.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_timer.h>
#include "common/assert.h"
#include "core/libraries/audio/audioout_error.h"
#include "core/libraries/audio/sdl_audio.h"
namespace Libraries::AudioOut {
constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold
s32 SDLAudioOut::Open(OrbisAudioOutPort type, u32 samples_num, u32 freq,
OrbisAudioOutParamFormat format) {
std::scoped_lock lock{m_mutex};
const auto port = std::ranges::find(ports_out, false, &PortOut::is_open);
if (port == ports_out.end()) {
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
}
port->is_open = true;
port->type = type;
port->samples_num = samples_num;
port->freq = freq;
port->format = format;
SDL_AudioFormat sampleFormat;
switch (format) {
case OrbisAudioOutParamFormat::S16Mono:
sampleFormat = SDL_AUDIO_S16;
port->channels_num = 1;
port->sample_size = 2;
break;
case OrbisAudioOutParamFormat::FloatMono:
sampleFormat = SDL_AUDIO_F32;
port->channels_num = 1;
port->sample_size = 4;
break;
case OrbisAudioOutParamFormat::S16Stereo:
sampleFormat = SDL_AUDIO_S16;
port->channels_num = 2;
port->sample_size = 2;
break;
case OrbisAudioOutParamFormat::FloatStereo:
sampleFormat = SDL_AUDIO_F32;
port->channels_num = 2;
port->sample_size = 4;
break;
case OrbisAudioOutParamFormat::S16_8CH:
sampleFormat = SDL_AUDIO_S16;
port->channels_num = 8;
port->sample_size = 2;
break;
case OrbisAudioOutParamFormat::Float_8CH:
sampleFormat = SDL_AUDIO_F32;
port->channels_num = 8;
port->sample_size = 4;
break;
case OrbisAudioOutParamFormat::S16_8CH_Std:
sampleFormat = SDL_AUDIO_S16;
port->channels_num = 8;
port->sample_size = 2;
break;
case OrbisAudioOutParamFormat::Float_8CH_Std:
sampleFormat = SDL_AUDIO_F32;
port->channels_num = 8;
port->sample_size = 4;
break;
default:
UNREACHABLE_MSG("Unknown format");
}
port->volume.fill(Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB);
SDL_AudioSpec fmt;
SDL_zero(fmt);
fmt.format = sampleFormat;
fmt.channels = port->channels_num;
fmt.freq = freq;
port->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL);
SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port->stream));
return std::distance(ports_out.begin(), port) + 1;
}
s32 SDLAudioOut::Output(s32 handle, const void* ptr) {
auto& port = ports_out.at(handle - 1);
if (!port.is_open) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size);
while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
SDL_Delay(0);
}
return result ? ORBIS_OK : -1;
}
s32 SDLAudioOut::SetVolume(s32 handle, s32 bitflag, s32* volume) {
using Libraries::AudioOut::OrbisAudioOutParamFormat;
auto& port = ports_out.at(handle - 1);
if (!port.is_open) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) {
auto bit = bitflag & 0x1u;
if (bit == 1) {
int src_index = i;
if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std ||
port.format == OrbisAudioOutParamFormat::S16_8CH_Std) {
switch (i) {
case 4:
src_index = 6;
break;
case 5:
src_index = 7;
break;
case 6:
src_index = 4;
break;
case 7:
src_index = 5;
break;
default:
break;
}
}
port.volume[i] = volume[src_index];
}
}
return ORBIS_OK;
}
} // namespace Libraries::AudioOut

View File

@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <shared_mutex>
#include <SDL3/SDL_audio.h>
#include "core/libraries/audio/audioout.h"
namespace Libraries::AudioOut {
class SDLAudioOut {
public:
explicit SDLAudioOut() = default;
~SDLAudioOut() = default;
s32 Open(OrbisAudioOutPort type, u32 samples_num, u32 freq, OrbisAudioOutParamFormat format);
s32 Output(s32 handle, const void* ptr);
s32 SetVolume(s32 handle, s32 bitflag, s32* volume);
constexpr std::pair<OrbisAudioOutPort, int> GetStatus(s32 handle) const {
const auto& port = ports_out.at(handle - 1);
return std::make_pair(port.type, port.channels_num);
}
private:
struct PortOut {
SDL_AudioStream* stream;
u32 samples_num;
u32 freq;
OrbisAudioOutParamFormat format;
OrbisAudioOutPort type;
int channels_num;
std::array<int, 8> volume;
u8 sample_size;
bool is_open;
};
std::shared_mutex m_mutex;
std::array<PortOut, Libraries::AudioOut::SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
};
} // namespace Libraries::AudioOut

View File

@ -1,19 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "audio3d.h"
#include "audio3d_error.h"
#include "audio3d_impl.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/audio/audioout.h" #include "core/libraries/audio/audioout.h"
#include "core/libraries/audio3d/audio3d.h"
#include "core/libraries/audio3d/audio3d_error.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
namespace Libraries::Audio3d { namespace Libraries::Audio3d {
// Audio3d
int PS4_SYSV_ABI sceAudio3dInitialize(s64 iReserved) { int PS4_SYSV_ABI sceAudio3dInitialize(s64 iReserved) {
LOG_INFO(Lib_Audio3d, "iReserved = {}", iReserved); LOG_INFO(Lib_Audio3d, "iReserved = {}", iReserved);
return ORBIS_OK; return ORBIS_OK;
@ -25,18 +21,19 @@ int PS4_SYSV_ABI sceAudio3dTerminate() {
return ORBIS_OK; return ORBIS_OK;
} }
void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* sParameters) { void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* parameters) {
if (sParameters != NULL) { if (parameters == nullptr) {
sParameters->szSizeThis = sizeof(OrbisAudio3dOpenParameters);
sParameters->uiGranularity = 256;
sParameters->eRate = ORBIS_AUDIO3D_RATE_48000;
sParameters->uiMaxObjects = 512;
sParameters->uiQueueDepth = 2;
sParameters->eBufferMode = ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH;
sParameters->uiNumBeds = 2;
} else {
LOG_ERROR(Lib_Audio3d, "Invalid OpenParameters ptr"); LOG_ERROR(Lib_Audio3d, "Invalid OpenParameters ptr");
return;
} }
parameters->size_this = sizeof(OrbisAudio3dOpenParameters);
parameters->granularity = 256;
parameters->rate = OrbisAudio3dRate::Rate48000;
parameters->max_objects = 512;
parameters->queue_depth = 2;
parameters->buffer_mode = OrbisAudio3dBufferMode::AdvanceAndPush;
parameters->num_beds = 2;
} }
int PS4_SYSV_ABI sceAudio3dPortOpen(OrbisUserServiceUserId iUserId, int PS4_SYSV_ABI sceAudio3dPortOpen(OrbisUserServiceUserId iUserId,
@ -65,24 +62,24 @@ int PS4_SYSV_ABI sceAudio3dPortFlush(OrbisAudio3dPortId uiPortId) {
} }
int PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId uiPortId) { int PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId uiPortId) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId uiPortId, OrbisAudio3dBlocking eBlocking) { int PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId uiPortId, OrbisAudio3dBlocking eBlocking) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported(OrbisAudio3dPortId uiPortId, int PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported(OrbisAudio3dPortId uiPortId,
OrbisAudio3dAttributeId* pCapabilities, OrbisAudio3dAttributeId* pCapabilities,
unsigned int* pNumCapabilities) { u32* pNumCapabilities) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId uiPortId, unsigned int* pQueueLevel, int PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId uiPortId, u32* pQueueLevel,
unsigned int* pQueueAvailable) { u32* pQueueAvailable) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK; return ORBIS_OK;
} }
@ -107,24 +104,24 @@ int PS4_SYSV_ABI sceAudio3dObjectSetAttributes(OrbisAudio3dPortId uiPortId,
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, unsigned int uiNumChannels, int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, u32 uiNumChannels,
OrbisAudio3dFormat eFormat, const void* pBuffer, OrbisAudio3dFormat eFormat, const void* pBuffer,
unsigned int uiNumSamples) { u32 uiNumSamples) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId, LOG_TRACE(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId,
uiNumChannels, uiNumSamples); uiNumChannels, uiNumSamples);
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAudio3dBedWrite2(OrbisAudio3dPortId uiPortId, unsigned int uiNumChannels, int PS4_SYSV_ABI sceAudio3dBedWrite2(OrbisAudio3dPortId uiPortId, u32 uiNumChannels,
OrbisAudio3dFormat eFormat, const void* pBuffer, OrbisAudio3dFormat eFormat, const void* pBuffer,
unsigned int uiNumSamples, u32 uiNumSamples, OrbisAudio3dOutputRoute eOutputRoute,
OrbisAudio3dOutputRoute eOutputRoute, bool bRestricted) { bool bRestricted) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}, bRestricted = {}", LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}, bRestricted = {}",
uiPortId, uiNumChannels, uiNumSamples, bRestricted); uiPortId, uiNumChannels, uiNumSamples, bRestricted);
return ORBIS_OK; return ORBIS_OK;
} }
size_t PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize(unsigned int uiNumSpeakers, bool bIs3d) { size_t PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize(u32 uiNumSpeakers, bool bIs3d) {
LOG_INFO(Lib_Audio3d, "uiNumSpeakers = {}, bIs3d = {}", uiNumSpeakers, bIs3d); LOG_INFO(Lib_Audio3d, "uiNumSpeakers = {}, bIs3d = {}", uiNumSpeakers, bIs3d);
return ORBIS_OK; return ORBIS_OK;
} }
@ -152,7 +149,7 @@ int PS4_SYSV_ABI sceAudio3dDeleteSpeakerArray(OrbisAudio3dSpeakerArrayHandle han
int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients(OrbisAudio3dSpeakerArrayHandle handle, int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients(OrbisAudio3dSpeakerArrayHandle handle,
OrbisAudio3dPosition pos, float fSpread, OrbisAudio3dPosition pos, float fSpread,
float* pCoefficients, float* pCoefficients,
unsigned int uiNumCoefficients) { u32 uiNumCoefficients) {
LOG_INFO(Lib_Audio3d, "fSpread = {}, uiNumCoefficients = {}", fSpread, uiNumCoefficients); LOG_INFO(Lib_Audio3d, "fSpread = {}, uiNumCoefficients = {}", fSpread, uiNumCoefficients);
if (handle == nullptr) { if (handle == nullptr) {
LOG_ERROR(Lib_Audio3d, "invalid SpeakerArrayHandle"); LOG_ERROR(Lib_Audio3d, "invalid SpeakerArrayHandle");
@ -164,8 +161,7 @@ int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients(OrbisAudio3dSpeakerArr
int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2(OrbisAudio3dSpeakerArrayHandle handle, int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2(OrbisAudio3dSpeakerArrayHandle handle,
OrbisAudio3dPosition pos, float fSpread, OrbisAudio3dPosition pos, float fSpread,
float* pCoefficients, float* pCoefficients,
unsigned int uiNumCoefficients, u32 uiNumCoefficients, bool bHeightAware,
bool bHeightAware,
float fDownmixSpreadRadius) { float fDownmixSpreadRadius) {
LOG_INFO(Lib_Audio3d, LOG_INFO(Lib_Audio3d,
"fSpread = {}, uiNumCoefficients = {}, bHeightAware = {}, fDownmixSpreadRadius = {}", "fSpread = {}, uiNumCoefficients = {}, bHeightAware = {}, fDownmixSpreadRadius = {}",
@ -191,7 +187,7 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle) {
} }
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, const void* ptr) { s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, const void* ptr) {
LOG_INFO(Lib_Audio3d, "handle = {}", handle); LOG_TRACE(Lib_Audio3d, "handle = {}", handle);
if (ptr == nullptr) { if (ptr == nullptr) {
LOG_ERROR(Lib_Audio3d, "invalid Output ptr"); LOG_ERROR(Lib_Audio3d, "invalid Output ptr");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
@ -209,8 +205,8 @@ s32 PS4_SYSV_ABI sceAudio3dAudioOutOutputs(::Libraries::AudioOut::OrbisAudioOutO
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceAudio3dPortCreate(unsigned int uiGranularity, OrbisAudio3dRate eRate, int PS4_SYSV_ABI sceAudio3dPortCreate(u32 uiGranularity, OrbisAudio3dRate eRate, s64 iReserved,
s64 iReserved, OrbisAudio3dPortId* pId) { OrbisAudio3dPortId* pId) {
LOG_INFO(Lib_Audio3d, "uiGranularity = {}, iReserved = {}", uiGranularity, iReserved); LOG_INFO(Lib_Audio3d, "uiGranularity = {}, iReserved = {}", uiGranularity, iReserved);
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -15,56 +15,57 @@ namespace Libraries::Audio3d {
class Audio3d; class Audio3d;
typedef int OrbisUserServiceUserId; using OrbisUserServiceUserId = s32;
typedef unsigned int OrbisAudio3dPortId; using OrbisAudio3dPortId = u32;
typedef unsigned int OrbisAudio3dObjectId; using OrbisAudio3dObjectId = u32;
typedef unsigned int OrbisAudio3dAttributeId; using OrbisAudio3dAttributeId = u32;
enum OrbisAudio3dFormat { enum class OrbisAudio3dFormat {
ORBIS_AUDIO3D_FORMAT_S16 = 0, // s16 S16 = 0,
ORBIS_AUDIO3D_FORMAT_FLOAT = 1 // f32 Float = 1,
}; };
enum OrbisAudio3dRate { ORBIS_AUDIO3D_RATE_48000 = 0 }; enum class OrbisAudio3dRate {
Rate48000 = 0,
enum OrbisAudio3dBufferMode {
ORBIS_AUDIO3D_BUFFER_NO_ADVANCE = 0,
ORBIS_AUDIO3D_BUFFER_ADVANCE_NO_PUSH = 1,
ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH = 2
}; };
enum OrbisAudio3dBlocking { ORBIS_AUDIO3D_BLOCKING_ASYNC = 0, ORBIS_AUDIO3D_BLOCKING_SYNC = 1 }; enum class OrbisAudio3dBufferMode { NoAdvance = 0, AdvanceNoPush = 1, AdvanceAndPush = 2 };
enum OrbisAudio3dPassthrough { enum class OrbisAudio3dBlocking {
ORBIS_AUDIO3D_PASSTHROUGH_NONE = 0, Async = 0,
ORBIS_AUDIO3D_PASSTHROUGH_LEFT = 1, Sync = 1,
ORBIS_AUDIO3D_PASSTHROUGH_RIGHT = 2
}; };
enum OrbisAudio3dOutputRoute { enum class OrbisAudio3dPassthrough {
ORBIS_AUDIO3D_OUTPUT_BOTH = 0, None = 0,
ORBIS_AUDIO3D_OUTPUT_HMU_ONLY = 1, Left = 1,
ORBIS_AUDIO3D_OUTPUT_TV_ONLY = 2 Right = 2,
}; };
enum OrbisAudio3dAmbisonics { enum class OrbisAudio3dOutputRoute {
ORBIS_AUDIO3D_AMBISONICS_NONE = ~0, Both = 0,
ORBIS_AUDIO3D_AMBISONICS_W = 0, HmuOnly = 1,
ORBIS_AUDIO3D_AMBISONICS_X = 1, TvOnly = 2,
ORBIS_AUDIO3D_AMBISONICS_Y = 2, };
ORBIS_AUDIO3D_AMBISONICS_Z = 3,
ORBIS_AUDIO3D_AMBISONICS_R = 4, enum class OrbisAudio3dAmbisonics : u32 {
ORBIS_AUDIO3D_AMBISONICS_S = 5, None = ~0U,
ORBIS_AUDIO3D_AMBISONICS_T = 6, W = 0,
ORBIS_AUDIO3D_AMBISONICS_U = 7, X = 1,
ORBIS_AUDIO3D_AMBISONICS_V = 8, Y = 2,
ORBIS_AUDIO3D_AMBISONICS_K = 9, Z = 3,
ORBIS_AUDIO3D_AMBISONICS_L = 10, R = 4,
ORBIS_AUDIO3D_AMBISONICS_M = 11, S = 5,
ORBIS_AUDIO3D_AMBISONICS_N = 12, T = 6,
ORBIS_AUDIO3D_AMBISONICS_O = 13, U = 7,
ORBIS_AUDIO3D_AMBISONICS_P = 14, V = 8,
ORBIS_AUDIO3D_AMBISONICS_Q = 15 K = 9,
L = 10,
M = 11,
N = 12,
O = 13,
P = 14,
Q = 15
}; };
static const OrbisAudio3dAttributeId s_sceAudio3dAttributePcm = 0x00000001; static const OrbisAudio3dAttributeId s_sceAudio3dAttributePcm = 0x00000001;
@ -86,21 +87,21 @@ struct OrbisAudio3dSpeakerArray;
using OrbisAudio3dSpeakerArrayHandle = OrbisAudio3dSpeakerArray*; // head using OrbisAudio3dSpeakerArrayHandle = OrbisAudio3dSpeakerArray*; // head
struct OrbisAudio3dOpenParameters { struct OrbisAudio3dOpenParameters {
size_t szSizeThis; size_t size_this;
unsigned int uiGranularity; u32 granularity;
OrbisAudio3dRate eRate; OrbisAudio3dRate rate;
unsigned int uiMaxObjects; u32 max_objects;
unsigned int uiQueueDepth; u32 queue_depth;
OrbisAudio3dBufferMode eBufferMode; OrbisAudio3dBufferMode buffer_mode;
char padding[32]; char padding[32];
unsigned int uiNumBeds; u32 num_beds;
}; };
struct OrbisAudio3dAttribute { struct OrbisAudio3dAttribute {
OrbisAudio3dAttributeId uiAttributeId; OrbisAudio3dAttributeId attribute_id;
char padding[32]; char padding[32];
const void* pValue; const void* p_value;
size_t szValue; size_t value;
}; };
struct OrbisAudio3dPosition { struct OrbisAudio3dPosition {
@ -110,22 +111,22 @@ struct OrbisAudio3dPosition {
}; };
struct OrbisAudio3dPcm { struct OrbisAudio3dPcm {
OrbisAudio3dFormat eFormat; OrbisAudio3dFormat format;
const void* pSampleBuffer; const void* sample_buffer;
unsigned int uiNumSamples; u32 num_samples;
}; };
struct OrbisAudio3dSpeakerArrayParameters { struct OrbisAudio3dSpeakerArrayParameters {
OrbisAudio3dPosition* pSpeakerPosition; OrbisAudio3dPosition* speaker_position;
unsigned int uiNumSpeakers; u32 num_speakers;
bool bIs3d; bool is_3d;
void* pBuffer; void* buffer;
size_t szSize; size_t size;
}; };
struct OrbisAudio3dApplicationSpecific { struct OrbisAudio3dApplicationSpecific {
size_t szSizeThis; size_t size_this;
u8 cApplicationSpecific[32]; u8 application_specific[32];
}; };
void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* sParameters); void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* sParameters);

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