mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-04 08:22:32 +00:00
Merge remote-tracking branch 'origin/ajm' into ajm
This commit is contained in:
commit
16ce85b646
2
.github/linux-appimage-qt.sh
vendored
2
.github/linux-appimage-qt.sh
vendored
@ -9,6 +9,8 @@ fi
|
||||
|
||||
export Qt6_DIR="/usr/lib/qt6"
|
||||
export PATH="$Qt6_DIR/bin:$PATH"
|
||||
export EXTRA_QT_PLUGINS="waylandcompositor"
|
||||
export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"
|
||||
|
||||
# Prepare Tools for building the AppImage
|
||||
wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
|
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -287,7 +287,7 @@ jobs:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev
|
||||
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
@ -343,7 +343,7 @@ jobs:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev
|
||||
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
@ -368,7 +368,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 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel3
|
||||
|
||||
- name: Run AppImage packaging script
|
||||
run: ./.github/linux-appimage-qt.sh
|
||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -98,3 +98,7 @@
|
||||
[submodule "externals/LibAtrac9"]
|
||||
path = externals/LibAtrac9
|
||||
url = https://github.com/Vita3K/LibAtrac9
|
||||
[submodule "externals/discord-rpc"]
|
||||
path = externals/discord-rpc
|
||||
url = https://github.com/shadps4-emu/ext-discord-rpc.git
|
||||
shallow = true
|
@ -30,7 +30,7 @@ if(UNIX AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF)
|
||||
|
||||
option(ENABLE_DISCORD_RPC "Enable the Discord RPC integration" ON)
|
||||
# First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR.
|
||||
if (APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
set(BASE_ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
|
||||
@ -47,9 +47,15 @@ else()
|
||||
message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}")
|
||||
endif()
|
||||
|
||||
if (APPLE AND ARCHITECTURE STREQUAL "x86_64")
|
||||
if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
# Exclude ARM homebrew path to avoid conflicts when cross compiling.
|
||||
list(APPEND CMAKE_IGNORE_PREFIX_PATH "/opt/homebrew")
|
||||
|
||||
# Need to reconfigure pkg-config to use the right architecture library paths.
|
||||
# It's not ideal to override these but otherwise the build breaks just by having pkg-config installed.
|
||||
set(ENV{PKG_CONFIG_DIR} "")
|
||||
set(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig:${CMAKE_SYSROOT}/usr/local/lib/pkgconfig:${CMAKE_SYSROOT}/usr/local/share/pkgconfig")
|
||||
set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
|
||||
endif()
|
||||
|
||||
# This function should be passed a list of all files in a target. It will automatically generate file groups
|
||||
@ -103,6 +109,7 @@ find_package(Boost 1.84.0 CONFIG)
|
||||
find_package(FFmpeg 5.1.2 MODULE)
|
||||
find_package(fmt 10.2.0 CONFIG)
|
||||
find_package(glslang 14.2.0 CONFIG)
|
||||
find_package(half 1.12.0 MODULE)
|
||||
find_package(magic_enum 0.9.6 CONFIG)
|
||||
find_package(RenderDoc 1.6.0 MODULE)
|
||||
find_package(SDL3 3.1.2 CONFIG)
|
||||
@ -286,6 +293,14 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
|
||||
src/core/libraries/audio3d/audio3d_error.h
|
||||
src/core/libraries/audio3d/audio3d_impl.cpp
|
||||
src/core/libraries/audio3d/audio3d_impl.h
|
||||
src/core/libraries/ime/ime.cpp
|
||||
src/core/libraries/ime/ime.h
|
||||
src/core/libraries/game_live_streaming/gamelivestreaming.cpp
|
||||
src/core/libraries/game_live_streaming/gamelivestreaming.h
|
||||
src/core/libraries/remote_play/remoteplay.cpp
|
||||
src/core/libraries/remote_play/remoteplay.h
|
||||
src/core/libraries/share_play/shareplay.cpp
|
||||
src/core/libraries/share_play/shareplay.h
|
||||
)
|
||||
|
||||
set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
|
||||
@ -301,6 +316,8 @@ set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp
|
||||
|
||||
set(DIALOGS_LIB src/core/libraries/dialogs/error_dialog.cpp
|
||||
src/core/libraries/dialogs/error_dialog.h
|
||||
src/core/libraries/dialogs/ime_dialog_ui.cpp
|
||||
src/core/libraries/dialogs/ime_dialog_ui.h
|
||||
src/core/libraries/dialogs/ime_dialog.cpp
|
||||
src/core/libraries/dialogs/ime_dialog.h
|
||||
src/core/libraries/dialogs/error_codes.h
|
||||
@ -328,6 +345,10 @@ set(USBD_LIB src/core/libraries/usbd/usbd.cpp
|
||||
src/core/libraries/usbd/usbd.h
|
||||
)
|
||||
|
||||
set(FIBER_LIB src/core/libraries/fiber/fiber.cpp
|
||||
src/core/libraries/fiber/fiber.h
|
||||
)
|
||||
|
||||
set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
|
||||
src/core/libraries/np_manager/np_manager.h
|
||||
src/core/libraries/np_score/np_score.cpp
|
||||
@ -344,15 +365,25 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
|
||||
|
||||
set(DEV_TOOLS src/core/devtools/layer.cpp
|
||||
src/core/devtools/layer.h
|
||||
src/core/devtools/options.cpp
|
||||
src/core/devtools/options.h
|
||||
src/core/devtools/gcn/gcn_context_regs.cpp
|
||||
src/core/devtools/gcn/gcn_op_names.cpp
|
||||
src/core/devtools/gcn/gcn_shader_regs.cpp
|
||||
src/core/devtools/widget/cmd_list.cpp
|
||||
src/core/devtools/widget/cmd_list.h
|
||||
src/core/devtools/widget/common.h
|
||||
src/core/devtools/widget/frame_dump.cpp
|
||||
src/core/devtools/widget/frame_dump.h
|
||||
src/core/devtools/widget/frame_graph.cpp
|
||||
src/core/devtools/widget/frame_graph.h
|
||||
src/core/devtools/widget/imgui_memory_editor.h
|
||||
src/core/devtools/widget/reg_popup.cpp
|
||||
src/core/devtools/widget/reg_popup.h
|
||||
src/core/devtools/widget/reg_view.cpp
|
||||
src/core/devtools/widget/reg_view.h
|
||||
src/core/devtools/widget/text_editor.cpp
|
||||
src/core/devtools/widget/text_editor.h
|
||||
)
|
||||
|
||||
set(COMMON src/common/logging/backend.cpp
|
||||
@ -410,12 +441,18 @@ set(COMMON src/common/logging/backend.cpp
|
||||
src/common/version.h
|
||||
src/common/ntapi.h
|
||||
src/common/ntapi.cpp
|
||||
src/common/number_utils.h
|
||||
src/common/number_utils.cpp
|
||||
src/common/memory_patcher.h
|
||||
src/common/memory_patcher.cpp
|
||||
src/common/scm_rev.cpp
|
||||
src/common/scm_rev.h
|
||||
)
|
||||
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
list(APPEND COMMON src/common/discord_rpc_handler.cpp src/common/discord_rpc_handler.h)
|
||||
endif()
|
||||
|
||||
set(CORE src/core/aerolib/stubs.cpp
|
||||
src/core/aerolib/stubs.h
|
||||
src/core/aerolib/aerolib.cpp
|
||||
@ -466,6 +503,7 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||
${USBD_LIB}
|
||||
${MISC_LIBS}
|
||||
${DIALOGS_LIB}
|
||||
${FIBER_LIB}
|
||||
${DEV_TOOLS}
|
||||
src/core/debug_state.cpp
|
||||
src/core/debug_state.h
|
||||
@ -695,6 +733,8 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
|
||||
src/qt_gui/game_grid_frame.h
|
||||
src/qt_gui/game_install_dialog.cpp
|
||||
src/qt_gui/game_install_dialog.h
|
||||
src/qt_gui/install_dir_select.cpp
|
||||
src/qt_gui/install_dir_select.h
|
||||
src/qt_gui/pkg_viewer.cpp
|
||||
src/qt_gui/pkg_viewer.h
|
||||
src/qt_gui/trophy_viewer.cpp
|
||||
@ -746,7 +786,7 @@ endif()
|
||||
|
||||
create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui LibAtrac9 gcn)
|
||||
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 LibAtrac9 gcn half::half)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml)
|
||||
|
||||
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
||||
@ -769,9 +809,6 @@ if (APPLE)
|
||||
|
||||
# Replacement for std::chrono::time_zone
|
||||
target_link_libraries(shadps4 PRIVATE date::date-tz)
|
||||
|
||||
# Half float conversions for F16C patches
|
||||
target_link_libraries(shadps4 PRIVATE half)
|
||||
endif()
|
||||
|
||||
if (NOT ENABLE_QT_GUI)
|
||||
@ -862,3 +899,16 @@ if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Discord RPC
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
target_link_libraries(shadps4 PRIVATE discord-rpc)
|
||||
endif()
|
||||
|
||||
# Install rules
|
||||
install(TARGETS shadps4 BUNDLE DESTINATION .)
|
||||
|
||||
if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
install(FILES ".github/shadps4.desktop" DESTINATION "share/applications")
|
||||
install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps")
|
||||
endif()
|
||||
|
19
README.md
19
README.md
@ -36,14 +36,10 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
**shadPS4** is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++.
|
||||
|
||||
If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/Quickstart.md).
|
||||
|
||||
To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility).
|
||||
|
||||
To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).
|
||||
|
||||
To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/).
|
||||
|
||||
If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/Quickstart.md).\
|
||||
To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-emu/shadps4-game-compatibility).\
|
||||
To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).\
|
||||
To get the latest news, go to our [**X (Twitter)**](https://x.com/shadps4) or our [**website**](https://shadps4.net/).\
|
||||
For those who'd like to donate to the project, we now have a [**Kofi page**](https://ko-fi.com/shadps4)!
|
||||
|
||||
# Status
|
||||
@ -51,7 +47,7 @@ For those who'd like to donate to the project, we now have a [**Kofi page**](htt
|
||||
> [!IMPORTANT]
|
||||
> shadPS4 is early in development, don't expect a flawless experience.
|
||||
|
||||
Currently, the emulator successfully runs small games like [**Sonic Mania**](https://www.youtube.com/watch?v=AAHoNzhHyCU), [**Undertale**](https://youtu.be/5zIvdy65Ro4) and it can even run [**Bloodborne**](https://www.youtube.com/watch?v=wC6s0avpQRE).
|
||||
Currently, the emulator can successfully run games like [**Bloodborne**](https://www.youtube.com/watch?v=wC6s0avpQRE), [**Dark Souls Remastered**](https://www.youtube.com/watch?v=-3PA-Xwszts), [**Red Dead Redemption**](https://www.youtube.com/watch?v=Al7yz_5nLag) and many other games.
|
||||
|
||||
# Why
|
||||
|
||||
@ -102,7 +98,7 @@ PAD DOWN | DOWN |
|
||||
PAD LEFT | LEFT |
|
||||
PAD RIGHT | RIGHT |
|
||||
OPTIONS | RETURN |
|
||||
TOUCH PAD | SPACE |
|
||||
BACK BUTTON / TOUCH PAD | SPACE |
|
||||
L1 | Q |
|
||||
R1 | U |
|
||||
L2 | E |
|
||||
@ -123,8 +119,7 @@ Logo is done by [**Xphalnos**](https://github.com/Xphalnos)
|
||||
|
||||
# Contributing
|
||||
|
||||
If you want to contribute, please look the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.
|
||||
|
||||
If you want to contribute, please look the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.\
|
||||
Open a PR and we'll check it :)
|
||||
|
||||
# Contributors
|
||||
|
28
cmake/Findhalf.cmake
Normal file
28
cmake/Findhalf.cmake
Normal file
@ -0,0 +1,28 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
find_path(half_INCLUDE_DIR NAMES half.hpp PATH_SUFFIXES half)
|
||||
|
||||
if (half_INCLUDE_DIR)
|
||||
file(STRINGS "${half_INCLUDE_DIR}/half.hpp" _ver_line
|
||||
REGEX "^// Version [0-9.]+$"
|
||||
LIMIT_COUNT 1
|
||||
)
|
||||
string(REGEX MATCH "[0-9.]+" half_VERSION "${_ver_line}")
|
||||
unset(_ver_line)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(half
|
||||
REQUIRED_VARS half_INCLUDE_DIR
|
||||
VERSION_VAR half_VERSION
|
||||
)
|
||||
|
||||
if (half_FOUND AND NOT TARGET half::half)
|
||||
add_library(half::half INTERFACE IMPORTED)
|
||||
set_target_properties(half::half PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${half_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(half_INCLUDE_DIR)
|
@ -7,21 +7,22 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
## Summary
|
||||
|
||||
- [PC Requirements](#pc-requirements)
|
||||
- [CPU](#cpu)
|
||||
- [GPU](#gpu)
|
||||
- [RAM](#ram)
|
||||
- [OS](#os)
|
||||
- [Have the latest WIP version](#have-the-latest-wip-version)
|
||||
- [Install PKG files (Games and Updates)](#install-pkg-files)
|
||||
- [Configure the emulator](#configure-the-emulator)
|
||||
- [**PC Requirements**](#minimum-pc-requirements)
|
||||
- [**CPU**](#cpu)
|
||||
- [**GPU**](#gpu)
|
||||
- [**RAM**](#ram)
|
||||
- [**OS**](#os)
|
||||
- [**Have the latest WIP version**](#how-to-run-the-latest-work-in-progress-builds-of-shadps4)
|
||||
- [**Install PKG files (Games and Updates)**](#install-pkg-files)
|
||||
- [**Configure the emulator**](#configure-the-emulator)
|
||||
|
||||
## PC Requirements
|
||||
## Minimum PC requirements
|
||||
|
||||
### CPU
|
||||
|
||||
- A processor with at least 4 cores and 6 threads
|
||||
- Above 2.5 GHz frequency
|
||||
- required support AVX2 extension or Rosetta 2 on ARM
|
||||
|
||||
### GPU
|
||||
|
||||
@ -37,41 +38,25 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
- Windows 10 or Ubuntu 22.04
|
||||
|
||||
## How to run the latest Work-in-Progress builds of ShadPS4
|
||||
## How to run the latest Work-in-Progress builds of shadPS4
|
||||
|
||||
1. Go to <https://github.com/shadps4-emu/shadPS4/releases> In the release identified as 'pre-release' click on the down arrow(Assets), select your operating system of choice (the "**qt**" versions have a user interface, which is probably the one you want. The others are SDL versions, which can only be run via command line).
|
||||

|
||||
|
||||
2. Once downloaded, extract to its own folder, and run ShadPS4's executable from the extracted folder.
|
||||
2. Once downloaded, extract to its own folder, and run shadPS4's executable from the extracted folder.
|
||||
|
||||
3. Upon first launch, ShadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that ShadPS4 can use to install your PKG files to.
|
||||
3. Upon first launch, shadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that shadPS4 can use to install your PKG files to.
|
||||
|
||||
## Install PKG files
|
||||
|
||||
To install PKG files (game and updates), you will need the Qt application (with UI). You will have to go to "File" then to "Install Packages (PKG)", a window will open then you will have to select the files. You can install multiple PKG files at once. Once finished, the game should appear in the application.
|
||||
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/2.png" width="800"></a>
|
||||
<img src="https://github.com/shadps4-emu/shadPS4/blob/main/documents/Quickstart/2.png" width="800">
|
||||
|
||||
## Configure the emulator
|
||||
|
||||
You can configure the emulator by editing the `config.toml` file found in the `user` folder created after starting the application.\
|
||||
Some settings may be related to more technical development and debugging. For more information on those, see [Debugging](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration).
|
||||
To configure the emulator, you can go through the interface and go to "settings".
|
||||
|
||||
Here's a list of configuration entries that are worth changing:
|
||||
|
||||
- `[General]`
|
||||
|
||||
- `Fullscreen`: Display the game in a full screen borderless window.
|
||||
|
||||
- `logType`: Configures logging synchronization (`sync`/`async`)
|
||||
- It can be beneficial to set this to `sync` in order for the log to accurately maintain message order, at the cost of performance.
|
||||
- Use when sending logs to developers. See more about [reporting issues](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#reporting-and-communicating-about-issues).
|
||||
- `logFilter`: Sets the logging category for various logging classes.
|
||||
- Format: `<class>:<level> ...`, `<class.*>:<level> <*:level> ...`
|
||||
- Valid log levels: `Trace, Debug, Info, Warning, Error, Critical` - in this order, setting a level silences all levels preceding it and logs every level after it.
|
||||
- Examples:
|
||||
- If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages.
|
||||
- If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Error Render.Vulkan:Info`
|
||||
|
||||
- `[GPU]`
|
||||
- `screenWidth` and `screenHeight`: Configures the game window width and height.
|
||||
You can also configure the emulator by editing the `config.toml` file located in the `user` folder created after the application is started (Mostly useful if you are using the SDL version).
|
||||
Some settings may be related to more technical development and debugging.\
|
||||
For more information on this, see [**Debugging**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration).
|
@ -6,7 +6,7 @@ v0.3.0 23/09/2024 - codename broamic
|
||||
- New translations support (26 languages)
|
||||
- Support for unlocking trophies
|
||||
- Support for more controllers (Dualshock and Xbox)
|
||||
- Many GUI imporovements
|
||||
- Many GUI improvements
|
||||
- AVplayer
|
||||
|
||||
v0.2.0 15/08/2024 - codename validptr
|
||||
|
28
externals/CMakeLists.txt
vendored
28
externals/CMakeLists.txt
vendored
@ -3,7 +3,10 @@
|
||||
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
set(BUILD_TESTING OFF)
|
||||
set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL ON)
|
||||
set_directory_properties(PROPERTIES
|
||||
EXCLUDE_FROM_ALL ON
|
||||
SYSTEM ON
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
# Silence "deprecation" warnings
|
||||
@ -13,11 +16,8 @@ endif()
|
||||
# Boost
|
||||
if (NOT TARGET Boost::headers)
|
||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "")
|
||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "")
|
||||
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
||||
add_library(boost INTERFACE)
|
||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
||||
add_library(Boost::headers ALIAS boost)
|
||||
add_subdirectory(ext-boost)
|
||||
endif()
|
||||
|
||||
# fmtlib
|
||||
@ -54,7 +54,7 @@ file(GLOB LIBATRAC9_SOURCES
|
||||
)
|
||||
|
||||
add_library(LibAtrac9 STATIC ${LIBATRAC9_SOURCES})
|
||||
target_include_directories(LibAtrac9 PUBLIC LibAtrac9/C/src)
|
||||
target_include_directories(LibAtrac9 INTERFACE LibAtrac9/C/src)
|
||||
|
||||
# Zlib-Ng
|
||||
if (NOT TARGET zlib-ng::zlib)
|
||||
@ -86,7 +86,7 @@ endif()
|
||||
# RenderDoc
|
||||
if (NOT TARGET RenderDoc::API)
|
||||
add_library(renderdoc INTERFACE)
|
||||
target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc)
|
||||
target_include_directories(renderdoc INTERFACE ./renderdoc)
|
||||
add_library(RenderDoc::API ALIAS renderdoc)
|
||||
endif()
|
||||
|
||||
@ -150,11 +150,14 @@ if (WIN32)
|
||||
target_compile_options(sirit PUBLIC "-Wno-error=unused-command-line-argument")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
# half
|
||||
# half
|
||||
if (NOT TARGET half::half)
|
||||
add_library(half INTERFACE)
|
||||
target_include_directories(half INTERFACE half/include)
|
||||
add_library(half::half ALIAS half)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
# date
|
||||
if (NOT TARGET date::date-tz)
|
||||
option(BUILD_TZ_LIB "" ON)
|
||||
@ -193,5 +196,12 @@ if (NOT TARGET pugixml::pugixml)
|
||||
add_subdirectory(pugixml)
|
||||
endif()
|
||||
|
||||
# Discord RPC
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
set(BUILD_EXAMPLES OFF)
|
||||
add_subdirectory(discord-rpc/)
|
||||
target_include_directories(discord-rpc INTERFACE discord-rpc/include)
|
||||
endif()
|
||||
|
||||
# GCN Headers
|
||||
add_subdirectory(gcn)
|
1
externals/discord-rpc
vendored
Submodule
1
externals/discord-rpc
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 4ec218155d73bcb8022f8f7ca72305d801f84beb
|
18
src/.vscode/c_cpp_properties.json
vendored
18
src/.vscode/c_cpp_properties.json
vendored
@ -1,18 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "linux-gcc-x64",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "${default}",
|
||||
"cppStandard": "${default}",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"compilerArgs": [
|
||||
""
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
24
src/.vscode/launch.json
vendored
24
src/.vscode/launch.json
vendored
@ -1,24 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "C/C++ Runner: Debug Session",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": false,
|
||||
"cwd": "/home/turtle/Desktop/shadPS4/src",
|
||||
"program": "/home/turtle/Desktop/shadPS4/src/build/Debug/outDebug",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
59
src/.vscode/settings.json
vendored
59
src/.vscode/settings.json
vendored
@ -1,59 +0,0 @@
|
||||
{
|
||||
"C_Cpp_Runner.cCompilerPath": "gcc",
|
||||
"C_Cpp_Runner.cppCompilerPath": "g++",
|
||||
"C_Cpp_Runner.debuggerPath": "gdb",
|
||||
"C_Cpp_Runner.cStandard": "",
|
||||
"C_Cpp_Runner.cppStandard": "",
|
||||
"C_Cpp_Runner.msvcBatchPath": "",
|
||||
"C_Cpp_Runner.useMsvc": false,
|
||||
"C_Cpp_Runner.warnings": [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wpedantic",
|
||||
"-Wshadow",
|
||||
"-Wformat=2",
|
||||
"-Wcast-align",
|
||||
"-Wconversion",
|
||||
"-Wsign-conversion",
|
||||
"-Wnull-dereference"
|
||||
],
|
||||
"C_Cpp_Runner.msvcWarnings": [
|
||||
"/W4",
|
||||
"/permissive-",
|
||||
"/w14242",
|
||||
"/w14287",
|
||||
"/w14296",
|
||||
"/w14311",
|
||||
"/w14826",
|
||||
"/w44062",
|
||||
"/w44242",
|
||||
"/w14905",
|
||||
"/w14906",
|
||||
"/w14263",
|
||||
"/w44265",
|
||||
"/w14928"
|
||||
],
|
||||
"C_Cpp_Runner.enableWarnings": true,
|
||||
"C_Cpp_Runner.warningsAsError": false,
|
||||
"C_Cpp_Runner.compilerArgs": [],
|
||||
"C_Cpp_Runner.linkerArgs": [],
|
||||
"C_Cpp_Runner.includePaths": [],
|
||||
"C_Cpp_Runner.includeSearch": [
|
||||
"*",
|
||||
"**/*"
|
||||
],
|
||||
"C_Cpp_Runner.excludeSearch": [
|
||||
"**/build",
|
||||
"**/build/**",
|
||||
"**/.*",
|
||||
"**/.*/**",
|
||||
"**/.vscode",
|
||||
"**/.vscode/**"
|
||||
],
|
||||
"C_Cpp_Runner.useAddressSanitizer": false,
|
||||
"C_Cpp_Runner.useUndefinedSanitizer": false,
|
||||
"C_Cpp_Runner.useLeakSanitizer": false,
|
||||
"C_Cpp_Runner.showCompilationTime": false,
|
||||
"C_Cpp_Runner.useLinkTimeOptimization": false,
|
||||
"C_Cpp_Runner.msvcSecureNoWarnings": false
|
||||
}
|
@ -103,7 +103,7 @@ s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) {
|
||||
|
||||
const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
|
||||
|
||||
SDL_bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size);
|
||||
bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size);
|
||||
|
||||
lock.unlock(); // Unlock only after necessary operations
|
||||
|
||||
|
@ -81,7 +81,9 @@
|
||||
#pragma pack(1)
|
||||
template <std::size_t Position, std::size_t Bits, typename T>
|
||||
struct BitField {
|
||||
private:
|
||||
|
||||
using Type = T;
|
||||
|
||||
// UnderlyingType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
// former case to workaround compile errors which arise when using
|
||||
@ -92,7 +94,6 @@ private:
|
||||
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
|
||||
using StorageType = std::make_unsigned_t<UnderlyingType>;
|
||||
|
||||
public:
|
||||
/// Constants to allow limited introspection of fields if needed
|
||||
static constexpr std::size_t position = Position;
|
||||
static constexpr std::size_t bits = Bits;
|
||||
|
@ -34,6 +34,7 @@ static bool isNeo = false;
|
||||
static bool isFullscreen = false;
|
||||
static bool playBGM = false;
|
||||
static int BGMvolume = 50;
|
||||
static bool enableDiscordRPC = false;
|
||||
static u32 screenWidth = 1280;
|
||||
static u32 screenHeight = 720;
|
||||
static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
|
||||
@ -41,6 +42,7 @@ static std::string logFilter;
|
||||
static std::string logType = "async";
|
||||
static std::string userName = "shadPS4";
|
||||
static std::string updateChannel;
|
||||
static std::string backButtonBehavior = "left";
|
||||
static bool useSpecialPad = false;
|
||||
static int specialPadClass = 1;
|
||||
static bool isDebugDump = false;
|
||||
@ -56,9 +58,12 @@ static bool vkValidationGpu = false;
|
||||
static bool rdocEnable = false;
|
||||
static bool vkMarkers = false;
|
||||
static bool vkCrashDiagnostic = false;
|
||||
static s16 cursorState = HideCursorState::Idle;
|
||||
static int cursorHideTimeout = 5; // 5 seconds (default)
|
||||
static bool separateupdatefolder = false;
|
||||
|
||||
// Gui
|
||||
std::filesystem::path settings_install_dir = {};
|
||||
std::vector<std::filesystem::path> settings_install_dirs = {};
|
||||
std::filesystem::path settings_addon_install_dir = {};
|
||||
u32 main_window_geometry_x = 400;
|
||||
u32 main_window_geometry_y = 400;
|
||||
@ -95,6 +100,18 @@ int getBGMvolume() {
|
||||
return BGMvolume;
|
||||
}
|
||||
|
||||
bool getEnableDiscordRPC() {
|
||||
return enableDiscordRPC;
|
||||
}
|
||||
|
||||
s16 getCursorState() {
|
||||
return cursorState;
|
||||
}
|
||||
|
||||
int getCursorHideTimeout() {
|
||||
return cursorHideTimeout;
|
||||
}
|
||||
|
||||
u32 getScreenWidth() {
|
||||
return screenWidth;
|
||||
}
|
||||
@ -123,6 +140,10 @@ std::string getUpdateChannel() {
|
||||
return updateChannel;
|
||||
}
|
||||
|
||||
std::string getBackButtonBehavior() {
|
||||
return backButtonBehavior;
|
||||
}
|
||||
|
||||
bool getUseSpecialPad() {
|
||||
return useSpecialPad;
|
||||
}
|
||||
@ -187,6 +208,10 @@ bool vkCrashDiagnosticEnabled() {
|
||||
return vkCrashDiagnostic;
|
||||
}
|
||||
|
||||
bool getSeparateUpdateEnabled() {
|
||||
return separateupdatefolder;
|
||||
}
|
||||
|
||||
void setGpuId(s32 selectedGpuId) {
|
||||
gpuId = selectedGpuId;
|
||||
}
|
||||
@ -251,6 +276,18 @@ void setBGMvolume(int volume) {
|
||||
BGMvolume = volume;
|
||||
}
|
||||
|
||||
void setEnableDiscordRPC(bool enable) {
|
||||
enableDiscordRPC = enable;
|
||||
}
|
||||
|
||||
void setCursorState(s16 newCursorState) {
|
||||
cursorState = newCursorState;
|
||||
}
|
||||
|
||||
void setCursorHideTimeout(int newcursorHideTimeout) {
|
||||
cursorHideTimeout = newcursorHideTimeout;
|
||||
}
|
||||
|
||||
void setLanguage(u32 language) {
|
||||
m_language = language;
|
||||
}
|
||||
@ -275,6 +312,10 @@ void setUpdateChannel(const std::string& type) {
|
||||
updateChannel = type;
|
||||
}
|
||||
|
||||
void setBackButtonBehavior(const std::string& type) {
|
||||
backButtonBehavior = type;
|
||||
}
|
||||
|
||||
void setUseSpecialPad(bool use) {
|
||||
useSpecialPad = use;
|
||||
}
|
||||
@ -283,14 +324,29 @@ void setSpecialPadClass(int type) {
|
||||
specialPadClass = type;
|
||||
}
|
||||
|
||||
void setSeparateUpdateEnabled(bool use) {
|
||||
separateupdatefolder = use;
|
||||
}
|
||||
|
||||
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
|
||||
main_window_geometry_x = x;
|
||||
main_window_geometry_y = y;
|
||||
main_window_geometry_w = w;
|
||||
main_window_geometry_h = h;
|
||||
}
|
||||
void setGameInstallDir(const std::filesystem::path& dir) {
|
||||
settings_install_dir = dir;
|
||||
bool addGameInstallDir(const std::filesystem::path& dir) {
|
||||
if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) ==
|
||||
settings_install_dirs.end()) {
|
||||
settings_install_dirs.push_back(dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void removeGameInstallDir(const std::filesystem::path& dir) {
|
||||
auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir);
|
||||
if (iterator != settings_install_dirs.end()) {
|
||||
settings_install_dirs.erase(iterator);
|
||||
}
|
||||
}
|
||||
void setAddonInstallDir(const std::filesystem::path& dir) {
|
||||
settings_addon_install_dir = dir;
|
||||
@ -348,8 +404,8 @@ u32 getMainWindowGeometryW() {
|
||||
u32 getMainWindowGeometryH() {
|
||||
return main_window_geometry_h;
|
||||
}
|
||||
std::filesystem::path getGameInstallDir() {
|
||||
return settings_install_dir;
|
||||
const std::vector<std::filesystem::path>& getGameInstallDirs() {
|
||||
return settings_install_dirs;
|
||||
}
|
||||
std::filesystem::path getAddonInstallDir() {
|
||||
if (settings_addon_install_dir.empty()) {
|
||||
@ -425,6 +481,7 @@ void load(const std::filesystem::path& path) {
|
||||
isFullscreen = toml::find_or<bool>(general, "Fullscreen", false);
|
||||
playBGM = toml::find_or<bool>(general, "playBGM", false);
|
||||
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
|
||||
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
|
||||
logFilter = toml::find_or<std::string>(general, "logFilter", "");
|
||||
logType = toml::find_or<std::string>(general, "logType", "sync");
|
||||
userName = toml::find_or<std::string>(general, "userName", "shadPS4");
|
||||
@ -435,11 +492,15 @@ void load(const std::filesystem::path& path) {
|
||||
}
|
||||
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
|
||||
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
|
||||
separateupdatefolder = toml::find_or<bool>(general, "separateUpdateEnabled", false);
|
||||
}
|
||||
|
||||
if (data.contains("Input")) {
|
||||
const toml::value& input = data.at("Input");
|
||||
|
||||
cursorState = toml::find_or<int>(input, "cursorState", HideCursorState::Idle);
|
||||
cursorHideTimeout = toml::find_or<int>(input, "cursorHideTimeout", 5);
|
||||
backButtonBehavior = toml::find_or<std::string>(input, "backButtonBehavior", "left");
|
||||
useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", false);
|
||||
specialPadClass = toml::find_or<int>(input, "specialPadClass", 1);
|
||||
}
|
||||
@ -483,7 +544,19 @@ void load(const std::filesystem::path& path) {
|
||||
mw_themes = toml::find_or<int>(gui, "theme", 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);
|
||||
settings_install_dir = toml::find_fs_path_or(gui, "installDir", {});
|
||||
|
||||
// TODO Migration code, after a major release this should be removed.
|
||||
auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {});
|
||||
if (!old_game_install_dir.empty()) {
|
||||
addGameInstallDir(std::filesystem::path{old_game_install_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", {});
|
||||
main_window_geometry_x = toml::find_or<int>(gui, "geometry_x", 0);
|
||||
main_window_geometry_y = toml::find_or<int>(gui, "geometry_y", 0);
|
||||
@ -527,12 +600,17 @@ void save(const std::filesystem::path& path) {
|
||||
data["General"]["Fullscreen"] = isFullscreen;
|
||||
data["General"]["playBGM"] = playBGM;
|
||||
data["General"]["BGMvolume"] = BGMvolume;
|
||||
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
|
||||
data["General"]["logFilter"] = logFilter;
|
||||
data["General"]["logType"] = logType;
|
||||
data["General"]["userName"] = userName;
|
||||
data["General"]["updateChannel"] = updateChannel;
|
||||
data["General"]["showSplash"] = isShowSplash;
|
||||
data["General"]["autoUpdate"] = isAutoUpdate;
|
||||
data["General"]["separateUpdateEnabled"] = separateupdatefolder;
|
||||
data["Input"]["cursorState"] = cursorState;
|
||||
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
|
||||
data["Input"]["backButtonBehavior"] = backButtonBehavior;
|
||||
data["Input"]["useSpecialPad"] = useSpecialPad;
|
||||
data["Input"]["specialPadClass"] = specialPadClass;
|
||||
data["GPU"]["screenWidth"] = screenWidth;
|
||||
@ -557,7 +635,13 @@ void save(const std::filesystem::path& path) {
|
||||
data["GUI"]["gameTableMode"] = m_table_mode;
|
||||
data["GUI"]["mw_width"] = m_window_size_W;
|
||||
data["GUI"]["mw_height"] = m_window_size_H;
|
||||
data["GUI"]["installDir"] = std::string{fmt::UTF(settings_install_dir.u8string()).data};
|
||||
|
||||
std::vector<std::string> install_dirs;
|
||||
for (const auto& dirString : settings_install_dirs) {
|
||||
install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data});
|
||||
}
|
||||
data["GUI"]["installDirs"] = install_dirs;
|
||||
|
||||
data["GUI"]["addonInstallDir"] =
|
||||
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
|
||||
data["GUI"]["geometry_x"] = main_window_geometry_x;
|
||||
@ -571,6 +655,9 @@ void save(const std::filesystem::path& path) {
|
||||
|
||||
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::out);
|
||||
file << data;
|
||||
file.close();
|
||||
@ -581,6 +668,7 @@ void setDefaultValues() {
|
||||
isFullscreen = false;
|
||||
playBGM = false;
|
||||
BGMvolume = 50;
|
||||
enableDiscordRPC = true;
|
||||
screenWidth = 1280;
|
||||
screenHeight = 720;
|
||||
logFilter = "";
|
||||
@ -591,6 +679,9 @@ void setDefaultValues() {
|
||||
} else {
|
||||
updateChannel = "Nightly";
|
||||
}
|
||||
cursorState = HideCursorState::Idle;
|
||||
cursorHideTimeout = 5;
|
||||
backButtonBehavior = "left";
|
||||
useSpecialPad = false;
|
||||
specialPadClass = 1;
|
||||
isDebugDump = false;
|
||||
|
@ -8,6 +8,9 @@
|
||||
#include "types.h"
|
||||
|
||||
namespace Config {
|
||||
|
||||
enum HideCursorState : s16 { Never, Idle, Always };
|
||||
|
||||
void load(const std::filesystem::path& path);
|
||||
void save(const std::filesystem::path& path);
|
||||
|
||||
@ -15,12 +18,17 @@ bool isNeoMode();
|
||||
bool isFullscreenMode();
|
||||
bool getPlayBGM();
|
||||
int getBGMvolume();
|
||||
bool getEnableDiscordRPC();
|
||||
bool getSeparateUpdateEnabled();
|
||||
|
||||
std::string getLogFilter();
|
||||
std::string getLogType();
|
||||
std::string getUserName();
|
||||
std::string getUpdateChannel();
|
||||
|
||||
s16 getCursorState();
|
||||
int getCursorHideTimeout();
|
||||
std::string getBackButtonBehavior();
|
||||
bool getUseSpecialPad();
|
||||
int getSpecialPadClass();
|
||||
|
||||
@ -50,11 +58,16 @@ void setScreenHeight(u32 height);
|
||||
void setFullscreenMode(bool enable);
|
||||
void setPlayBGM(bool enable);
|
||||
void setBGMvolume(int volume);
|
||||
void setEnableDiscordRPC(bool enable);
|
||||
void setLanguage(u32 language);
|
||||
void setNeoMode(bool enable);
|
||||
void setUserName(const std::string& type);
|
||||
void setUpdateChannel(const std::string& type);
|
||||
void setSeparateUpdateEnabled(bool use);
|
||||
|
||||
void setCursorState(s16 cursorState);
|
||||
void setCursorHideTimeout(int newcursorHideTimeout);
|
||||
void setBackButtonBehavior(const std::string& type);
|
||||
void setUseSpecialPad(bool use);
|
||||
void setSpecialPadClass(int type);
|
||||
|
||||
@ -73,7 +86,8 @@ bool vkCrashDiagnosticEnabled();
|
||||
|
||||
// Gui
|
||||
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);
|
||||
void setGameInstallDir(const std::filesystem::path& dir);
|
||||
bool addGameInstallDir(const std::filesystem::path& dir);
|
||||
void removeGameInstallDir(const std::filesystem::path& dir);
|
||||
void setAddonInstallDir(const std::filesystem::path& dir);
|
||||
void setMainWindowTheme(u32 theme);
|
||||
void setIconSize(u32 size);
|
||||
@ -92,7 +106,7 @@ u32 getMainWindowGeometryX();
|
||||
u32 getMainWindowGeometryY();
|
||||
u32 getMainWindowGeometryW();
|
||||
u32 getMainWindowGeometryH();
|
||||
std::filesystem::path getGameInstallDir();
|
||||
const std::vector<std::filesystem::path>& getGameInstallDirs();
|
||||
std::filesystem::path getAddonInstallDir();
|
||||
u32 getMainWindowTheme();
|
||||
u32 getIconSize();
|
||||
|
@ -81,34 +81,42 @@ public:
|
||||
return std::basic_string_view<T>{data};
|
||||
}
|
||||
|
||||
char* begin() {
|
||||
T* begin() {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
const char* begin() const {
|
||||
const T* begin() const {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
char* end() {
|
||||
T* end() {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data + N;
|
||||
}
|
||||
|
||||
const char* end() const {
|
||||
const T* end() const {
|
||||
if (this == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return data + N;
|
||||
}
|
||||
|
||||
constexpr std::size_t capacity() const {
|
||||
return N;
|
||||
}
|
||||
|
||||
std::size_t size() const {
|
||||
return std::char_traits<T>::length(data);
|
||||
}
|
||||
|
||||
T& operator[](size_t idx) {
|
||||
return data[idx];
|
||||
}
|
||||
@ -152,6 +160,12 @@ public:
|
||||
static_assert(sizeof(CString<13>) == sizeof(char[13])); // Ensure size still matches a simple array
|
||||
static_assert(std::weakly_incrementable<CString<13>::Iterator>);
|
||||
|
||||
template <size_t N>
|
||||
using CWString = CString<N, wchar_t>;
|
||||
|
||||
template <size_t N>
|
||||
using CU16String = CString<N, char16_t>;
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // namespace Common
|
@ -1,43 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include "common/discord.h"
|
||||
|
||||
namespace Discord {
|
||||
|
||||
void RPC::init() {
|
||||
DiscordEventHandlers handlers{};
|
||||
Discord_Initialize("1139939140494971051", &handlers, 1, nullptr);
|
||||
|
||||
startTimestamp = time(nullptr);
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void RPC::update(Discord::RPCStatus status, const std::string& game) {
|
||||
DiscordRichPresence rpc{};
|
||||
|
||||
if (status == Discord::RPCStatus::Playing) {
|
||||
rpc.details = "Playing a game";
|
||||
rpc.state = game.c_str();
|
||||
} else {
|
||||
rpc.details = "Idle";
|
||||
}
|
||||
|
||||
rpc.largeImageKey = "shadps4";
|
||||
rpc.largeImageText = "ShadPS4 is a PS4 emulator";
|
||||
rpc.startTimestamp = startTimestamp;
|
||||
|
||||
Discord_UpdatePresence(&rpc);
|
||||
}
|
||||
|
||||
void RPC::stop() {
|
||||
if (enabled) {
|
||||
enabled = false;
|
||||
Discord_ClearPresence();
|
||||
Discord_Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Discord
|
57
src/common/discord_rpc_handler.cpp
Normal file
57
src/common/discord_rpc_handler.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include "src/common/discord_rpc_handler.h"
|
||||
|
||||
namespace DiscordRPCHandler {
|
||||
|
||||
void RPC::init() {
|
||||
DiscordEventHandlers handlers{};
|
||||
|
||||
Discord_Initialize("1139939140494971051", &handlers, 1, nullptr);
|
||||
startTimestamp = time(nullptr);
|
||||
rpcEnabled = true;
|
||||
}
|
||||
|
||||
void RPC::setStatusIdling() {
|
||||
DiscordRichPresence rpc{};
|
||||
rpc.largeImageKey = "https://github.com/shadps4-emu/shadPS4/raw/main/.github/shadps4.png";
|
||||
rpc.largeImageText = "shadPS4 is a PS4 emulator";
|
||||
rpc.startTimestamp = startTimestamp;
|
||||
rpc.details = "Idle";
|
||||
|
||||
status = RPCStatus::Idling;
|
||||
Discord_UpdatePresence(&rpc);
|
||||
}
|
||||
|
||||
void RPC::setStatusPlaying(const std::string& game_name, const std::string& game_id) {
|
||||
DiscordRichPresence rpc{};
|
||||
|
||||
rpc.details = "Playing";
|
||||
rpc.state = game_name.c_str();
|
||||
std::string largeImageUrl =
|
||||
"https://store.playstation.com/store/api/chihiro/00_09_000/titlecontainer/US/en/999/" +
|
||||
game_id + "_00/image";
|
||||
rpc.largeImageKey = largeImageUrl.c_str();
|
||||
rpc.largeImageText = game_name.c_str();
|
||||
rpc.startTimestamp = startTimestamp;
|
||||
|
||||
status = RPCStatus::Playing;
|
||||
Discord_UpdatePresence(&rpc);
|
||||
}
|
||||
|
||||
void RPC::shutdown() {
|
||||
if (rpcEnabled) {
|
||||
rpcEnabled = false;
|
||||
Discord_ClearPresence();
|
||||
Discord_Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool RPC::getRPCEnabled() {
|
||||
return rpcEnabled;
|
||||
}
|
||||
|
||||
} // namespace DiscordRPCHandler
|
@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
#include <discord_rpc.h>
|
||||
|
||||
namespace Discord {
|
||||
namespace DiscordRPCHandler {
|
||||
|
||||
enum class RPCStatus {
|
||||
Idling,
|
||||
@ -16,12 +16,15 @@ enum class RPCStatus {
|
||||
|
||||
class RPC {
|
||||
std::uint64_t startTimestamp;
|
||||
bool enabled = false;
|
||||
bool rpcEnabled = false;
|
||||
RPCStatus status;
|
||||
|
||||
public:
|
||||
void init();
|
||||
void update(RPCStatus status, const std::string& title);
|
||||
void stop();
|
||||
void setStatusIdling();
|
||||
void setStatusPlaying(const std::string& game_name, const std::string& game_id);
|
||||
void shutdown();
|
||||
bool getRPCEnabled();
|
||||
};
|
||||
|
||||
} // namespace Discord
|
||||
} // namespace DiscordRPCHandler
|
@ -231,7 +231,7 @@ void IOFile::Unlink() {
|
||||
|
||||
// Mark the file for deletion
|
||||
// TODO: Also remove the file path?
|
||||
#if _WIN64
|
||||
#ifdef _WIN64
|
||||
FILE_DISPOSITION_INFORMATION disposition;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
|
||||
@ -242,7 +242,11 @@ void IOFile::Unlink() {
|
||||
NtSetInformationFile(hfile, &iosb, &disposition, sizeof(disposition),
|
||||
FileDispositionInformation);
|
||||
#else
|
||||
UNREACHABLE_MSG("Missing Linux implementation");
|
||||
if (unlink(file_path.c_str()) != 0) {
|
||||
const auto ec = std::error_code{errno, std::generic_category()};
|
||||
LOG_ERROR(Common_Filesystem, "Failed to unlink the file at path={}, ec_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,11 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Lib, AvPlayer) \
|
||||
SUB(Lib, Ngs2) \
|
||||
SUB(Lib, Audio3d) \
|
||||
SUB(Lib, Ime) \
|
||||
SUB(Lib, GameLiveStreaming) \
|
||||
SUB(Lib, Remoteplay) \
|
||||
SUB(Lib, SharePlay) \
|
||||
SUB(Lib, Fiber) \
|
||||
CLS(Frontend) \
|
||||
CLS(Render) \
|
||||
SUB(Render, Vulkan) \
|
||||
|
@ -29,67 +29,72 @@ enum class Level : u8 {
|
||||
* filter.cpp.
|
||||
*/
|
||||
enum class Class : u8 {
|
||||
Log, ///< Messages about the log system itself
|
||||
Common, ///< Library routines
|
||||
Common_Filesystem, ///< Filesystem interface library
|
||||
Common_Memory, ///< Memory mapping and management functions
|
||||
Core, ///< LLE emulation core
|
||||
Core_Linker, ///< The module linker
|
||||
Config, ///< Emulator configuration (including commandline)
|
||||
Debug, ///< Debugging tools
|
||||
Kernel, ///< The HLE implementation of the PS4 kernel.
|
||||
Kernel_Pthread, ///< The pthread implementation of the kernel.
|
||||
Kernel_Fs, ///< The filesystem implementation of the kernel.
|
||||
Kernel_Vmm, ///< The virtual memory implementation of the kernel.
|
||||
Kernel_Event, ///< The event management implementation of the kernel.
|
||||
Kernel_Sce, ///< The sony specific interfaces provided by the kernel.
|
||||
Lib, ///< HLE implementation of system library. Each major library
|
||||
///< should have its own subclass.
|
||||
Lib_LibC, ///< The LibC implementation.
|
||||
Lib_Kernel, ///< The LibKernel implementation.
|
||||
Lib_Pad, ///< The LibScePad implementation.
|
||||
Lib_GnmDriver, ///< The LibSceGnmDriver implementation.
|
||||
Lib_SystemService, ///< The LibSceSystemService implementation.
|
||||
Lib_UserService, ///< The LibSceUserService implementation.
|
||||
Lib_VideoOut, ///< The LibSceVideoOut implementation.
|
||||
Lib_CommonDlg, ///< The LibSceCommonDialog implementation.
|
||||
Lib_MsgDlg, ///< The LibSceMsgDialog implementation.
|
||||
Lib_AudioOut, ///< The LibSceAudioOut implementation.
|
||||
Lib_AudioIn, ///< The LibSceAudioIn implementation.
|
||||
Lib_Net, ///< The LibSceNet implementation.
|
||||
Lib_NetCtl, ///< The LibSecNetCtl implementation.
|
||||
Lib_SaveData, ///< The LibSceSaveData implementation.
|
||||
Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation.
|
||||
Lib_Ssl, ///< The LibSceSsl implementation.
|
||||
Lib_Http, ///< The LibSceHttp implementation.
|
||||
Lib_SysModule, ///< The LibSceSysModule implementation
|
||||
Lib_NpManager, ///< The LibSceNpManager implementation
|
||||
Lib_NpScore, ///< The LibSceNpScore implementation
|
||||
Lib_NpTrophy, ///< The LibSceNpTrophy implementation
|
||||
Lib_Screenshot, ///< The LibSceScreenshot implementation
|
||||
Lib_LibCInternal, ///< The LibCInternal implementation.
|
||||
Lib_AppContent, ///< The LibSceAppContent implementation.
|
||||
Lib_Rtc, ///< The LibSceRtc implementation.
|
||||
Lib_DiscMap, ///< The LibSceDiscMap implementation.
|
||||
Lib_Png, ///< The LibScePng implementation.
|
||||
Lib_PlayGo, ///< The LibScePlayGo implementation.
|
||||
Lib_Random, ///< The libSceRandom implementation.
|
||||
Lib_Usbd, ///< The LibSceUsbd implementation.
|
||||
Lib_Ajm, ///< The LibSceAjm implementation.
|
||||
Lib_ErrorDialog, ///< The LibSceErrorDialog implementation.
|
||||
Lib_ImeDialog, ///< The LibSceImeDialog implementation.
|
||||
Lib_AvPlayer, ///< The LibSceAvPlayer implementation.
|
||||
Lib_Ngs2, ///< The LibSceNgs2 implementation.
|
||||
Lib_Audio3d, ///< The LibSceAudio3d implementation.
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Video Core
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
Render_Recompiler, ///< Shader recompiler
|
||||
ImGui, ///< ImGui
|
||||
Loader, ///< ROM loader
|
||||
Input, ///< Input emulation
|
||||
Tty, ///< Debug output from emu
|
||||
Count ///< Total number of logging classes
|
||||
Log, ///< Messages about the log system itself
|
||||
Common, ///< Library routines
|
||||
Common_Filesystem, ///< Filesystem interface library
|
||||
Common_Memory, ///< Memory mapping and management functions
|
||||
Core, ///< LLE emulation core
|
||||
Core_Linker, ///< The module linker
|
||||
Config, ///< Emulator configuration (including commandline)
|
||||
Debug, ///< Debugging tools
|
||||
Kernel, ///< The HLE implementation of the PS4 kernel.
|
||||
Kernel_Pthread, ///< The pthread implementation of the kernel.
|
||||
Kernel_Fs, ///< The filesystem implementation of the kernel.
|
||||
Kernel_Vmm, ///< The virtual memory implementation of the kernel.
|
||||
Kernel_Event, ///< The event management implementation of the kernel.
|
||||
Kernel_Sce, ///< The sony specific interfaces provided by the kernel.
|
||||
Lib, ///< HLE implementation of system library. Each major library
|
||||
///< should have its own subclass.
|
||||
Lib_LibC, ///< The LibC implementation.
|
||||
Lib_Kernel, ///< The LibKernel implementation.
|
||||
Lib_Pad, ///< The LibScePad implementation.
|
||||
Lib_GnmDriver, ///< The LibSceGnmDriver implementation.
|
||||
Lib_SystemService, ///< The LibSceSystemService implementation.
|
||||
Lib_UserService, ///< The LibSceUserService implementation.
|
||||
Lib_VideoOut, ///< The LibSceVideoOut implementation.
|
||||
Lib_CommonDlg, ///< The LibSceCommonDialog implementation.
|
||||
Lib_MsgDlg, ///< The LibSceMsgDialog implementation.
|
||||
Lib_AudioOut, ///< The LibSceAudioOut implementation.
|
||||
Lib_AudioIn, ///< The LibSceAudioIn implementation.
|
||||
Lib_Net, ///< The LibSceNet implementation.
|
||||
Lib_NetCtl, ///< The LibSecNetCtl implementation.
|
||||
Lib_SaveData, ///< The LibSceSaveData implementation.
|
||||
Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation.
|
||||
Lib_Ssl, ///< The LibSceSsl implementation.
|
||||
Lib_Http, ///< The LibSceHttp implementation.
|
||||
Lib_SysModule, ///< The LibSceSysModule implementation
|
||||
Lib_NpManager, ///< The LibSceNpManager implementation
|
||||
Lib_NpScore, ///< The LibSceNpScore implementation
|
||||
Lib_NpTrophy, ///< The LibSceNpTrophy implementation
|
||||
Lib_Screenshot, ///< The LibSceScreenshot implementation
|
||||
Lib_LibCInternal, ///< The LibCInternal implementation.
|
||||
Lib_AppContent, ///< The LibSceAppContent implementation.
|
||||
Lib_Rtc, ///< The LibSceRtc implementation.
|
||||
Lib_DiscMap, ///< The LibSceDiscMap implementation.
|
||||
Lib_Png, ///< The LibScePng implementation.
|
||||
Lib_PlayGo, ///< The LibScePlayGo implementation.
|
||||
Lib_Random, ///< The libSceRandom implementation.
|
||||
Lib_Usbd, ///< The LibSceUsbd implementation.
|
||||
Lib_Ajm, ///< The LibSceAjm implementation.
|
||||
Lib_ErrorDialog, ///< The LibSceErrorDialog implementation.
|
||||
Lib_ImeDialog, ///< The LibSceImeDialog implementation.
|
||||
Lib_AvPlayer, ///< The LibSceAvPlayer implementation.
|
||||
Lib_Ngs2, ///< The LibSceNgs2 implementation.
|
||||
Lib_Audio3d, ///< The LibSceAudio3d implementation.
|
||||
Lib_Ime, ///< The LibSceIme implementation
|
||||
Lib_GameLiveStreaming, ///< The LibSceGameLiveStreaming implementation
|
||||
Lib_Remoteplay, ///< The LibSceRemotePlay implementation
|
||||
Lib_SharePlay, ///< The LibSceSharePlay implemenation
|
||||
Lib_Fiber, ///< The LibSceFiber implementation.
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Video Core
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
Render_Recompiler, ///< Shader recompiler
|
||||
ImGui, ///< ImGui
|
||||
Loader, ///< ROM loader
|
||||
Input, ///< Input emulation
|
||||
Tty, ///< Debug output from emu
|
||||
Count ///< Total number of logging classes
|
||||
};
|
||||
|
||||
} // namespace Common::Log
|
||||
|
161
src/common/number_utils.cpp
Normal file
161
src/common/number_utils.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <half.hpp>
|
||||
|
||||
#include "common/number_utils.h"
|
||||
#include "video_core/amdgpu/pixel_format.h"
|
||||
#include "video_core/amdgpu/types.h"
|
||||
|
||||
#define UF11_EXPONENT_SHIFT 6
|
||||
#define UF10_EXPONENT_SHIFT 5
|
||||
|
||||
#define RGB9E5_MANTISSA_BITS 9
|
||||
#define RGB9E5_EXP_BIAS 1
|
||||
|
||||
#define F32_INFINITY 0x7f800000
|
||||
|
||||
namespace NumberUtils {
|
||||
|
||||
float Uf11ToF32(u16 val) {
|
||||
union {
|
||||
float f;
|
||||
u32 ui;
|
||||
} f32;
|
||||
|
||||
int exponent = (val & 0x07c0) >> UF11_EXPONENT_SHIFT;
|
||||
int mantissa = (val & 0x003f);
|
||||
|
||||
f32.f = 0.0;
|
||||
|
||||
if (exponent == 0) {
|
||||
if (mantissa != 0) {
|
||||
const float scale = 1.0 / (1 << 20);
|
||||
f32.f = scale * mantissa;
|
||||
}
|
||||
} else if (exponent == 31) {
|
||||
f32.ui = F32_INFINITY | mantissa;
|
||||
} else {
|
||||
float scale, decimal;
|
||||
exponent -= 15;
|
||||
if (exponent < 0) {
|
||||
scale = 1.0f / (1 << -exponent);
|
||||
} else {
|
||||
scale = (float)(1 << exponent);
|
||||
}
|
||||
decimal = 1.0f + (float)mantissa / 64;
|
||||
f32.f = scale * decimal;
|
||||
}
|
||||
|
||||
return f32.f;
|
||||
}
|
||||
|
||||
float Uf10ToF32(u16 val) {
|
||||
union {
|
||||
float f;
|
||||
u32 ui;
|
||||
} f32;
|
||||
|
||||
int exponent = (val & 0x03e0) >> UF10_EXPONENT_SHIFT;
|
||||
int mantissa = (val & 0x001f);
|
||||
|
||||
f32.f = 0.0;
|
||||
|
||||
if (exponent == 0) {
|
||||
if (mantissa != 0) {
|
||||
const float scale = 1.0 / (1 << 19);
|
||||
f32.f = scale * mantissa;
|
||||
}
|
||||
} else if (exponent == 31) {
|
||||
f32.ui = F32_INFINITY | mantissa;
|
||||
} else {
|
||||
float scale, decimal;
|
||||
exponent -= 15;
|
||||
if (exponent < 0) {
|
||||
scale = 1.0f / (1 << -exponent);
|
||||
} else {
|
||||
scale = (float)(1 << exponent);
|
||||
}
|
||||
decimal = 1.0f + (float)mantissa / 32;
|
||||
f32.f = scale * decimal;
|
||||
}
|
||||
|
||||
return f32.f;
|
||||
}
|
||||
|
||||
float Uf16ToF32(u16 val) {
|
||||
return half_float::half_cast<float>(reinterpret_cast<half_float::half&>(val));
|
||||
}
|
||||
|
||||
float U2ToUnorm(u8 val) {
|
||||
static constexpr auto c = 1.0f / 3.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S2ToSnorm(s8 val) {
|
||||
static constexpr auto c = 1.0f / 1.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U4ToUnorm(u8 val) {
|
||||
static constexpr auto c = 1.0f / 15.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S4ToSnorm(s8 val) {
|
||||
static constexpr auto c = 1.0f / 7.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U5ToUnorm(u8 val) {
|
||||
static constexpr auto c = 1.0f / 31.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S5ToSnorm(s8 val) {
|
||||
static constexpr auto c = 1.0f / 15.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U6ToUnorm(u8 val) {
|
||||
static constexpr auto c = 1.0f / 63.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S6ToSnorm(s8 val) {
|
||||
static constexpr auto c = 1.0f / 31.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U8ToUnorm(u8 val) {
|
||||
static constexpr auto c = 1.0f / 255.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S8ToSnorm(s8 val) {
|
||||
static constexpr auto c = 1.0f / 127.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U10ToUnorm(u16 val) {
|
||||
static constexpr auto c = 1.0f / 1023.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S10ToSnorm(s16 val) {
|
||||
static constexpr auto c = 1.0f / 511.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float U16ToUnorm(u16 val) {
|
||||
static constexpr auto c = 1.0f / 65535.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
float S16ToSnorm(s16 val) {
|
||||
static constexpr auto c = 1.0f / 32767.0f;
|
||||
return float(val * c);
|
||||
}
|
||||
|
||||
} // namespace NumberUtils
|
28
src/common/number_utils.h
Normal file
28
src/common/number_utils.h
Normal file
@ -0,0 +1,28 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace NumberUtils {
|
||||
|
||||
float Uf11ToF32(u16 val);
|
||||
float Uf10ToF32(u16 val);
|
||||
float Uf16ToF32(u16 val);
|
||||
float U2ToUnorm(u8 val);
|
||||
float S2ToSnorm(s8 val);
|
||||
float U4ToUnorm(u8 val);
|
||||
float S4ToSnorm(s8 val);
|
||||
float U5ToUnorm(u8 val);
|
||||
float S5ToSnorm(s8 val);
|
||||
float U6ToUnorm(u8 val);
|
||||
float S6ToSnorm(s8 val);
|
||||
float U8ToUnorm(u8 val);
|
||||
float S8ToSnorm(s8 val);
|
||||
float U10ToUnorm(u16 val);
|
||||
float S10ToSnorm(s16 val);
|
||||
float U16ToUnorm(u16 val);
|
||||
float S16ToSnorm(s16 val);
|
||||
|
||||
} // namespace NumberUtils
|
@ -95,6 +95,18 @@ static auto UserPaths = [] {
|
||||
user_dir =
|
||||
std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4";
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
|
||||
// Check if the "user" directory exists in the current path:
|
||||
if (!std::filesystem::exists(user_dir)) {
|
||||
// If it doesn't exist, use XDG_DATA_HOME if it is set, and provide a standard default
|
||||
const char* xdg_data_home = getenv("XDG_DATA_HOME");
|
||||
if (xdg_data_home != nullptr && strlen(xdg_data_home) > 0) {
|
||||
user_dir = std::filesystem::path(xdg_data_home) / "shadPS4";
|
||||
} else {
|
||||
user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4";
|
||||
}
|
||||
}
|
||||
#else
|
||||
const auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
|
||||
#endif
|
||||
|
@ -25,19 +25,18 @@ asm(".zerofill GUEST_SYSTEM,GUEST_SYSTEM,__guest_system,0xFBFC00000");
|
||||
|
||||
namespace Core {
|
||||
|
||||
static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE;
|
||||
static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
[[nodiscard]] constexpr u64 ToWindowsProt(Core::MemoryProt prot) {
|
||||
switch (prot) {
|
||||
case Core::MemoryProt::NoAccess:
|
||||
default:
|
||||
return PAGE_NOACCESS;
|
||||
case Core::MemoryProt::CpuRead:
|
||||
return PAGE_READONLY;
|
||||
case Core::MemoryProt::CpuReadWrite:
|
||||
if (True(prot & Core::MemoryProt::CpuReadWrite) ||
|
||||
True(prot & Core::MemoryProt::GpuReadWrite)) {
|
||||
return PAGE_READWRITE;
|
||||
} else if (True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead)) {
|
||||
return PAGE_READONLY;
|
||||
} else {
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,14 +289,13 @@ enum PosixPageProtection {
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr PosixPageProtection ToPosixProt(Core::MemoryProt prot) {
|
||||
switch (prot) {
|
||||
case Core::MemoryProt::NoAccess:
|
||||
default:
|
||||
return PAGE_NOACCESS;
|
||||
case Core::MemoryProt::CpuRead:
|
||||
return PAGE_READONLY;
|
||||
case Core::MemoryProt::CpuReadWrite:
|
||||
if (True(prot & Core::MemoryProt::CpuReadWrite) ||
|
||||
True(prot & Core::MemoryProt::GpuReadWrite)) {
|
||||
return PAGE_READWRITE;
|
||||
} else if (True(prot & Core::MemoryProt::CpuRead) || True(prot & Core::MemoryProt::GpuRead)) {
|
||||
return PAGE_READONLY;
|
||||
} else {
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,10 @@ public:
|
||||
explicit AddressSpace();
|
||||
~AddressSpace();
|
||||
|
||||
[[nodiscard]] u8* BackingBase() const noexcept {
|
||||
return backing_base;
|
||||
}
|
||||
|
||||
[[nodiscard]] VAddr SystemManagedVirtualBase() noexcept {
|
||||
return reinterpret_cast<VAddr>(system_managed_base);
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/native_clock.h"
|
||||
#include "common/singleton.h"
|
||||
#include "debug_state.h"
|
||||
#include "libraries/kernel/event_queues.h"
|
||||
#include "devtools/widget/common.h"
|
||||
#include "libraries/kernel/time_management.h"
|
||||
#include "libraries/system/msgdialog.h"
|
||||
#include "video_core/amdgpu/pm4_cmds.h"
|
||||
|
||||
using namespace DebugStateType;
|
||||
|
||||
@ -95,8 +98,81 @@ void DebugStateImpl::ResumeGuestThreads() {
|
||||
}
|
||||
|
||||
void DebugStateImpl::RequestFrameDump(s32 count) {
|
||||
ASSERT(!DumpingCurrentFrame());
|
||||
gnm_frame_dump_request_count = count;
|
||||
frame_dump_list.clear();
|
||||
frame_dump_list.resize(count);
|
||||
const auto f = gnm_frame_count.load() + 1;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
frame_dump_list[i].frame_id = f + i;
|
||||
}
|
||||
waiting_submit_pause = true;
|
||||
}
|
||||
|
||||
void DebugStateImpl::PushQueueDump(QueueDump dump) {
|
||||
ASSERT(DumpingCurrentFrame());
|
||||
std::unique_lock lock{frame_dump_list_mutex};
|
||||
auto& frame = GetFrameDump();
|
||||
{ // Find draw calls
|
||||
auto data = std::span{dump.data};
|
||||
auto initial_data = data.data();
|
||||
while (!data.empty()) {
|
||||
const auto* header = reinterpret_cast<const AmdGpu::PM4Type3Header*>(data.data());
|
||||
const auto type = header->type;
|
||||
if (type == 2) {
|
||||
data = data.subspan(1);
|
||||
} else if (type != 3) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
const AmdGpu::PM4ItOpcode opcode = header->opcode;
|
||||
if (Core::Devtools::Widget::IsDrawCall(opcode)) {
|
||||
const auto offset =
|
||||
reinterpret_cast<uintptr_t>(header) - reinterpret_cast<uintptr_t>(initial_data);
|
||||
const auto addr = dump.base_addr + offset;
|
||||
waiting_reg_dumps.emplace(addr, &frame);
|
||||
waiting_reg_dumps_dbg.emplace(
|
||||
addr,
|
||||
fmt::format("#{} h({}) queue {} {} {}",
|
||||
frame_dump_list.size() - gnm_frame_dump_request_count, addr,
|
||||
magic_enum::enum_name(dump.type), dump.submit_num, dump.num2));
|
||||
}
|
||||
data = data.subspan(header->NumWords() + 1);
|
||||
}
|
||||
}
|
||||
frame.queues.push_back(std::move(dump));
|
||||
}
|
||||
|
||||
void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
||||
const AmdGpu::Liverpool::Regs& regs, bool is_compute) {
|
||||
std::scoped_lock lock{frame_dump_list_mutex};
|
||||
const auto it = waiting_reg_dumps.find(header_addr);
|
||||
if (it == waiting_reg_dumps.end()) {
|
||||
return;
|
||||
}
|
||||
auto& frame = *it->second;
|
||||
waiting_reg_dumps.erase(it);
|
||||
waiting_reg_dumps_dbg.erase(waiting_reg_dumps_dbg.find(header_addr));
|
||||
auto& dump = frame.regs[header_addr - base_addr];
|
||||
dump.regs = regs;
|
||||
if (is_compute) {
|
||||
dump.is_compute = true;
|
||||
const auto& cs = dump.regs.cs_program;
|
||||
dump.cs_data = ComputerShaderDump{
|
||||
.cs_program = cs,
|
||||
.code = std::vector<u32>{cs.Code().begin(), cs.Code().end()},
|
||||
};
|
||||
} else {
|
||||
for (int i = 0; i < RegDump::MaxShaderStages; i++) {
|
||||
if (regs.stage_enable.IsStageEnabled(i)) {
|
||||
auto stage = regs.ProgramForStage(i);
|
||||
if (stage->address_lo != 0) {
|
||||
auto code = stage->Code();
|
||||
dump.stages[i] = ShaderDump{
|
||||
.user_data = *stage,
|
||||
.code = std::vector<u32>{code.begin(), code.end()},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,14 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
@ -32,9 +36,9 @@ class FrameGraph;
|
||||
namespace DebugStateType {
|
||||
|
||||
enum class QueueType {
|
||||
acb,
|
||||
dcb,
|
||||
ccb,
|
||||
dcb = 0,
|
||||
ccb = 1,
|
||||
acb = 2,
|
||||
};
|
||||
|
||||
struct QueueDump {
|
||||
@ -42,10 +46,31 @@ struct QueueDump {
|
||||
u32 submit_num;
|
||||
u32 num2; // acb: queue_num; else: buffer_in_submit
|
||||
std::vector<u32> data;
|
||||
uintptr_t base_addr;
|
||||
};
|
||||
|
||||
struct ShaderDump {
|
||||
Vulkan::Liverpool::ShaderProgram user_data{};
|
||||
std::vector<u32> code{};
|
||||
};
|
||||
|
||||
struct ComputerShaderDump {
|
||||
Vulkan::Liverpool::ComputeProgram cs_program{};
|
||||
std::vector<u32> code{};
|
||||
};
|
||||
|
||||
struct RegDump {
|
||||
bool is_compute{false};
|
||||
static constexpr size_t MaxShaderStages = 5;
|
||||
Vulkan::Liverpool::Regs regs{};
|
||||
std::array<ShaderDump, MaxShaderStages> stages{};
|
||||
ComputerShaderDump cs_data{};
|
||||
};
|
||||
|
||||
struct FrameDump {
|
||||
u32 frame_id;
|
||||
std::vector<QueueDump> queues;
|
||||
std::unordered_map<uintptr_t, RegDump> regs; // address -> reg dump
|
||||
};
|
||||
|
||||
class DebugStateImpl {
|
||||
@ -61,15 +86,24 @@ class DebugStateImpl {
|
||||
std::atomic_int32_t gnm_frame_count = 0;
|
||||
|
||||
s32 gnm_frame_dump_request_count = -1;
|
||||
std::unordered_map<size_t, FrameDump*> waiting_reg_dumps;
|
||||
std::unordered_map<size_t, std::string> waiting_reg_dumps_dbg;
|
||||
bool waiting_submit_pause = false;
|
||||
bool should_show_frame_dump = false;
|
||||
|
||||
std::mutex frame_dump_list_mutex;
|
||||
std::shared_mutex frame_dump_list_mutex;
|
||||
std::vector<FrameDump> frame_dump_list{};
|
||||
|
||||
std::queue<std::string> debug_message_popup;
|
||||
|
||||
public:
|
||||
void ShowDebugMessage(std::string message) {
|
||||
if (message.empty()) {
|
||||
return;
|
||||
}
|
||||
debug_message_popup.push(std::move(message));
|
||||
}
|
||||
|
||||
void AddCurrentThreadToGuestList();
|
||||
|
||||
void RemoveCurrentThreadFromGuestList();
|
||||
@ -99,6 +133,11 @@ public:
|
||||
return gnm_frame_dump_request_count > 0;
|
||||
}
|
||||
|
||||
bool DumpingCurrentReg() {
|
||||
std::shared_lock lock{frame_dump_list_mutex};
|
||||
return !waiting_reg_dumps.empty();
|
||||
}
|
||||
|
||||
bool ShouldPauseInSubmit() const {
|
||||
return waiting_submit_pause && gnm_frame_dump_request_count == 0;
|
||||
}
|
||||
@ -109,17 +148,10 @@ public:
|
||||
return frame_dump_list[frame_dump_list.size() - gnm_frame_dump_request_count];
|
||||
}
|
||||
|
||||
void PushQueueDump(QueueDump dump) {
|
||||
std::unique_lock lock{frame_dump_list_mutex};
|
||||
GetFrameDump().queues.push_back(std::move(dump));
|
||||
}
|
||||
void PushQueueDump(QueueDump dump);
|
||||
|
||||
void ShowDebugMessage(std::string message) {
|
||||
if (message.empty()) {
|
||||
return;
|
||||
}
|
||||
debug_message_popup.push(std::move(message));
|
||||
}
|
||||
void PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
||||
const AmdGpu::Liverpool::Regs& regs, bool is_compute = false);
|
||||
};
|
||||
} // namespace DebugStateType
|
||||
|
||||
|
8
src/core/devtools/help.txt
Normal file
8
src/core/devtools/help.txt
Normal file
@ -0,0 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
R"(
|
||||
* If you hold shift, you can move the window without docking it.
|
||||
* You don't need to close every window you open. When a parent window is closed, all its children will be closed too.
|
||||
* If you want to inspect or compare more than 1 frame dump without undocking, there's a option to keep showing opened popups even when in hide/minimize the frame dump window.
|
||||
* To use the disassembly viewer, you need to set up a cli to use a external disassembler and use "{src}" as a placeholder for the source code file, e.g. dis.exe --some-opt "{src}"
|
||||
)"
|
@ -10,6 +10,7 @@
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "layer.h"
|
||||
#include "options.h"
|
||||
#include "widget/frame_dump.h"
|
||||
#include "widget/frame_graph.h"
|
||||
|
||||
@ -18,10 +19,9 @@ using namespace Core::Devtools;
|
||||
using L = Core::Devtools::Layer;
|
||||
|
||||
static bool show_simple_fps = false;
|
||||
|
||||
static float fps_scale = 1.0f;
|
||||
|
||||
static bool show_advanced_debug = false;
|
||||
|
||||
static int dump_frame_count = 1;
|
||||
|
||||
static Widget::FrameGraph frame_graph;
|
||||
@ -29,12 +29,23 @@ static std::vector<Widget::FrameDumpViewer> frame_viewers;
|
||||
|
||||
static float debug_popup_timing = 3.0f;
|
||||
|
||||
static bool just_opened_options = false;
|
||||
|
||||
// clang-format off
|
||||
static std::string help_text =
|
||||
#include "help.txt"
|
||||
;
|
||||
// clang-format on
|
||||
|
||||
void L::DrawMenuBar() {
|
||||
const auto& ctx = *GImGui;
|
||||
const auto& io = ctx.IO;
|
||||
|
||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||
|
||||
bool open_popup_options = false;
|
||||
bool open_popup_help = false;
|
||||
|
||||
if (BeginMainMenuBar()) {
|
||||
if (BeginMenu("Options")) {
|
||||
if (MenuItemEx("Emulator Paused", nullptr, nullptr, isSystemPaused)) {
|
||||
@ -55,6 +66,8 @@ void L::DrawMenuBar() {
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
open_popup_options = MenuItem("Options");
|
||||
open_popup_help = MenuItem("Help & Tips");
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
EndMainMenuBar();
|
||||
@ -74,6 +87,14 @@ void L::DrawMenuBar() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (open_popup_options) {
|
||||
OpenPopup("GPU Tools Options");
|
||||
just_opened_options = true;
|
||||
}
|
||||
if (open_popup_help) {
|
||||
OpenPopup("HelpTips");
|
||||
}
|
||||
}
|
||||
|
||||
void L::DrawAdvanced() {
|
||||
@ -91,13 +112,19 @@ void L::DrawAdvanced() {
|
||||
->AddText({10.0f, io.DisplaySize.y - 40.0f}, IM_COL32_WHITE, "Emulator paused");
|
||||
}
|
||||
|
||||
if (DebugState.should_show_frame_dump) {
|
||||
if (DebugState.should_show_frame_dump && DebugState.waiting_reg_dumps.empty()) {
|
||||
DebugState.should_show_frame_dump = false;
|
||||
std::unique_lock lock{DebugState.frame_dump_list_mutex};
|
||||
while (!DebugState.frame_dump_list.empty()) {
|
||||
auto frame_dump = std::move(DebugState.frame_dump_list.back());
|
||||
DebugState.frame_dump_list.pop_back();
|
||||
const auto& frame_dump = DebugState.frame_dump_list.back();
|
||||
frame_viewers.emplace_back(frame_dump);
|
||||
DebugState.frame_dump_list.pop_back();
|
||||
}
|
||||
static bool first_time = true;
|
||||
if (first_time) {
|
||||
first_time = false;
|
||||
DebugState.ShowDebugMessage("Tip: You can shift+click any\n"
|
||||
"popup to open a new window");
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,6 +160,54 @@ void L::DrawAdvanced() {
|
||||
debug_popup_timing = 3.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bool close_popup_options = true;
|
||||
if (BeginPopupModal("GPU Tools Options", &close_popup_options,
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) {
|
||||
static char disassembly_cli[512];
|
||||
static bool frame_dump_render_on_collapse;
|
||||
|
||||
if (just_opened_options) {
|
||||
just_opened_options = false;
|
||||
auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1);
|
||||
disassembly_cli[s] = '\0';
|
||||
frame_dump_render_on_collapse = Options.frame_dump_render_on_collapse;
|
||||
}
|
||||
|
||||
InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli));
|
||||
if (IsItemHovered()) {
|
||||
SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")");
|
||||
}
|
||||
Checkbox("Show frame dump popups even when collapsed", &frame_dump_render_on_collapse);
|
||||
if (IsItemHovered()) {
|
||||
SetTooltip("When a frame dump is collapsed, it will keep\n"
|
||||
"showing all opened popups related to it");
|
||||
}
|
||||
|
||||
if (Button("Save")) {
|
||||
Options.disassembly_cli = disassembly_cli;
|
||||
Options.frame_dump_render_on_collapse = frame_dump_render_on_collapse;
|
||||
SaveIniSettingsToDisk(io.IniFilename);
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
EndPopup();
|
||||
}
|
||||
|
||||
if (BeginPopup("HelpTips", ImGuiWindowFlags_AlwaysAutoResize |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove)) {
|
||||
CentralizeWindow();
|
||||
|
||||
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{10.0f});
|
||||
PushTextWrapPos(600.0f);
|
||||
|
||||
const char* begin = help_text.data();
|
||||
TextUnformatted(begin, begin + help_text.size());
|
||||
|
||||
PopTextWrapPos();
|
||||
PopStyleVar();
|
||||
|
||||
EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void L::DrawSimple() {
|
||||
@ -140,26 +215,54 @@ void L::DrawSimple() {
|
||||
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
}
|
||||
|
||||
static void LoadSettings(const char* line) {
|
||||
int i;
|
||||
float f;
|
||||
if (sscanf(line, "fps_scale=%f", &f) == 1) {
|
||||
fps_scale = f;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "show_advanced_debug=%d", &i) == 1) {
|
||||
show_advanced_debug = i != 0;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "show_frame_graph=%d", &i) == 1) {
|
||||
frame_graph.is_open = i != 0;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "dump_frame_count=%d", &i) == 1) {
|
||||
dump_frame_count = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void L::SetupSettings() {
|
||||
frame_graph.is_open = true;
|
||||
|
||||
using SettingLoader = void (*)(const char*);
|
||||
|
||||
ImGuiSettingsHandler handler{};
|
||||
handler.TypeName = "DevtoolsLayer";
|
||||
handler.TypeHash = ImHashStr(handler.TypeName);
|
||||
handler.ReadOpenFn = [](ImGuiContext*, ImGuiSettingsHandler*, const char* name) {
|
||||
return std::string_view("Data") == name ? (void*)1 : nullptr;
|
||||
if (std::string_view("Data") == name) {
|
||||
static_assert(std::is_same_v<decltype(&LoadSettings), SettingLoader>);
|
||||
return (void*)&LoadSettings;
|
||||
}
|
||||
if (std::string_view("CmdList") == name) {
|
||||
static_assert(
|
||||
std::is_same_v<decltype(&Widget::CmdListViewer::LoadConfig), SettingLoader>);
|
||||
return (void*)&Widget::CmdListViewer::LoadConfig;
|
||||
}
|
||||
if (std::string_view("Options") == name) {
|
||||
static_assert(std::is_same_v<decltype(&LoadOptionsConfig), SettingLoader>);
|
||||
return (void*)&LoadOptionsConfig;
|
||||
}
|
||||
return (void*)nullptr;
|
||||
};
|
||||
handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void*, const char* line) {
|
||||
int v;
|
||||
float f;
|
||||
if (sscanf(line, "fps_scale=%f", &f) == 1) {
|
||||
fps_scale = f;
|
||||
} else if (sscanf(line, "show_advanced_debug=%d", &v) == 1) {
|
||||
show_advanced_debug = v != 0;
|
||||
} else if (sscanf(line, "show_frame_graph=%d", &v) == 1) {
|
||||
frame_graph.is_open = v != 0;
|
||||
} else if (sscanf(line, "dump_frame_count=%d", &v) == 1) {
|
||||
dump_frame_count = v;
|
||||
handler.ReadLineFn = [](ImGuiContext*, ImGuiSettingsHandler*, void* handle, const char* line) {
|
||||
if (handle != nullptr) {
|
||||
reinterpret_cast<SettingLoader>(handle)(line);
|
||||
}
|
||||
};
|
||||
handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) {
|
||||
@ -169,12 +272,19 @@ void L::SetupSettings() {
|
||||
buf->appendf("show_frame_graph=%d\n", frame_graph.is_open);
|
||||
buf->appendf("dump_frame_count=%d\n", dump_frame_count);
|
||||
buf->append("\n");
|
||||
buf->appendf("[%s][CmdList]\n", handler->TypeName);
|
||||
Widget::CmdListViewer::SerializeConfig(buf);
|
||||
buf->append("\n");
|
||||
buf->appendf("[%s][Options]\n", handler->TypeName);
|
||||
SerializeOptionsConfig(buf);
|
||||
buf->append("\n");
|
||||
};
|
||||
AddSettingsHandler(&handler);
|
||||
|
||||
const ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
||||
DockBuilderAddNode(dock_id, 0);
|
||||
DockBuilderSetNodePos(dock_id, ImVec2{50.0, 50.0});
|
||||
DockBuilderSetNodePos(dock_id, ImVec2{450.0, 150.0});
|
||||
DockBuilderSetNodeSize(dock_id, ImVec2{400.0, 500.0});
|
||||
DockBuilderFinish(dock_id);
|
||||
}
|
||||
|
||||
|
30
src/core/devtools/options.cpp
Normal file
30
src/core/devtools/options.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "options.h"
|
||||
|
||||
namespace Core::Devtools {
|
||||
|
||||
TOptions Options;
|
||||
|
||||
void LoadOptionsConfig(const char* line) {
|
||||
char str[512];
|
||||
int i;
|
||||
if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) {
|
||||
Options.disassembly_cli = str;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "frame_dump_render_on_collapse=%d", &i) == 1) {
|
||||
Options.frame_dump_render_on_collapse = i != 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeOptionsConfig(ImGuiTextBuffer* buf) {
|
||||
buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str());
|
||||
buf->appendf("frame_dump_render_on_collapse=%d\n", Options.frame_dump_render_on_collapse);
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools
|
22
src/core/devtools/options.h
Normal file
22
src/core/devtools/options.h
Normal file
@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct ImGuiTextBuffer;
|
||||
|
||||
namespace Core::Devtools {
|
||||
|
||||
struct TOptions {
|
||||
std::string disassembly_cli{};
|
||||
bool frame_dump_render_on_collapse{false};
|
||||
};
|
||||
|
||||
extern TOptions Options;
|
||||
|
||||
void LoadOptionsConfig(const char* line);
|
||||
void SerializeOptionsConfig(ImGuiTextBuffer* buf);
|
||||
|
||||
} // namespace Core::Devtools
|
@ -32,6 +32,26 @@ const char* GetOpCodeName(u32 op);
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
static bool group_batches = true;
|
||||
static bool show_markers = false;
|
||||
|
||||
void CmdListViewer::LoadConfig(const char* line) {
|
||||
int i;
|
||||
if (sscanf(line, "group_batches=%d", &i) == 1) {
|
||||
group_batches = i != 0;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "show_markers=%d", &i) == 1) {
|
||||
show_markers = i != 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CmdListViewer::SerializeConfig(ImGuiTextBuffer* buf) {
|
||||
buf->appendf("group_batches=%d\n", group_batches);
|
||||
buf->appendf("show_markers=%d\n", show_markers);
|
||||
}
|
||||
|
||||
template <typename HdrType>
|
||||
static HdrType GetNext(HdrType this_pm4, uint32_t n) {
|
||||
HdrType curr_pm4 = this_pm4;
|
||||
@ -43,10 +63,11 @@ static HdrType GetNext(HdrType this_pm4, uint32_t n) {
|
||||
return curr_pm4;
|
||||
}
|
||||
|
||||
static void ParsePolygonControl(u32 value) {
|
||||
void ParsePolygonControl(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<AmdGpu::Liverpool::PolygonControl const&>(value);
|
||||
|
||||
if (BeginTable("PA_SU_SC_MODE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("PA_SU_SC_MODE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("CULL_FRONT");
|
||||
@ -126,14 +147,17 @@ static void ParsePolygonControl(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.multi_prim_ib_ena.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseAaConfig(u32 value) {
|
||||
void ParseAaConfig(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<Liverpool::AaConfig const&>(value);
|
||||
|
||||
if (BeginTable("PA_SC_AA_CONFIG", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("PA_SC_AA_CONFIG", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("MSAA_NUM_SAMPLES");
|
||||
@ -164,14 +188,17 @@ static void ParseAaConfig(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.detail_to_exposed_mode.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseViewportControl(u32 value) {
|
||||
void ParseViewportControl(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<Liverpool::ViewportControl const&>(value);
|
||||
|
||||
if (BeginTable("PA_CL_VTE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("PA_CL_VTE_CNTL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("VPORT_X_SCALE_ENA");
|
||||
@ -232,14 +259,17 @@ static void ParseViewportControl(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.perfcounter_ref.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseColorControl(u32 value) {
|
||||
void ParseColorControl(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<Liverpool::ColorControl const&>(value);
|
||||
|
||||
if (BeginTable("CB_COLOR_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("CB_COLOR_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("DISABLE_DUAL_QUAD__VI");
|
||||
@ -264,14 +294,17 @@ static void ParseColorControl(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.rop3.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseColor0Info(u32 value) {
|
||||
void ParseColor0Info(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<Liverpool::ColorBuffer::Color0Info const&>(value);
|
||||
|
||||
if (BeginTable("CB_COLOR_INFO", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("CB_COLOR_INFO", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("ENDIAN");
|
||||
@ -380,14 +413,17 @@ static void ParseColor0Info(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.cmask_addr_type.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseColor0Attrib(u32 value) {
|
||||
void ParseColor0Attrib(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<Liverpool::ColorBuffer::Color0Attrib const&>(value);
|
||||
|
||||
if (BeginTable("CB_COLOR_ATTRIB", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("CB_COLOR_ATTRIB", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("TILE_MODE_INDEX");
|
||||
@ -424,14 +460,17 @@ static void ParseColor0Attrib(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.force_dst_alpha_1.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseBlendControl(u32 value) {
|
||||
void ParseBlendControl(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<Liverpool::BlendControl const&>(value);
|
||||
|
||||
if (BeginTable("CB_BLEND_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("CB_BLEND_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("COLOR_SRCBLEND");
|
||||
@ -490,14 +529,17 @@ static void ParseBlendControl(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.disable_rop3.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseDepthRenderControl(u32 value) {
|
||||
void ParseDepthRenderControl(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<Liverpool::DepthRenderControl const&>(value);
|
||||
|
||||
if (BeginTable("DB_RENDER_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("DB_RENDER_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("DEPTH_CLEAR_ENABLE");
|
||||
@ -558,14 +600,17 @@ static void ParseDepthRenderControl(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.decompress_enable.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseDepthControl(u32 value) {
|
||||
void ParseDepthControl(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<Liverpool::DepthControl const&>(value);
|
||||
|
||||
if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("STENCIL_ENABLE");
|
||||
@ -628,14 +673,17 @@ static void ParseDepthControl(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.disable_color_writes_on_depth_pass.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseEqaa(u32 value) {
|
||||
void ParseEqaa(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<Liverpool::Eqaa const&>(value);
|
||||
|
||||
if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("MAX_ANCHOR_SAMPLES");
|
||||
@ -708,14 +756,17 @@ static void ParseEqaa(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.enable_postz_overrasterization.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseZInfo(u32 value) {
|
||||
void ParseZInfo(u32 value, bool begin_table) {
|
||||
auto const reg = reinterpret_cast<Liverpool::DepthBuffer::ZInfo const&>(value);
|
||||
|
||||
if (BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
if (!begin_table ||
|
||||
BeginTable("DB_DEPTH_CONTROL", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
TableNextRow();
|
||||
TableSetColumnIndex(0);
|
||||
Text("FORMAT");
|
||||
@ -776,40 +827,41 @@ static void ParseZInfo(u32 value) {
|
||||
TableSetColumnIndex(1);
|
||||
Text("%X", reg.zrange_precision.Value());
|
||||
|
||||
EndTable();
|
||||
if (begin_table) {
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CmdListViewer::OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body) {
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
enum class NOP_PAYLOAD : u32 {
|
||||
ACB_SUBMIT_MRK = 0x68750013,
|
||||
ALLOC_ALIGN8 = 0x68753000,
|
||||
PUSH_MARKER = 0x68750001,
|
||||
SET_VSHARP = 0x68750004,
|
||||
SET_TSHARP = 0x68750005,
|
||||
SET_SSHARP = 0x68750006,
|
||||
SET_USER_DATA = 0x6875000d,
|
||||
};
|
||||
auto get_noppayload_text = [](NOP_PAYLOAD const nop_payload) {
|
||||
#define NOP_PAYLOAD \
|
||||
P(PUSH_MARKER, 0x68750001) \
|
||||
P(POP_MARKER, 0x68750002) \
|
||||
P(SET_MARKER, 0x68750003) \
|
||||
P(SET_VSHARP, 0x68750004) \
|
||||
P(SET_TSHARP, 0x68750005) \
|
||||
P(SET_SSHARP, 0x68750006) \
|
||||
P(ACB_SUBMIT_MRK, 0x68750013) \
|
||||
P(SET_USER_DATA, 0x6875000D) \
|
||||
P(PATCHED_FLIP, 0x68750776) \
|
||||
P(PREPARE_FLIP, 0x68750777) \
|
||||
P(PREPARE_FLIP_LABEL, 0x68750778) \
|
||||
P(PREPARE_FLIP_INTERRUPT, 0x68750780) \
|
||||
P(PREPARE_FLIP_INTERRUPT_LABEL, 0x68750781) \
|
||||
P(ALLOC_ALIGN8, 0x68753000)
|
||||
|
||||
auto get_nop_payload_text = [](u32 const nop_payload) {
|
||||
switch (nop_payload) {
|
||||
case NOP_PAYLOAD::ACB_SUBMIT_MRK:
|
||||
return "ACB_SUBMIT_MRK"sv;
|
||||
case NOP_PAYLOAD::ALLOC_ALIGN8:
|
||||
return "ALLOC_ALIGN8"sv;
|
||||
case NOP_PAYLOAD::PUSH_MARKER:
|
||||
return "PUSH_MARKER"sv;
|
||||
case NOP_PAYLOAD::SET_VSHARP:
|
||||
return "SET_VSHARP"sv;
|
||||
case NOP_PAYLOAD::SET_TSHARP:
|
||||
return "SET_TSHARP"sv;
|
||||
case NOP_PAYLOAD::SET_SSHARP:
|
||||
return "SET_SSHARP"sv;
|
||||
case NOP_PAYLOAD::SET_USER_DATA:
|
||||
return "SET_USER_DATA"sv;
|
||||
#define P(name, value) \
|
||||
case value: \
|
||||
return #name##sv;
|
||||
NOP_PAYLOAD
|
||||
#undef P
|
||||
default:
|
||||
return ""sv;
|
||||
}
|
||||
return ""sv;
|
||||
};
|
||||
|
||||
Separator();
|
||||
@ -822,7 +874,7 @@ void CmdListViewer::OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body)
|
||||
for (unsigned i = 0; i < pkt->header.count + 1; ++i) {
|
||||
Text("%02X: %08X", i, payload[i]);
|
||||
if ((payload[i] & 0xffff0000) == 0x68750000) {
|
||||
const auto& e = get_noppayload_text((NOP_PAYLOAD)payload[i]);
|
||||
const auto& e = get_nop_payload_text(payload[i]);
|
||||
if (!e.empty()) {
|
||||
SameLine();
|
||||
Text("(%s)", e.data());
|
||||
@ -836,7 +888,7 @@ void CmdListViewer::OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* b
|
||||
Separator();
|
||||
BeginGroup();
|
||||
|
||||
auto const* pkt = reinterpret_cast<AmdGpu::PM4CmdSetBase const*>(header);
|
||||
// auto const* pkt = reinterpret_cast<AmdGpu::PM4CmdSetBase const*>(header);
|
||||
Text("BASE_INDEX: %08X", body[0]);
|
||||
Text("ADDRESS0 : %08X", body[1]);
|
||||
Text("ADDRESS1 : %08X", body[2]);
|
||||
@ -1025,20 +1077,31 @@ void CmdListViewer::OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const*
|
||||
EndGroup();
|
||||
}
|
||||
|
||||
CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cmd_list)
|
||||
: parent(parent) {
|
||||
CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump,
|
||||
const std::vector<u32>& cmd_list, uintptr_t _base_addr,
|
||||
std::string _name)
|
||||
: frame_dump(_frame_dump), base_addr(_base_addr), name(std::move(_name)) {
|
||||
using namespace AmdGpu;
|
||||
|
||||
cmdb_addr = (uintptr_t)cmd_list.data();
|
||||
cmdb_size = cmd_list.size() * sizeof(u32);
|
||||
|
||||
cmdb_view_name = fmt::format("[GFX] Command buffer {}###cmdview_hex_{}", this->name, cmdb_addr);
|
||||
cmdb_view.Open = false;
|
||||
cmdb_view.ReadOnly = true;
|
||||
|
||||
auto const* pm4_hdr = reinterpret_cast<PM4Header const*>(cmdb_addr);
|
||||
|
||||
size_t processed_size = 0;
|
||||
size_t prev_offset = 0;
|
||||
u32 batch_id = 0;
|
||||
|
||||
std::string marker{};
|
||||
|
||||
if (cmdb_size > 0) {
|
||||
events.emplace_back(BatchBegin{.id = 0});
|
||||
}
|
||||
|
||||
while (processed_size < cmdb_size) {
|
||||
auto* next_pm4_hdr = GetNext(pm4_hdr, 1);
|
||||
auto processed_len =
|
||||
@ -1048,22 +1111,28 @@ CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cm
|
||||
if (pm4_hdr->type == PM4Type3Header::TYPE) {
|
||||
|
||||
auto const* pm4_t3 = reinterpret_cast<PM4Type3Header const*>(pm4_hdr);
|
||||
auto opcode = pm4_t3->opcode;
|
||||
|
||||
if (pm4_t3->opcode == PM4ItOpcode::Nop) {
|
||||
if (opcode == PM4ItOpcode::Nop) {
|
||||
auto const* it_body = reinterpret_cast<uint32_t const*>(pm4_hdr + 1);
|
||||
if (it_body[0] == 0x68750001) {
|
||||
switch (it_body[0]) {
|
||||
case PM4CmdNop::PayloadType::DebugSetMarker:
|
||||
marker = std::string{(char*)&it_body[1]};
|
||||
break;
|
||||
case PM4CmdNop::PayloadType::DebugMarkerPush:
|
||||
events.emplace_back(PushMarker{
|
||||
.name = std::string{(char*)&it_body[1]},
|
||||
});
|
||||
break;
|
||||
case PM4CmdNop::PayloadType::DebugMarkerPop:
|
||||
events.emplace_back(PopMarker{});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pm4_t3->opcode == PM4ItOpcode::DispatchDirect ||
|
||||
pm4_t3->opcode == PM4ItOpcode::DispatchIndirect ||
|
||||
pm4_t3->opcode == PM4ItOpcode::DrawIndex2 ||
|
||||
pm4_t3->opcode == PM4ItOpcode::DrawIndexAuto ||
|
||||
pm4_t3->opcode == PM4ItOpcode::DrawIndexOffset2 ||
|
||||
pm4_t3->opcode == PM4ItOpcode::DrawIndexIndirect
|
||||
// ...
|
||||
) {
|
||||
if (IsDrawCall(opcode)) {
|
||||
// All these commands are terminated by NOP at the end, so
|
||||
// it is safe to skip it to be even with CP
|
||||
// next_pm4_hdr = get_next(next_pm4_hdr, 1);
|
||||
@ -1071,15 +1140,17 @@ CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cm
|
||||
// processed_len += nop_len;
|
||||
// processed_size += nop_len;
|
||||
|
||||
batches.emplace_back(BatchInfo{
|
||||
marker,
|
||||
prev_offset,
|
||||
processed_size,
|
||||
processed_size - processed_len,
|
||||
pm4_t3->opcode,
|
||||
events.emplace_back(BatchInfo{
|
||||
.id = batch_id++,
|
||||
.marker = marker,
|
||||
.start_addr = prev_offset,
|
||||
.end_addr = processed_size,
|
||||
.command_addr = processed_size - processed_len,
|
||||
.type = opcode,
|
||||
});
|
||||
prev_offset = processed_size;
|
||||
marker.clear();
|
||||
events.emplace_back(BatchBegin{.id = batch_id});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1088,18 +1159,62 @@ CmdListViewer::CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cm
|
||||
|
||||
// state batch (last)
|
||||
if (processed_size - prev_offset > 0) {
|
||||
batches.emplace_back(BatchInfo{
|
||||
marker,
|
||||
prev_offset,
|
||||
processed_size,
|
||||
0,
|
||||
static_cast<PM4ItOpcode>(0xFF),
|
||||
events.emplace_back(BatchInfo{
|
||||
.id = batch_id++,
|
||||
.marker = marker,
|
||||
.start_addr = prev_offset,
|
||||
.end_addr = processed_size,
|
||||
.command_addr = 0,
|
||||
.type = static_cast<PM4ItOpcode>(0xFF),
|
||||
});
|
||||
}
|
||||
if (!events.empty() && std::holds_alternative<BatchBegin>(events.back())) {
|
||||
events.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void CmdListViewer::Draw() {
|
||||
void CmdListViewer::Draw(bool only_batches_view) {
|
||||
const auto& ctx = *GetCurrentContext();
|
||||
|
||||
if (batch_view.open) {
|
||||
batch_view.Draw();
|
||||
}
|
||||
for (auto it = extra_batch_view.begin(); it != extra_batch_view.end();) {
|
||||
if (!it->open) {
|
||||
it = extra_batch_view.erase(it);
|
||||
continue;
|
||||
}
|
||||
it->Draw();
|
||||
++it;
|
||||
}
|
||||
|
||||
if (only_batches_view) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmdb_view.Open) {
|
||||
MemoryEditor::Sizes s;
|
||||
cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr);
|
||||
SetNextWindowSize({s.WindowWidth, s.WindowWidth * 0.6f}, ImGuiCond_FirstUseEver);
|
||||
SetNextWindowSizeConstraints({0.0f}, {s.WindowWidth, FLT_MAX});
|
||||
if (Begin(cmdb_view_name.c_str(), &cmdb_view.Open,
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings)) {
|
||||
cmdb_view.DrawContents((void*)cmdb_addr, cmdb_size, base_addr);
|
||||
if (cmdb_view.ContentsWidthChanged) {
|
||||
cmdb_view.CalcSizes(s, cmdb_size, cmdb_addr);
|
||||
SetWindowSize({s.WindowWidth, s.WindowWidth * 0.6f});
|
||||
}
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
PushID(name.c_str());
|
||||
if (BeginChild("cmd_queue", {})) {
|
||||
|
||||
Checkbox("Group batches", &group_batches);
|
||||
SameLine();
|
||||
Checkbox("Show markers", &show_markers);
|
||||
|
||||
char queue_name[32]{};
|
||||
if (vqid < 254) {
|
||||
std::snprintf(queue_name, sizeof(queue_name), "%s %d", vqid > 254 ? "GFX" : "ASC",
|
||||
@ -1111,113 +1226,257 @@ void CmdListViewer::Draw() {
|
||||
Text("queue : %s", queue_name);
|
||||
Text("base addr: %08llX", cmdb_addr);
|
||||
SameLine();
|
||||
if (SmallButton(">")) {
|
||||
parent->cmdb_view.Open ^= true;
|
||||
if (SmallButton("Memory >")) {
|
||||
cmdb_view.Open ^= true;
|
||||
}
|
||||
Text("size : %04llX", cmdb_size);
|
||||
Separator();
|
||||
|
||||
char batch_hdr[128];
|
||||
for (int batch_id = 0; batch_id < batches.size(); ++batch_id) {
|
||||
auto processed_size = 0ull;
|
||||
auto const* pm4_hdr =
|
||||
reinterpret_cast<PM4Header const*>(cmdb_addr + batches[batch_id].start_addr);
|
||||
{
|
||||
int tree_depth = 0;
|
||||
int tree_depth_show = 0;
|
||||
|
||||
sprintf(batch_hdr, "%08llX: batch-%03d | %s", cmdb_addr + batches[batch_id].start_addr,
|
||||
batch_id, batches[batch_id].marker.c_str());
|
||||
|
||||
if (batch_id == batch_bp) { // highlight batch at breakpoint
|
||||
PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f});
|
||||
u32 last_batch_id = ~0u;
|
||||
if (!events.empty() && std::holds_alternative<BatchInfo>(events.back())) {
|
||||
last_batch_id = std::get<BatchInfo>(events.back()).id;
|
||||
}
|
||||
|
||||
if (batches[batch_id].type == static_cast<AmdGpu::PM4ItOpcode>(0xFF) ||
|
||||
CollapsingHeader(batch_hdr)) {
|
||||
auto const batch_sz = batches[batch_id].end_addr - batches[batch_id].start_addr;
|
||||
while (processed_size < batch_sz) {
|
||||
AmdGpu::PM4ItOpcode op{0xFFu};
|
||||
u32 batch_id = ~0u;
|
||||
u32 current_highlight_batch = ~0u;
|
||||
|
||||
if (pm4_hdr->type == AmdGpu::PM4Type3Header::TYPE) {
|
||||
auto const* pm4_t3 =
|
||||
reinterpret_cast<AmdGpu::PM4Type3Header const*>(pm4_hdr);
|
||||
op = pm4_t3->opcode;
|
||||
int id = 0;
|
||||
PushID(0);
|
||||
for (const auto& event : events) {
|
||||
PopID();
|
||||
PushID(id++);
|
||||
|
||||
static char header_name[128];
|
||||
sprintf(header_name, "%08llX: %s",
|
||||
cmdb_addr + batches[batch_id].start_addr + processed_size,
|
||||
Gcn::GetOpCodeName((u32)op));
|
||||
if (std::holds_alternative<BatchBegin>(event)) {
|
||||
batch_id = std::get<BatchBegin>(event).id;
|
||||
}
|
||||
|
||||
if (TreeNode(header_name)) {
|
||||
bool just_opened = IsItemToggledOpen();
|
||||
if (BeginTable("split", 1)) {
|
||||
TableNextColumn();
|
||||
Text("size: %d", pm4_hdr->count + 1);
|
||||
if (show_markers) {
|
||||
if (std::holds_alternative<PushMarker>(event)) {
|
||||
if (tree_depth_show >= tree_depth) {
|
||||
auto& marker = std::get<PushMarker>(event);
|
||||
bool show = TreeNode(&event, "%s", marker.name.c_str());
|
||||
if (show) {
|
||||
tree_depth_show++;
|
||||
}
|
||||
}
|
||||
tree_depth++;
|
||||
continue;
|
||||
}
|
||||
if (std::holds_alternative<PopMarker>(event)) {
|
||||
if (tree_depth_show >= tree_depth) {
|
||||
tree_depth_show--;
|
||||
TreePop();
|
||||
}
|
||||
tree_depth--;
|
||||
continue;
|
||||
}
|
||||
if (tree_depth_show < tree_depth) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (just_opened) {
|
||||
if (!std::holds_alternative<BatchInfo>(event)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& batch = std::get<BatchInfo>(event);
|
||||
auto const* pm4_hdr =
|
||||
reinterpret_cast<PM4Header const*>(cmdb_addr + batch.start_addr);
|
||||
|
||||
bool ignore_header = false;
|
||||
char batch_hdr[128];
|
||||
if (batch.type == static_cast<AmdGpu::PM4ItOpcode>(0xFF)) {
|
||||
ignore_header = true;
|
||||
} else if (!batch.marker.empty()) {
|
||||
snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s | %s",
|
||||
cmdb_addr + batch.start_addr, batch.id,
|
||||
Gcn::GetOpCodeName(static_cast<u32>(batch.type)),
|
||||
batch.marker.c_str());
|
||||
} else {
|
||||
snprintf(batch_hdr, sizeof(batch_hdr), "%08llX: batch-%03d %s",
|
||||
cmdb_addr + batch.start_addr, batch.id,
|
||||
Gcn::GetOpCodeName(static_cast<u32>(batch.type)));
|
||||
}
|
||||
|
||||
if (batch.id == batch_bp) { // highlight batch at breakpoint
|
||||
PushStyleColor(ImGuiCol_Header, ImVec4{1.0f, 0.5f, 0.5f, 0.5f});
|
||||
}
|
||||
if (batch.id == highlight_batch) {
|
||||
PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
|
||||
}
|
||||
|
||||
const auto open_batch_view = [&, this] {
|
||||
if (frame_dump->regs.contains(batch.command_addr)) {
|
||||
auto data = frame_dump->regs.at(batch.command_addr);
|
||||
if (GetIO().KeyShift) {
|
||||
auto& pop = extra_batch_view.emplace_back();
|
||||
pop.SetData(data, name, batch_id);
|
||||
pop.open = true;
|
||||
} else {
|
||||
if (batch_view.open &&
|
||||
this->last_selected_batch == static_cast<int>(batch_id)) {
|
||||
batch_view.open = false;
|
||||
} else {
|
||||
this->last_selected_batch = static_cast<int>(batch_id);
|
||||
batch_view.SetData(data, name, batch_id);
|
||||
if (!batch_view.open || !batch_view.moved) {
|
||||
batch_view.open = true;
|
||||
const auto pos = GetItemRectMax() + ImVec2{5.0f, 0.0f};
|
||||
batch_view.SetPos(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool show_batch_content = true;
|
||||
|
||||
if (group_batches && !ignore_header) {
|
||||
show_batch_content =
|
||||
CollapsingHeader(batch_hdr, ImGuiTreeNodeFlags_AllowOverlap);
|
||||
SameLine(GetContentRegionAvail().x - 40.0f);
|
||||
const char* text =
|
||||
last_selected_batch == static_cast<int>(batch_id) && batch_view.open ? "X"
|
||||
: "->";
|
||||
if (Button(text, {40.0f, 0.0f})) {
|
||||
open_batch_view();
|
||||
}
|
||||
}
|
||||
|
||||
if (show_batch_content) {
|
||||
auto processed_size = 0ull;
|
||||
auto bb = ctx.LastItemData.Rect;
|
||||
if (group_batches && !ignore_header) {
|
||||
Indent();
|
||||
}
|
||||
auto const batch_sz = batch.end_addr - batch.start_addr;
|
||||
|
||||
while (processed_size < batch_sz) {
|
||||
AmdGpu::PM4ItOpcode op{0xFFu};
|
||||
|
||||
if (pm4_hdr->type == AmdGpu::PM4Type3Header::TYPE) {
|
||||
auto const* pm4_t3 =
|
||||
reinterpret_cast<AmdGpu::PM4Type3Header const*>(pm4_hdr);
|
||||
op = pm4_t3->opcode;
|
||||
|
||||
char header_name[128];
|
||||
sprintf(header_name, "%08llX: %s",
|
||||
cmdb_addr + batch.start_addr + processed_size,
|
||||
Gcn::GetOpCodeName((u32)op));
|
||||
|
||||
bool open_pm4 = TreeNode(header_name);
|
||||
if (!group_batches) {
|
||||
if (IsDrawCall(op)) {
|
||||
SameLine(GetContentRegionAvail().x - 40.0f);
|
||||
const char* text =
|
||||
last_selected_batch == static_cast<int>(batch_id) &&
|
||||
batch_view.open
|
||||
? "X"
|
||||
: "->";
|
||||
if (Button(text, {40.0f, 0.0f})) {
|
||||
open_batch_view();
|
||||
}
|
||||
}
|
||||
if (IsItemHovered() && ctx.IO.KeyShift) {
|
||||
if (BeginTooltip()) {
|
||||
Text("Batch %d", batch_id);
|
||||
EndTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (open_pm4) {
|
||||
if (IsItemToggledOpen()) {
|
||||
// Editor
|
||||
parent->cmdb_view.GotoAddrAndHighlight(
|
||||
cmdb_view.GotoAddrAndHighlight(
|
||||
reinterpret_cast<size_t>(pm4_hdr) - cmdb_addr,
|
||||
reinterpret_cast<size_t>(pm4_hdr) - cmdb_addr +
|
||||
(pm4_hdr->count + 2) * 4);
|
||||
}
|
||||
|
||||
auto const* it_body =
|
||||
reinterpret_cast<uint32_t const*>(pm4_hdr + 1);
|
||||
if (BeginTable("split", 1)) {
|
||||
TableNextColumn();
|
||||
Text("size: %d", pm4_hdr->count + 1);
|
||||
|
||||
switch (op) {
|
||||
case AmdGpu::PM4ItOpcode::Nop: {
|
||||
OnNop(pm4_t3, it_body);
|
||||
break;
|
||||
}
|
||||
case AmdGpu::PM4ItOpcode::SetBase: {
|
||||
OnSetBase(pm4_t3, it_body);
|
||||
break;
|
||||
}
|
||||
case AmdGpu::PM4ItOpcode::SetContextReg: {
|
||||
OnSetContextReg(pm4_t3, it_body);
|
||||
break;
|
||||
}
|
||||
case AmdGpu::PM4ItOpcode::SetShReg: {
|
||||
OnSetShReg(pm4_t3, it_body);
|
||||
break;
|
||||
}
|
||||
case AmdGpu::PM4ItOpcode::DispatchDirect: {
|
||||
OnDispatch(pm4_t3, it_body);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
auto const* payload = &it_body[0];
|
||||
for (unsigned i = 0; i < pm4_hdr->count + 1; ++i) {
|
||||
Text("%02X: %08X", i, payload[i]);
|
||||
auto const* it_body =
|
||||
reinterpret_cast<uint32_t const*>(pm4_hdr + 1);
|
||||
|
||||
switch (op) {
|
||||
case AmdGpu::PM4ItOpcode::Nop: {
|
||||
OnNop(pm4_t3, it_body);
|
||||
break;
|
||||
}
|
||||
case AmdGpu::PM4ItOpcode::SetBase: {
|
||||
OnSetBase(pm4_t3, it_body);
|
||||
break;
|
||||
}
|
||||
case AmdGpu::PM4ItOpcode::SetContextReg: {
|
||||
OnSetContextReg(pm4_t3, it_body);
|
||||
break;
|
||||
}
|
||||
case AmdGpu::PM4ItOpcode::SetShReg: {
|
||||
OnSetShReg(pm4_t3, it_body);
|
||||
break;
|
||||
}
|
||||
case AmdGpu::PM4ItOpcode::DispatchDirect: {
|
||||
OnDispatch(pm4_t3, it_body);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
auto const* payload = &it_body[0];
|
||||
for (unsigned i = 0; i < pm4_hdr->count + 1; ++i) {
|
||||
Text("%02X: %08X", i, payload[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EndTable();
|
||||
EndTable();
|
||||
}
|
||||
TreePop();
|
||||
}
|
||||
TreePop();
|
||||
|
||||
} else {
|
||||
Text("<UNK PACKET>");
|
||||
}
|
||||
} else {
|
||||
Text("<UNK PACKET>");
|
||||
|
||||
auto const* next_pm4_hdr = GetNext(pm4_hdr, 1);
|
||||
auto const processed_len = reinterpret_cast<uintptr_t>(next_pm4_hdr) -
|
||||
reinterpret_cast<uintptr_t>(pm4_hdr);
|
||||
pm4_hdr = next_pm4_hdr;
|
||||
processed_size += processed_len;
|
||||
}
|
||||
|
||||
auto const* next_pm4_hdr = GetNext(pm4_hdr, 1);
|
||||
auto const processed_len = reinterpret_cast<uintptr_t>(next_pm4_hdr) -
|
||||
reinterpret_cast<uintptr_t>(pm4_hdr);
|
||||
pm4_hdr = next_pm4_hdr;
|
||||
processed_size += processed_len;
|
||||
if (group_batches && !ignore_header) {
|
||||
Unindent();
|
||||
};
|
||||
bb = {{0.0f, bb.Max.y}, ctx.LastItemData.Rect.Max};
|
||||
if (bb.Contains(ctx.IO.MousePos)) {
|
||||
current_highlight_batch = batch.id;
|
||||
}
|
||||
}
|
||||
|
||||
if (batch.id == highlight_batch) {
|
||||
PopStyleColor();
|
||||
}
|
||||
|
||||
if (batch.id == batch_bp) {
|
||||
PopStyleColor();
|
||||
}
|
||||
|
||||
if (batch.id == last_batch_id) {
|
||||
Separator();
|
||||
}
|
||||
}
|
||||
PopID();
|
||||
|
||||
if (batch_id == batch_bp) {
|
||||
PopStyleColor();
|
||||
}
|
||||
|
||||
if (batch_id == batches.size() - 2) {
|
||||
Separator();
|
||||
}
|
||||
highlight_batch = current_highlight_batch;
|
||||
}
|
||||
}
|
||||
EndChild();
|
||||
PopID();
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
@ -5,10 +5,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "common/types.h"
|
||||
#include "video_core/buffer_cache/buffer_cache.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "reg_view.h"
|
||||
|
||||
namespace AmdGpu {
|
||||
union PM4Type3Header;
|
||||
@ -19,45 +23,54 @@ namespace Core::Devtools::Widget {
|
||||
|
||||
class FrameDumpViewer;
|
||||
|
||||
class CmdListViewer {
|
||||
/*
|
||||
* Generic PM4 header
|
||||
*/
|
||||
union PM4Header {
|
||||
struct {
|
||||
u32 reserved : 16;
|
||||
u32 count : 14;
|
||||
u32 type : 2; // PM4_TYPE
|
||||
};
|
||||
u32 u32All;
|
||||
};
|
||||
struct BatchInfo {
|
||||
std::string marker{};
|
||||
size_t start_addr;
|
||||
size_t end_addr;
|
||||
size_t command_addr;
|
||||
AmdGpu::PM4ItOpcode type;
|
||||
bool bypass{false};
|
||||
};
|
||||
void ParsePolygonControl(u32 value, bool begin_table = true);
|
||||
void ParseAaConfig(u32 value, bool begin_table = true);
|
||||
void ParseViewportControl(u32 value, bool begin_table = true);
|
||||
void ParseColorControl(u32 value, bool begin_table = true);
|
||||
void ParseColor0Info(u32 value, bool begin_table = true);
|
||||
void ParseColor0Attrib(u32 value, bool begin_table = true);
|
||||
void ParseBlendControl(u32 value, bool begin_table = true);
|
||||
void ParseDepthRenderControl(u32 value, bool begin_table = true);
|
||||
void ParseDepthControl(u32 value, bool begin_table = true);
|
||||
void ParseEqaa(u32 value, bool begin_table = true);
|
||||
void ParseZInfo(u32 value, bool begin_table = true);
|
||||
|
||||
FrameDumpViewer* parent;
|
||||
std::vector<BatchInfo> batches{};
|
||||
class CmdListViewer {
|
||||
|
||||
DebugStateType::FrameDump* frame_dump;
|
||||
|
||||
uintptr_t base_addr;
|
||||
std::string name;
|
||||
std::vector<GPUEvent> events{};
|
||||
uintptr_t cmdb_addr;
|
||||
size_t cmdb_size;
|
||||
|
||||
std::string cmdb_view_name;
|
||||
MemoryEditor cmdb_view;
|
||||
|
||||
int batch_bp{-1};
|
||||
int vqid{255};
|
||||
u32 highlight_batch{~0u};
|
||||
|
||||
void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
RegView batch_view;
|
||||
int last_selected_batch{-1};
|
||||
|
||||
std::vector<RegView> extra_batch_view;
|
||||
|
||||
static void OnNop(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
static void OnSetBase(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
static void OnSetContextReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
static void OnSetShReg(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
static void OnDispatch(AmdGpu::PM4Type3Header const* header, u32 const* body);
|
||||
|
||||
public:
|
||||
explicit CmdListViewer(FrameDumpViewer* parent, const std::vector<u32>& cmd_list);
|
||||
static void LoadConfig(const char* line);
|
||||
static void SerializeConfig(ImGuiTextBuffer* buf);
|
||||
|
||||
void Draw();
|
||||
explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector<u32>& cmd_list,
|
||||
uintptr_t base_addr = 0, std::string name = "");
|
||||
|
||||
void Draw(bool only_batches_view = false);
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
||||
|
109
src/core/devtools/widget/common.h
Normal file
109
src/core/devtools/widget/common.h
Normal file
@ -0,0 +1,109 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/types.h"
|
||||
#include "video_core/amdgpu/pm4_opcodes.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
/*
|
||||
* Generic PM4 header
|
||||
*/
|
||||
union PM4Header {
|
||||
struct {
|
||||
u32 reserved : 16;
|
||||
u32 count : 14;
|
||||
u32 type : 2; // PM4_TYPE
|
||||
};
|
||||
u32 u32All;
|
||||
};
|
||||
|
||||
struct PushMarker {
|
||||
std::string name{};
|
||||
};
|
||||
|
||||
struct PopMarker {};
|
||||
|
||||
struct BatchBegin {
|
||||
u32 id;
|
||||
};
|
||||
|
||||
struct BatchInfo {
|
||||
u32 id;
|
||||
std::string marker{};
|
||||
size_t start_addr;
|
||||
size_t end_addr;
|
||||
size_t command_addr;
|
||||
AmdGpu::PM4ItOpcode type;
|
||||
bool bypass{false};
|
||||
};
|
||||
|
||||
using GPUEvent = std::variant<PushMarker, PopMarker, BatchBegin, BatchInfo>;
|
||||
|
||||
template <typename... Args>
|
||||
void DrawRow(const char* text, const char* fmt, Args... args) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(text);
|
||||
ImGui::TableNextColumn();
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), fmt, args...);
|
||||
ImGui::TextUnformatted(buf);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DrawValueRow(const char* text, T value) {
|
||||
if constexpr (std::is_enum_v<T>) {
|
||||
return DrawRow(text, "%X (%s)", value, magic_enum::enum_name(value).data());
|
||||
} else if constexpr (std::is_integral_v<T>) {
|
||||
return DrawRow(text, "%X", value);
|
||||
} else if constexpr (std::is_base_of_v<BitField<T::position, T::bits, typename T::Type>, T>) {
|
||||
return DrawValueRow(text, value.Value());
|
||||
} else {
|
||||
static_assert(false, "Unsupported type");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V, typename... Extra>
|
||||
void DrawValueRowList(const char* text, V arg, Extra&&... extra_args) {
|
||||
DrawValueRow(text, arg);
|
||||
if constexpr (sizeof...(extra_args) > 0) {
|
||||
DrawValueRowList(std::forward<Extra>(extra_args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static void DoTooltip(const char* str_id, Args&&... args) {
|
||||
if (ImGui::BeginTooltip()) {
|
||||
if (ImGui::BeginTable(str_id, 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
DrawMultipleRow(std::forward<Args>(args)...);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
|
||||
using AmdGpu::PM4ItOpcode;
|
||||
switch (opcode) {
|
||||
case PM4ItOpcode::DrawIndex2:
|
||||
case PM4ItOpcode::DrawIndexOffset2:
|
||||
case PM4ItOpcode::DrawIndexAuto:
|
||||
case PM4ItOpcode::DrawIndirect:
|
||||
case PM4ItOpcode::DrawIndexIndirect:
|
||||
case PM4ItOpcode::DispatchDirect:
|
||||
case PM4ItOpcode::DispatchIndirect:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
@ -7,6 +7,7 @@
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "common/io_file.h"
|
||||
#include "core/devtools/options.h"
|
||||
#include "frame_dump.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
@ -36,7 +37,8 @@ static std::array<char, 3> small_int_to_str(const s32 i) {
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
FrameDumpViewer::FrameDumpViewer(FrameDump _frame_dump) : frame_dump(std::move(_frame_dump)) {
|
||||
FrameDumpViewer::FrameDumpViewer(const FrameDump& _frame_dump)
|
||||
: frame_dump(std::make_shared<FrameDump>(_frame_dump)) {
|
||||
static int unique_id = 0;
|
||||
id = unique_id++;
|
||||
|
||||
@ -44,33 +46,54 @@ FrameDumpViewer::FrameDumpViewer(FrameDump _frame_dump) : frame_dump(std::move(_
|
||||
selected_submit_num = 0;
|
||||
selected_queue_num2 = 0;
|
||||
|
||||
cmd_list_viewer.reserve(frame_dump.queues.size());
|
||||
for (const auto& cmd : frame_dump.queues) {
|
||||
cmd_list_viewer.emplace_back(this, cmd.data);
|
||||
if (cmd.type == QueueType::dcb && cmd.submit_num == selected_submit_num &&
|
||||
cmd.num2 == selected_queue_num2) {
|
||||
selected_cmd = cmd_list_viewer.size() - 1;
|
||||
has_queue_type.fill(false);
|
||||
cmd_list_viewer.reserve(frame_dump->queues.size());
|
||||
for (const auto& cmd : frame_dump->queues) {
|
||||
if (!cmd.data.empty()) {
|
||||
has_queue_type[static_cast<s32>(cmd.type)] = true;
|
||||
}
|
||||
const auto fname = fmt::format("F{} {}_{:02}_{:02}", frame_dump->frame_id,
|
||||
magic_enum::enum_name(cmd.type), cmd.submit_num, cmd.num2);
|
||||
cmd_list_viewer.emplace_back(frame_dump.get(), cmd.data, cmd.base_addr, fname);
|
||||
if (cmd.type == QueueType::dcb && cmd.submit_num == 0 && cmd.num2 == 0) {
|
||||
selected_cmd = static_cast<s32>(cmd_list_viewer.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
cmdb_view.Open = false;
|
||||
cmdb_view.ReadOnly = true;
|
||||
}
|
||||
|
||||
FrameDumpViewer::~FrameDumpViewer() {}
|
||||
FrameDumpViewer::~FrameDumpViewer() = default;
|
||||
|
||||
void FrameDumpViewer::Draw() {
|
||||
if (!is_open) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto try_select = [&, this] {
|
||||
const auto it = std::ranges::find_if(frame_dump->queues, [&](const auto& cmd) {
|
||||
return cmd.type == selected_queue_type &&
|
||||
(selected_submit_num == -1 || cmd.submit_num == selected_submit_num) &&
|
||||
(selected_queue_num2 == -1 || cmd.num2 == selected_queue_num2);
|
||||
});
|
||||
if (it != frame_dump->queues.end()) {
|
||||
selected_cmd = static_cast<s32>(std::distance(frame_dump->queues.begin(), it));
|
||||
selected_submit_num = static_cast<s32>(frame_dump->queues[selected_cmd].submit_num);
|
||||
selected_queue_num2 = static_cast<s32>(frame_dump->queues[selected_cmd].num2);
|
||||
}
|
||||
};
|
||||
|
||||
bool is_showing = Options.frame_dump_render_on_collapse;
|
||||
bool is_collapsed = true;
|
||||
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "Frame #%d dump", id);
|
||||
static ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
||||
SetNextWindowDockID(dock_id, ImGuiCond_Appearing);
|
||||
snprintf(name, sizeof(name), "Frame #%d dump", frame_dump->frame_id);
|
||||
if (Begin(name, &is_open, ImGuiWindowFlags_NoSavedSettings)) {
|
||||
is_showing = true;
|
||||
is_collapsed = false;
|
||||
|
||||
if (IsWindowAppearing()) {
|
||||
auto window = GetCurrentWindow();
|
||||
static ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
||||
SetWindowDock(window, dock_id, ImGuiCond_Once | ImGuiCond_FirstUseEver);
|
||||
SetWindowSize(window, ImVec2{470.0f, 600.0f});
|
||||
}
|
||||
BeginGroup();
|
||||
@ -79,23 +102,44 @@ void FrameDumpViewer::Draw() {
|
||||
if (BeginCombo("##select_queue_type", magic_enum::enum_name(selected_queue_type).data(),
|
||||
ImGuiComboFlags_WidthFitPreview)) {
|
||||
bool selected = false;
|
||||
#define COMBO(x) C_V(magic_enum::enum_name(x).data(), x, selected_queue_type, selected)
|
||||
COMBO(QueueType::acb)
|
||||
#define COMBO(x) \
|
||||
if (has_queue_type[static_cast<s32>(x)]) \
|
||||
C_V(magic_enum::enum_name(x).data(), x, selected_queue_type, selected)
|
||||
COMBO(QueueType::dcb);
|
||||
COMBO(QueueType::ccb);
|
||||
COMBO(QueueType::acb);
|
||||
if (selected) {
|
||||
selected_submit_num = selected_queue_num2 = -1;
|
||||
try_select();
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
SameLine();
|
||||
BeginDisabled(selected_cmd == -1);
|
||||
if (SmallButton("Dump cmd")) {
|
||||
auto now_time = fmt::localtime(std::time(nullptr));
|
||||
const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time,
|
||||
magic_enum::enum_name(selected_queue_type),
|
||||
selected_submit_num, selected_queue_num2);
|
||||
Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write);
|
||||
const auto& data = frame_dump->queues[selected_cmd].data;
|
||||
if (file.IsOpen()) {
|
||||
DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname));
|
||||
file.Write(data);
|
||||
} else {
|
||||
DebugState.ShowDebugMessage(fmt::format("Failed to save {}", fname));
|
||||
LOG_ERROR(Core, "Failed to open file {}", fname);
|
||||
}
|
||||
}
|
||||
EndDisabled();
|
||||
|
||||
TextEx("Submit num");
|
||||
SameLine();
|
||||
if (BeginCombo("##select_submit_num", small_int_to_str(selected_submit_num).data(),
|
||||
ImGuiComboFlags_WidthFitPreview)) {
|
||||
std::array<bool, 32> available_submits{};
|
||||
for (const auto& cmd : frame_dump.queues) {
|
||||
if (cmd.type == selected_queue_type) {
|
||||
std::array<bool, 32> available_submits{false};
|
||||
for (const auto& cmd : frame_dump->queues) {
|
||||
if (cmd.type == selected_queue_type && !cmd.data.empty()) {
|
||||
available_submits[cmd.submit_num] = true;
|
||||
}
|
||||
}
|
||||
@ -110,6 +154,7 @@ void FrameDumpViewer::Draw() {
|
||||
}
|
||||
if (selected) {
|
||||
selected_queue_num2 = -1;
|
||||
try_select();
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
@ -118,9 +163,10 @@ void FrameDumpViewer::Draw() {
|
||||
SameLine();
|
||||
if (BeginCombo("##select_queue_num2", small_int_to_str(selected_queue_num2).data(),
|
||||
ImGuiComboFlags_WidthFitPreview)) {
|
||||
std::array<bool, 32> available_queues{};
|
||||
for (const auto& cmd : frame_dump.queues) {
|
||||
if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num) {
|
||||
std::array<bool, 32> available_queues{false};
|
||||
for (const auto& cmd : frame_dump->queues) {
|
||||
if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num &&
|
||||
!cmd.data.empty()) {
|
||||
available_queues[cmd.num2] = true;
|
||||
}
|
||||
}
|
||||
@ -134,56 +180,16 @@ void FrameDumpViewer::Draw() {
|
||||
}
|
||||
}
|
||||
if (selected) {
|
||||
const auto it = std::ranges::find_if(frame_dump.queues, [&](const auto& cmd) {
|
||||
return cmd.type == selected_queue_type &&
|
||||
cmd.submit_num == selected_submit_num && cmd.num2 == selected_queue_num2;
|
||||
});
|
||||
if (it != frame_dump.queues.end()) {
|
||||
selected_cmd = std::distance(frame_dump.queues.begin(), it);
|
||||
}
|
||||
try_select();
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
SameLine();
|
||||
BeginDisabled(selected_cmd == -1);
|
||||
if (SmallButton("Dump cmd")) {
|
||||
auto now_time = fmt::localtime(std::time(nullptr));
|
||||
const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time,
|
||||
magic_enum::enum_name(selected_queue_type),
|
||||
selected_submit_num, selected_queue_num2);
|
||||
Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write);
|
||||
auto& data = frame_dump.queues[selected_cmd].data;
|
||||
if (file.IsOpen()) {
|
||||
DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname));
|
||||
file.Write(data);
|
||||
} else {
|
||||
DebugState.ShowDebugMessage(fmt::format("Failed to save {}", fname));
|
||||
LOG_ERROR(Core, "Failed to open file {}", fname);
|
||||
}
|
||||
}
|
||||
EndDisabled();
|
||||
EndGroup();
|
||||
|
||||
if (selected_cmd != -1) {
|
||||
cmd_list_viewer[selected_cmd].Draw();
|
||||
}
|
||||
}
|
||||
if (is_showing && selected_cmd != -1) {
|
||||
cmd_list_viewer[selected_cmd].Draw(is_collapsed);
|
||||
}
|
||||
End();
|
||||
|
||||
if (cmdb_view.Open && selected_cmd != -1) {
|
||||
auto& cmd = frame_dump.queues[selected_cmd].data;
|
||||
auto cmd_size = cmd.size() * sizeof(u32);
|
||||
MemoryEditor::Sizes s;
|
||||
cmdb_view.CalcSizes(s, cmd_size, (size_t)cmd.data());
|
||||
SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));
|
||||
|
||||
char name[64];
|
||||
snprintf(name, sizeof(name), "[GFX] Command buffer %d###cmdbuf_hex_%d", id, id);
|
||||
if (Begin(name, &cmdb_view.Open, ImGuiWindowFlags_NoScrollbar)) {
|
||||
cmdb_view.DrawContents(cmd.data(), cmd_size, (size_t)cmd.data());
|
||||
}
|
||||
End();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#include "cmd_list.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
@ -17,11 +16,11 @@ class CmdListViewer;
|
||||
class FrameDumpViewer {
|
||||
friend class CmdListViewer;
|
||||
|
||||
DebugStateType::FrameDump frame_dump;
|
||||
std::shared_ptr<DebugStateType::FrameDump> frame_dump;
|
||||
int id;
|
||||
|
||||
std::vector<CmdListViewer> cmd_list_viewer;
|
||||
MemoryEditor cmdb_view;
|
||||
std::array<bool, 3> has_queue_type;
|
||||
|
||||
DebugStateType::QueueType selected_queue_type;
|
||||
s32 selected_submit_num;
|
||||
@ -31,7 +30,7 @@ class FrameDumpViewer {
|
||||
public:
|
||||
bool is_open = true;
|
||||
|
||||
explicit FrameDumpViewer(DebugStateType::FrameDump frame_dump);
|
||||
explicit FrameDumpViewer(const DebugStateType::FrameDump& frame_dump);
|
||||
|
||||
~FrameDumpViewer();
|
||||
|
||||
|
192
src/core/devtools/widget/reg_popup.cpp
Normal file
192
src/core/devtools/widget/reg_popup.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "reg_popup.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "cmd_list.h"
|
||||
#include "common.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
|
||||
using namespace ImGui;
|
||||
using magic_enum::enum_name;
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) {
|
||||
if (BeginTable("COLOR_BUFFER", 2, ImGuiTableFlags_Borders)) {
|
||||
TableNextRow();
|
||||
|
||||
// clang-format off
|
||||
|
||||
DrawValueRowList(
|
||||
"BASE_ADDR", buffer.base_address,
|
||||
"PITCH.TILE_MAX", buffer.pitch.tile_max,
|
||||
"PITCH.FMASK_TILE_MAX", buffer.pitch.fmask_tile_max,
|
||||
"SLICE.TILE_MAX", buffer.slice.tile_max,
|
||||
"VIEW.SLICE_START", buffer.view.slice_start,
|
||||
"VIEW.SLICE_MAX", buffer.view.slice_max
|
||||
);
|
||||
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
if (TreeNode("Color0Info")) {
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
ParseColor0Info(buffer.info.u32all, false);
|
||||
TreePop();
|
||||
}
|
||||
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
if (TreeNode("Color0Attrib")) {
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
ParseColor0Attrib(buffer.attrib.u32all, false);
|
||||
TreePop();
|
||||
}
|
||||
|
||||
TableNextRow();
|
||||
DrawValueRowList(
|
||||
"CMASK_BASE_EXT", buffer.cmask_base_address,
|
||||
"FMASK_BASE_EXT", buffer.fmask_base_address,
|
||||
"FMASK_SLICE.TILE_MAX", buffer.fmask_slice.tile_max,
|
||||
"CLEAR_WORD0", buffer.clear_word0,
|
||||
"CLEAR_WORD1", buffer.clear_word1,
|
||||
"Pitch()", buffer.Pitch(),
|
||||
"Height()", buffer.Height(),
|
||||
"Address()", buffer.Address(),
|
||||
"CmaskAddress", buffer.CmaskAddress(),
|
||||
"FmaskAddress", buffer.FmaskAddress(),
|
||||
"NumSamples()", buffer.NumSamples(),
|
||||
"NumSlices()", buffer.NumSlices(),
|
||||
"GetColorSliceSize()", buffer.GetColorSliceSize(),
|
||||
"GetTilingMode()", buffer.GetTilingMode(),
|
||||
"IsTiled()", buffer.IsTiled(),
|
||||
"NumFormat()", buffer.NumFormat()
|
||||
);
|
||||
|
||||
// clang-format on
|
||||
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) {
|
||||
const auto& [depth_buffer, depth_control] = depth_data;
|
||||
|
||||
SeparatorText("Depth buffer");
|
||||
|
||||
if (BeginTable("DEPTH_BUFFER", 2, ImGuiTableFlags_Borders)) {
|
||||
TableNextRow();
|
||||
|
||||
// clang-format off
|
||||
DrawValueRowList(
|
||||
"Z_INFO.FORMAT", depth_buffer.z_info.format,
|
||||
"Z_INFO.NUM_SAMPLES", depth_buffer.z_info.num_samples,
|
||||
"Z_INFO.TILE_SPLIT", depth_buffer.z_info.tile_split,
|
||||
"Z_INFO.TILE_MODE_INDEX", depth_buffer.z_info.tile_mode_index,
|
||||
"Z_INFO.DECOMPRESS_ON_N_ZPLANES", depth_buffer.z_info.decompress_on_n_zplanes,
|
||||
"Z_INFO.ALLOW_EXPCLEAR", depth_buffer.z_info.allow_expclear,
|
||||
"Z_INFO.READ_SIZE", depth_buffer.z_info.read_size,
|
||||
"Z_INFO.TILE_SURFACE_EN", depth_buffer.z_info.tile_surface_en,
|
||||
"Z_INFO.CLEAR_DISALLOWED", depth_buffer.z_info.clear_disallowed,
|
||||
"Z_INFO.ZRANGE_PRECISION", depth_buffer.z_info.zrange_precision,
|
||||
"STENCIL_INFO.FORMAT", depth_buffer.stencil_info.format,
|
||||
"Z_READ_BASE", depth_buffer.z_read_base,
|
||||
"STENCIL_READ_BASE", depth_buffer.stencil_read_base,
|
||||
"Z_WRITE_BASE", depth_buffer.z_write_base,
|
||||
"STENCIL_WRITE_BASE", depth_buffer.stencil_write_base,
|
||||
"DEPTH_SIZE.PITCH_TILE_MAX", depth_buffer.depth_size.pitch_tile_max,
|
||||
"DEPTH_SIZE.HEIGHT_TILE_MAX", depth_buffer.depth_size.height_tile_max,
|
||||
"DEPTH_SLICE.TILE_MAX", depth_buffer.depth_slice.tile_max,
|
||||
"Pitch()", depth_buffer.Pitch(),
|
||||
"Height()", depth_buffer.Height(),
|
||||
"Address()", depth_buffer.Address(),
|
||||
"NumSamples()", depth_buffer.NumSamples(),
|
||||
"NumBits()", depth_buffer.NumBits(),
|
||||
"GetDepthSliceSize()", depth_buffer.GetDepthSliceSize()
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
EndTable();
|
||||
}
|
||||
SeparatorText("Depth control");
|
||||
if (BeginTable("DEPTH_CONTROL", 2, ImGuiTableFlags_Borders)) {
|
||||
TableNextRow();
|
||||
|
||||
// clang-format off
|
||||
DrawValueRowList(
|
||||
"STENCIL_ENABLE", depth_control.stencil_enable,
|
||||
"DEPTH_ENABLE", depth_control.depth_enable,
|
||||
"DEPTH_WRITE_ENABLE", depth_control.depth_write_enable,
|
||||
"DEPTH_BOUNDS_ENABLE", depth_control.depth_bounds_enable,
|
||||
"DEPTH_FUNC", depth_control.depth_func,
|
||||
"BACKFACE_ENABLE", depth_control.backface_enable,
|
||||
"STENCIL_FUNC", depth_control.stencil_ref_func,
|
||||
"STENCIL_FUNC_BF", depth_control.stencil_bf_func,
|
||||
"ENABLE_COLOR_WRITES_ON_DEPTH_FAIL", depth_control.enable_color_writes_on_depth_fail,
|
||||
"DISABLE_COLOR_WRITES_ON_DEPTH_PASS", depth_control.disable_color_writes_on_depth_pass
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
RegPopup::RegPopup() {
|
||||
static int unique_id = 0;
|
||||
id = unique_id++;
|
||||
}
|
||||
|
||||
void RegPopup::SetData(const std::string& base_title, AmdGpu::Liverpool::ColorBuffer color_buffer,
|
||||
u32 cb_id) {
|
||||
this->data = color_buffer;
|
||||
this->title = fmt::format("{}/CB #{}", base_title, cb_id);
|
||||
}
|
||||
|
||||
void RegPopup::SetData(const std::string& base_title, AmdGpu::Liverpool::DepthBuffer depth_buffer,
|
||||
AmdGpu::Liverpool::DepthControl depth_control) {
|
||||
this->data = std::make_tuple(depth_buffer, depth_control);
|
||||
this->title = fmt::format("{}/Depth", base_title);
|
||||
}
|
||||
|
||||
void RegPopup::SetPos(ImVec2 pos, bool auto_resize) {
|
||||
char name[128];
|
||||
snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id);
|
||||
Begin(name, &open, flags);
|
||||
SetWindowPos(pos);
|
||||
if (auto_resize) {
|
||||
if (std::holds_alternative<AmdGpu::Liverpool::ColorBuffer>(data)) {
|
||||
SetWindowSize({365.0f, 520.0f});
|
||||
KeepWindowInside();
|
||||
} else if (std::holds_alternative<DepthBuffer>(data)) {
|
||||
SetWindowSize({404.0f, 543.0f});
|
||||
KeepWindowInside();
|
||||
}
|
||||
}
|
||||
last_pos = GetWindowPos();
|
||||
moved = false;
|
||||
End();
|
||||
}
|
||||
|
||||
void RegPopup::Draw() {
|
||||
char name[128];
|
||||
snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id);
|
||||
if (Begin(name, &open, flags)) {
|
||||
if (GetWindowPos() != last_pos) {
|
||||
moved = true;
|
||||
}
|
||||
|
||||
if (const auto* buffer = std::get_if<AmdGpu::Liverpool::ColorBuffer>(&data)) {
|
||||
DrawColorBuffer(*buffer);
|
||||
} else if (const auto* depth_data = std::get_if<DepthBuffer>(&data)) {
|
||||
DrawDepthBuffer(*depth_data);
|
||||
}
|
||||
}
|
||||
End();
|
||||
}
|
||||
} // namespace Core::Devtools::Widget
|
46
src/core/devtools/widget/reg_popup.h
Normal file
46
src/core/devtools/widget/reg_popup.h
Normal file
@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class RegPopup {
|
||||
int id;
|
||||
ImGuiWindowFlags flags{ImGuiWindowFlags_NoSavedSettings};
|
||||
|
||||
using DepthBuffer = std::tuple<AmdGpu::Liverpool::DepthBuffer, AmdGpu::Liverpool::DepthControl>;
|
||||
|
||||
ImVec2 last_pos;
|
||||
std::variant<AmdGpu::Liverpool::ColorBuffer, DepthBuffer> data;
|
||||
std::string title{};
|
||||
|
||||
static void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer);
|
||||
|
||||
static void DrawDepthBuffer(const DepthBuffer& depth_data);
|
||||
|
||||
public:
|
||||
bool open = false;
|
||||
bool moved = false;
|
||||
|
||||
RegPopup();
|
||||
|
||||
void SetData(const std::string& base_title, AmdGpu::Liverpool::ColorBuffer color_buffer,
|
||||
u32 cb_id);
|
||||
|
||||
void SetData(const std::string& base_title, AmdGpu::Liverpool::DepthBuffer depth_buffer,
|
||||
AmdGpu::Liverpool::DepthControl depth_control);
|
||||
|
||||
void SetPos(ImVec2 pos, bool auto_resize = false);
|
||||
|
||||
void Draw();
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
423
src/core/devtools/widget/reg_view.cpp
Normal file
423
src/core/devtools/widget/reg_view.cpp
Normal file
@ -0,0 +1,423 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "common/io_file.h"
|
||||
#include "core/devtools/options.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "reg_view.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
#endif
|
||||
|
||||
using namespace ImGui;
|
||||
using magic_enum::enum_name;
|
||||
|
||||
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 {
|
||||
|
||||
void RegView::ProcessShader(int shader_id) {
|
||||
std::vector<u32> shader_code;
|
||||
Vulkan::Liverpool::UserData user_data;
|
||||
if (data.is_compute) {
|
||||
shader_code = data.cs_data.code;
|
||||
user_data = data.cs_data.cs_program.user_data;
|
||||
} else {
|
||||
const auto& s = data.stages[shader_id];
|
||||
shader_code = s.code;
|
||||
user_data = s.user_data.user_data;
|
||||
}
|
||||
|
||||
std::string shader_dis;
|
||||
|
||||
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;
|
||||
hex_view.Open = true;
|
||||
hex_view.ReadOnly = true;
|
||||
hex_view.Cols = 8;
|
||||
hex_view.OptShowAscii = false;
|
||||
hex_view.OptShowOptions = false;
|
||||
|
||||
TextEditor dis_view;
|
||||
dis_view.SetPalette(TextEditor::GetDarkPalette());
|
||||
dis_view.SetReadOnly(true);
|
||||
dis_view.SetText(shader_dis);
|
||||
|
||||
ShaderCache cache{
|
||||
.hex_view = hex_view,
|
||||
.dis_view = dis_view,
|
||||
.user_data = user_data,
|
||||
};
|
||||
shader_decomp.emplace(shader_id, std::move(cache));
|
||||
}
|
||||
void RegView::SelectShader(int id) {
|
||||
selected_shader = id;
|
||||
if (!shader_decomp.contains(id)) {
|
||||
ProcessShader(id);
|
||||
}
|
||||
}
|
||||
|
||||
void RegView::DrawComputeRegs() {
|
||||
const auto& cs = data.cs_data.cs_program;
|
||||
|
||||
if (BeginTable("CREGS", 2, ImGuiTableFlags_Borders)) {
|
||||
TableNextRow();
|
||||
|
||||
// clang-format off
|
||||
DrawValueRowList(
|
||||
"DISPATCH_INITIATOR", cs.dispatch_initiator,
|
||||
"DIM_X", cs.dim_x,
|
||||
"DIM_Y", cs.dim_y,
|
||||
"DIM_Z", cs.dim_z,
|
||||
"START_X", cs.start_x,
|
||||
"START_Y", cs.start_y,
|
||||
"START_Z", cs.start_z,
|
||||
"NUM_THREAD_X.FULL", cs.num_thread_x.full,
|
||||
"NUM_THREAD_X.PARTIAL", cs.num_thread_x.partial,
|
||||
"NUM_THREAD_Y.FULL", cs.num_thread_y.full,
|
||||
"NUM_THREAD_Y.PARTIAL", cs.num_thread_y.partial,
|
||||
"NUM_THREAD_Z.FULL", cs.num_thread_z.full,
|
||||
"NUM_THREAD_Z.PARTIAL", cs.num_thread_z.partial,
|
||||
"MAX_WAVE_ID", cs.max_wave_id,
|
||||
"SETTINGS.NUM_VGPRS", cs.settings.num_vgprs,
|
||||
"SETTINGS.NUM_SGPRS", cs.settings.num_sgprs,
|
||||
"SETTINGS.NUM_USER_REGS", cs.settings.num_user_regs,
|
||||
"SETTINGS.TGID_ENABLE", cs.settings.tgid_enable,
|
||||
"SETTINGS.LDS_DWORDS", cs.settings.lds_dwords,
|
||||
"RESOURCE_LIMITS", cs.resource_limits
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void RegView::DrawGraphicsRegs() {
|
||||
const auto& regs = data.regs;
|
||||
|
||||
if (BeginTable("REGS", 2, ImGuiTableFlags_Borders)) {
|
||||
TableNextRow();
|
||||
|
||||
DrawValueRow("Primitive type", regs.primitive_type);
|
||||
|
||||
const auto open_new_popup = [&](int cb, auto... args) {
|
||||
const auto pos = GetItemRectMax() + ImVec2(5.0f, 0.0f);
|
||||
if (GetIO().KeyShift) {
|
||||
auto& pop = extra_reg_popup.emplace_back();
|
||||
pop.SetData(title, args...);
|
||||
pop.open = true;
|
||||
pop.SetPos(pos, true);
|
||||
} else if (last_selected_cb == cb && default_reg_popup.open) {
|
||||
default_reg_popup.open = false;
|
||||
} else {
|
||||
last_selected_cb = cb;
|
||||
default_reg_popup.SetData(title, args...);
|
||||
if (!default_reg_popup.open || !default_reg_popup.moved) {
|
||||
default_reg_popup.open = true;
|
||||
default_reg_popup.SetPos(pos, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (int cb = 0; cb < AmdGpu::Liverpool::NumColorBuffers; ++cb) {
|
||||
PushID(cb);
|
||||
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
|
||||
const auto& buffer = regs.color_buffers[cb];
|
||||
|
||||
Text("Color buffer %d", cb);
|
||||
TableNextColumn();
|
||||
if (!buffer || !regs.color_target_mask.GetMask(cb)) {
|
||||
TextUnformatted("N/A");
|
||||
} else {
|
||||
const char* text = last_selected_cb == cb && default_reg_popup.open ? "x" : "->";
|
||||
if (SmallButton(text)) {
|
||||
open_new_popup(cb, buffer, cb);
|
||||
}
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
TextUnformatted("Depth buffer");
|
||||
TableNextColumn();
|
||||
if (regs.depth_buffer.Address() == 0 || !regs.depth_control.depth_enable) {
|
||||
TextUnformatted("N/A");
|
||||
} else {
|
||||
const char* text = last_selected_cb == depth_id && default_reg_popup.open ? "x" : "->";
|
||||
if (SmallButton(text)) {
|
||||
open_new_popup(depth_id, regs.depth_buffer, regs.depth_control);
|
||||
}
|
||||
}
|
||||
|
||||
auto& s = regs.screen_scissor;
|
||||
DrawRow("Scissor", "(%d, %d, %d, %d)", s.top_left_x, s.top_left_y, s.bottom_right_x,
|
||||
s.bottom_right_y);
|
||||
|
||||
DrawValueRow("Color control", regs.color_control.mode);
|
||||
|
||||
DrawRow("Primitive restart", "%X (IDX: %X)", regs.enable_primitive_restart & 1,
|
||||
regs.primitive_restart_index);
|
||||
// clang-format off
|
||||
DrawValueRowList(
|
||||
"Polygon mode", regs.polygon_control.PolyMode(),
|
||||
"Cull mode", regs.polygon_control.CullingMode(),
|
||||
"Clip Space", regs.clipper_control.clip_space,
|
||||
"Front face", regs.polygon_control.front_face,
|
||||
"Num Samples", regs.aa_config.NumSamples()
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
RegView::RegView() {
|
||||
static int unique_id = 0;
|
||||
id = unique_id++;
|
||||
|
||||
char name[128];
|
||||
snprintf(name, sizeof(name), "###reg_dump_%d", id);
|
||||
SetNextWindowPos({400.0f, 200.0f});
|
||||
SetNextWindowSize({290.0f, 435.0f});
|
||||
ImGuiID root_dock_id;
|
||||
Begin(name);
|
||||
{
|
||||
char dock_name[64];
|
||||
snprintf(dock_name, sizeof(dock_name), "BatchView###reg_dump_%d/dock_space", id);
|
||||
root_dock_id = ImHashStr(dock_name);
|
||||
DockSpace(root_dock_id);
|
||||
}
|
||||
End();
|
||||
|
||||
ImGuiID up1, down1;
|
||||
|
||||
DockBuilderRemoveNodeChildNodes(root_dock_id);
|
||||
DockBuilderSplitNode(root_dock_id, ImGuiDir_Up, 0.19f, &up1, &down1);
|
||||
|
||||
snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id);
|
||||
DockBuilderDockWindow(name, up1);
|
||||
|
||||
snprintf(name, sizeof(name), "Regs###reg_dump_%d/regs", id);
|
||||
DockBuilderDockWindow(name, down1);
|
||||
|
||||
snprintf(name, sizeof(name), "Disassembly###reg_dump_%d/disassembly", id);
|
||||
DockBuilderDockWindow(name, down1);
|
||||
|
||||
DockBuilderFinish(root_dock_id);
|
||||
}
|
||||
|
||||
void RegView::SetData(DebugStateType::RegDump _data, const std::string& base_title, u32 batch_id) {
|
||||
this->data = std::move(_data);
|
||||
this->batch_id = batch_id;
|
||||
this->title = fmt::format("{}/Batch {}", base_title, batch_id);
|
||||
// clear cache
|
||||
shader_decomp.clear();
|
||||
if (data.is_compute) {
|
||||
selected_shader = -2;
|
||||
last_selected_cb = -1;
|
||||
default_reg_popup.open = false;
|
||||
ProcessShader(-2);
|
||||
} else {
|
||||
const auto& regs = data.regs;
|
||||
if (selected_shader >= 0 && !regs.stage_enable.IsStageEnabled(selected_shader)) {
|
||||
selected_shader = -1;
|
||||
}
|
||||
if (default_reg_popup.open) {
|
||||
default_reg_popup.open = false;
|
||||
if (last_selected_cb == depth_id) {
|
||||
const auto& has_depth =
|
||||
regs.depth_buffer.Address() != 0 && regs.depth_control.depth_enable;
|
||||
if (has_depth) {
|
||||
default_reg_popup.SetData(title, regs.depth_buffer, regs.depth_control);
|
||||
default_reg_popup.open = true;
|
||||
}
|
||||
} else if (last_selected_cb >= 0 &&
|
||||
last_selected_cb < AmdGpu::Liverpool::NumColorBuffers) {
|
||||
const auto& buffer = regs.color_buffers[last_selected_cb];
|
||||
const bool has_cb = buffer && regs.color_target_mask.GetMask(last_selected_cb);
|
||||
if (has_cb) {
|
||||
default_reg_popup.SetData(title, buffer, last_selected_cb);
|
||||
default_reg_popup.open = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
extra_reg_popup.clear();
|
||||
}
|
||||
|
||||
void RegView::SetPos(ImVec2 pos) {
|
||||
char name[128];
|
||||
snprintf(name, sizeof(name), "%s###reg_dump_%d", title.c_str(), id);
|
||||
Begin(name, &open, ImGuiWindowFlags_MenuBar);
|
||||
SetWindowPos(pos);
|
||||
KeepWindowInside();
|
||||
last_pos = GetWindowPos();
|
||||
moved = false;
|
||||
End();
|
||||
}
|
||||
|
||||
void RegView::Draw() {
|
||||
char name[128];
|
||||
snprintf(name, sizeof(name), "%s###reg_dump_%d", title.c_str(), id);
|
||||
|
||||
if (Begin(name, &open, ImGuiWindowFlags_MenuBar)) {
|
||||
if (GetWindowPos() != last_pos) {
|
||||
moved = true;
|
||||
}
|
||||
|
||||
const char* names[] = {"vs", "ps", "gs", "es", "hs", "ls"};
|
||||
|
||||
if (BeginMenuBar()) {
|
||||
if (BeginMenu("Windows")) {
|
||||
Checkbox("Registers", &show_registers);
|
||||
Checkbox("User data", &show_user_data);
|
||||
Checkbox("Disassembly", &show_disassembly);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
EndMenuBar();
|
||||
}
|
||||
|
||||
if (!data.is_compute &&
|
||||
BeginChild("STAGES", {},
|
||||
ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY)) {
|
||||
for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; i++) {
|
||||
if (data.regs.stage_enable.IsStageEnabled(i)) {
|
||||
const bool selected = selected_shader == i;
|
||||
if (selected) {
|
||||
PushStyleColor(ImGuiCol_Button, ImVec4{1.0f, 0.7f, 0.7f, 1.0f});
|
||||
}
|
||||
if (Button(names[i], {40.0f, 40.0f})) {
|
||||
SelectShader(i);
|
||||
}
|
||||
if (selected) {
|
||||
PopStyleColor();
|
||||
}
|
||||
}
|
||||
SameLine();
|
||||
}
|
||||
EndChild();
|
||||
}
|
||||
}
|
||||
char dock_name[64];
|
||||
snprintf(dock_name, sizeof(dock_name), "BatchView###reg_dump_%d/dock_space", id);
|
||||
auto root_dock_id = ImHashStr(dock_name);
|
||||
DockSpace(root_dock_id);
|
||||
End();
|
||||
|
||||
auto get_shader = [&]() -> ShaderCache* {
|
||||
auto shader_cache = shader_decomp.find(selected_shader);
|
||||
if (shader_cache == shader_decomp.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &shader_cache->second;
|
||||
};
|
||||
|
||||
if (show_user_data) {
|
||||
snprintf(name, sizeof(name), "User data###reg_dump_%d/user_data", id);
|
||||
|
||||
if (Begin(name, &show_user_data, ImGuiWindowFlags_NoScrollbar)) {
|
||||
auto shader = get_shader();
|
||||
if (!shader) {
|
||||
Text("Stage not selected");
|
||||
} else {
|
||||
shader->hex_view.DrawContents(shader->user_data.data(), shader->user_data.size());
|
||||
}
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
if (show_disassembly) {
|
||||
snprintf(name, sizeof(name), "Disassembly###reg_dump_%d/disassembly", id);
|
||||
if (Begin(name, &show_disassembly)) {
|
||||
auto shader = get_shader();
|
||||
if (!shader) {
|
||||
Text("Stage not selected");
|
||||
} else {
|
||||
shader->dis_view.Render("Disassembly", GetContentRegionAvail());
|
||||
}
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
if (show_registers) {
|
||||
snprintf(name, sizeof(name), "Regs###reg_dump_%d/regs", id);
|
||||
if (Begin(name, &show_registers)) {
|
||||
if (data.is_compute) {
|
||||
DrawComputeRegs();
|
||||
} else {
|
||||
DrawGraphicsRegs();
|
||||
}
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
if (default_reg_popup.open) {
|
||||
default_reg_popup.Draw();
|
||||
}
|
||||
for (auto it = extra_reg_popup.begin(); it != extra_reg_popup.end();) {
|
||||
if (!it->open) {
|
||||
it = extra_reg_popup.erase(it);
|
||||
continue;
|
||||
}
|
||||
it->Draw();
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
57
src/core/devtools/widget/reg_view.h
Normal file
57
src/core/devtools/widget/reg_view.h
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include "core/debug_state.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "reg_popup.h"
|
||||
#include "text_editor.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
struct ShaderCache {
|
||||
MemoryEditor hex_view;
|
||||
TextEditor dis_view;
|
||||
Vulkan::Liverpool::UserData user_data;
|
||||
};
|
||||
|
||||
class RegView {
|
||||
int id;
|
||||
|
||||
std::string title;
|
||||
DebugStateType::RegDump data;
|
||||
u32 batch_id{~0u};
|
||||
ImVec2 last_pos;
|
||||
|
||||
std::unordered_map<int, ShaderCache> shader_decomp;
|
||||
int selected_shader{-1};
|
||||
RegPopup default_reg_popup;
|
||||
int last_selected_cb{-1};
|
||||
std::vector<RegPopup> extra_reg_popup;
|
||||
|
||||
bool show_registers{true};
|
||||
bool show_user_data{true};
|
||||
bool show_disassembly{true};
|
||||
|
||||
void ProcessShader(int shader_id);
|
||||
|
||||
void SelectShader(int shader_id);
|
||||
|
||||
void DrawComputeRegs();
|
||||
|
||||
void DrawGraphicsRegs();
|
||||
|
||||
public:
|
||||
bool open = false;
|
||||
bool moved = false;
|
||||
|
||||
RegView();
|
||||
|
||||
void SetData(DebugStateType::RegDump data, const std::string& base_title, u32 batch_id);
|
||||
|
||||
void SetPos(ImVec2 pos);
|
||||
|
||||
void Draw();
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
2334
src/core/devtools/widget/text_editor.cpp
Normal file
2334
src/core/devtools/widget/text_editor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
408
src/core/devtools/widget/text_editor.h
Normal file
408
src/core/devtools/widget/text_editor.h
Normal file
@ -0,0 +1,408 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2017 BalazsJako
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// source: https://github.com/BalazsJako/ImGuiColorTextEdit
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class TextEditor {
|
||||
public:
|
||||
enum class PaletteIndex {
|
||||
Default,
|
||||
Keyword,
|
||||
Number,
|
||||
String,
|
||||
CharLiteral,
|
||||
Punctuation,
|
||||
Preprocessor,
|
||||
Identifier,
|
||||
KnownIdentifier,
|
||||
PreprocIdentifier,
|
||||
Comment,
|
||||
MultiLineComment,
|
||||
Background,
|
||||
Cursor,
|
||||
Selection,
|
||||
ErrorMarker,
|
||||
Breakpoint,
|
||||
LineNumber,
|
||||
CurrentLineFill,
|
||||
CurrentLineFillInactive,
|
||||
CurrentLineEdge,
|
||||
Max
|
||||
};
|
||||
|
||||
enum class SelectionMode { Normal, Word, Line };
|
||||
|
||||
struct Breakpoint {
|
||||
int mLine;
|
||||
bool mEnabled;
|
||||
std::string mCondition;
|
||||
|
||||
Breakpoint() : mLine(-1), mEnabled(false) {}
|
||||
};
|
||||
|
||||
// Represents a character coordinate from the user's point of view,
|
||||
// i. e. consider an uniform grid (assuming fixed-width font) on the
|
||||
// screen as it is rendered, and each cell has its own coordinate, starting from 0.
|
||||
// Tabs are counted as [1..mTabSize] count empty spaces, depending on
|
||||
// how many space is necessary to reach the next tab stop.
|
||||
// For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize
|
||||
// = 4, because it is rendered as " ABC" on the screen.
|
||||
struct Coordinates {
|
||||
int mLine, mColumn;
|
||||
Coordinates() : mLine(0), mColumn(0) {}
|
||||
Coordinates(int aLine, int aColumn) : mLine(aLine), mColumn(aColumn) {
|
||||
ASSERT(aLine >= 0);
|
||||
ASSERT(aColumn >= 0);
|
||||
}
|
||||
static Coordinates Invalid() {
|
||||
static Coordinates invalid(-1, -1);
|
||||
return invalid;
|
||||
}
|
||||
|
||||
bool operator==(const Coordinates& o) const {
|
||||
return mLine == o.mLine && mColumn == o.mColumn;
|
||||
}
|
||||
|
||||
bool operator!=(const Coordinates& o) const {
|
||||
return mLine != o.mLine || mColumn != o.mColumn;
|
||||
}
|
||||
|
||||
bool operator<(const Coordinates& o) const {
|
||||
if (mLine != o.mLine)
|
||||
return mLine < o.mLine;
|
||||
return mColumn < o.mColumn;
|
||||
}
|
||||
|
||||
bool operator>(const Coordinates& o) const {
|
||||
if (mLine != o.mLine)
|
||||
return mLine > o.mLine;
|
||||
return mColumn > o.mColumn;
|
||||
}
|
||||
|
||||
bool operator<=(const Coordinates& o) const {
|
||||
if (mLine != o.mLine)
|
||||
return mLine < o.mLine;
|
||||
return mColumn <= o.mColumn;
|
||||
}
|
||||
|
||||
bool operator>=(const Coordinates& o) const {
|
||||
if (mLine != o.mLine)
|
||||
return mLine > o.mLine;
|
||||
return mColumn >= o.mColumn;
|
||||
}
|
||||
};
|
||||
|
||||
struct Identifier {
|
||||
Coordinates mLocation;
|
||||
std::string mDeclaration;
|
||||
};
|
||||
|
||||
typedef std::string String;
|
||||
typedef std::unordered_map<std::string, Identifier> Identifiers;
|
||||
typedef std::unordered_set<std::string> Keywords;
|
||||
typedef std::map<int, std::string> ErrorMarkers;
|
||||
typedef std::unordered_set<int> Breakpoints;
|
||||
typedef std::array<ImU32, (unsigned)PaletteIndex::Max> Palette;
|
||||
typedef uint8_t Char;
|
||||
|
||||
struct Glyph {
|
||||
Char mChar;
|
||||
PaletteIndex mColorIndex = PaletteIndex::Default;
|
||||
bool mComment : 1;
|
||||
bool mMultiLineComment : 1;
|
||||
bool mPreprocessor : 1;
|
||||
|
||||
Glyph(Char aChar, PaletteIndex aColorIndex)
|
||||
: mChar(aChar), mColorIndex(aColorIndex), mComment(false), mMultiLineComment(false),
|
||||
mPreprocessor(false) {}
|
||||
};
|
||||
|
||||
typedef std::vector<Glyph> Line;
|
||||
typedef std::vector<Line> Lines;
|
||||
|
||||
struct LanguageDefinition {
|
||||
typedef std::pair<std::string, PaletteIndex> TokenRegexString;
|
||||
typedef std::vector<TokenRegexString> TokenRegexStrings;
|
||||
typedef bool (*TokenizeCallback)(const char* in_begin, const char* in_end,
|
||||
const char*& out_begin, const char*& out_end,
|
||||
PaletteIndex& paletteIndex);
|
||||
|
||||
std::string mName;
|
||||
Keywords mKeywords;
|
||||
Identifiers mIdentifiers;
|
||||
Identifiers mPreprocIdentifiers;
|
||||
std::string mCommentStart, mCommentEnd, mSingleLineComment;
|
||||
char mPreprocChar;
|
||||
bool mAutoIndentation;
|
||||
|
||||
TokenizeCallback mTokenize;
|
||||
|
||||
TokenRegexStrings mTokenRegexStrings;
|
||||
|
||||
bool mCaseSensitive;
|
||||
|
||||
LanguageDefinition()
|
||||
: mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) {}
|
||||
|
||||
static const LanguageDefinition& GLSL();
|
||||
};
|
||||
|
||||
TextEditor();
|
||||
~TextEditor();
|
||||
|
||||
void SetLanguageDefinition(const LanguageDefinition& aLanguageDef);
|
||||
const LanguageDefinition& GetLanguageDefinition() const {
|
||||
return mLanguageDefinition;
|
||||
}
|
||||
|
||||
const Palette& GetPalette() const {
|
||||
return mPaletteBase;
|
||||
}
|
||||
void SetPalette(const Palette& aValue);
|
||||
|
||||
void SetErrorMarkers(const ErrorMarkers& aMarkers) {
|
||||
mErrorMarkers = aMarkers;
|
||||
}
|
||||
void SetBreakpoints(const Breakpoints& aMarkers) {
|
||||
mBreakpoints = aMarkers;
|
||||
}
|
||||
|
||||
void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false);
|
||||
void SetText(const std::string& aText);
|
||||
std::string GetText() const;
|
||||
|
||||
void SetTextLines(const std::vector<std::string>& aLines);
|
||||
std::vector<std::string> GetTextLines() const;
|
||||
|
||||
std::string GetSelectedText() const;
|
||||
std::string GetCurrentLineText() const;
|
||||
|
||||
int GetTotalLines() const {
|
||||
return (int)mLines.size();
|
||||
}
|
||||
bool IsOverwrite() const {
|
||||
return mOverwrite;
|
||||
}
|
||||
|
||||
void SetReadOnly(bool aValue);
|
||||
bool IsReadOnly() const {
|
||||
return mReadOnly;
|
||||
}
|
||||
bool IsTextChanged() const {
|
||||
return mTextChanged;
|
||||
}
|
||||
bool IsCursorPositionChanged() const {
|
||||
return mCursorPositionChanged;
|
||||
}
|
||||
|
||||
bool IsColorizerEnabled() const {
|
||||
return mColorizerEnabled;
|
||||
}
|
||||
void SetColorizerEnable(bool aValue);
|
||||
|
||||
Coordinates GetCursorPosition() const {
|
||||
return GetActualCursorCoordinates();
|
||||
}
|
||||
void SetCursorPosition(const Coordinates& aPosition);
|
||||
|
||||
inline void SetHandleMouseInputs(bool aValue) {
|
||||
mHandleMouseInputs = aValue;
|
||||
}
|
||||
inline bool IsHandleMouseInputsEnabled() const {
|
||||
return mHandleKeyboardInputs;
|
||||
}
|
||||
|
||||
inline void SetHandleKeyboardInputs(bool aValue) {
|
||||
mHandleKeyboardInputs = aValue;
|
||||
}
|
||||
inline bool IsHandleKeyboardInputsEnabled() const {
|
||||
return mHandleKeyboardInputs;
|
||||
}
|
||||
|
||||
inline void SetImGuiChildIgnored(bool aValue) {
|
||||
mIgnoreImGuiChild = aValue;
|
||||
}
|
||||
inline bool IsImGuiChildIgnored() const {
|
||||
return mIgnoreImGuiChild;
|
||||
}
|
||||
|
||||
inline void SetShowWhitespaces(bool aValue) {
|
||||
mShowWhitespaces = aValue;
|
||||
}
|
||||
inline bool IsShowingWhitespaces() const {
|
||||
return mShowWhitespaces;
|
||||
}
|
||||
|
||||
void SetTabSize(int aValue);
|
||||
inline int GetTabSize() const {
|
||||
return mTabSize;
|
||||
}
|
||||
|
||||
void InsertText(const std::string& aValue);
|
||||
void InsertText(const char* aValue);
|
||||
|
||||
void MoveUp(int aAmount = 1, bool aSelect = false);
|
||||
void MoveDown(int aAmount = 1, bool aSelect = false);
|
||||
void MoveLeft(int aAmount = 1, bool aSelect = false, bool aWordMode = false);
|
||||
void MoveRight(int aAmount = 1, bool aSelect = false, bool aWordMode = false);
|
||||
void MoveTop(bool aSelect = false);
|
||||
void MoveBottom(bool aSelect = false);
|
||||
void MoveHome(bool aSelect = false);
|
||||
void MoveEnd(bool aSelect = false);
|
||||
|
||||
void SetSelectionStart(const Coordinates& aPosition);
|
||||
void SetSelectionEnd(const Coordinates& aPosition);
|
||||
void SetSelection(const Coordinates& aStart, const Coordinates& aEnd,
|
||||
SelectionMode aMode = SelectionMode::Normal);
|
||||
void SelectWordUnderCursor();
|
||||
void SelectAll();
|
||||
bool HasSelection() const;
|
||||
|
||||
void Copy();
|
||||
void Cut();
|
||||
void Paste();
|
||||
void Delete();
|
||||
|
||||
bool CanUndo() const;
|
||||
bool CanRedo() const;
|
||||
void Undo(int aSteps = 1);
|
||||
void Redo(int aSteps = 1);
|
||||
|
||||
static const Palette& GetDarkPalette();
|
||||
static const Palette& GetLightPalette();
|
||||
static const Palette& GetRetroBluePalette();
|
||||
|
||||
private:
|
||||
typedef std::vector<std::pair<std::regex, PaletteIndex>> RegexList;
|
||||
|
||||
struct EditorState {
|
||||
Coordinates mSelectionStart;
|
||||
Coordinates mSelectionEnd;
|
||||
Coordinates mCursorPosition;
|
||||
};
|
||||
|
||||
class UndoRecord {
|
||||
public:
|
||||
UndoRecord() {}
|
||||
~UndoRecord() {}
|
||||
|
||||
UndoRecord(const std::string& aAdded, const TextEditor::Coordinates aAddedStart,
|
||||
const TextEditor::Coordinates aAddedEnd,
|
||||
|
||||
const std::string& aRemoved, const TextEditor::Coordinates aRemovedStart,
|
||||
const TextEditor::Coordinates aRemovedEnd,
|
||||
|
||||
TextEditor::EditorState& aBefore, TextEditor::EditorState& aAfter);
|
||||
|
||||
void Undo(TextEditor* aEditor);
|
||||
void Redo(TextEditor* aEditor);
|
||||
|
||||
std::string mAdded;
|
||||
Coordinates mAddedStart;
|
||||
Coordinates mAddedEnd;
|
||||
|
||||
std::string mRemoved;
|
||||
Coordinates mRemovedStart;
|
||||
Coordinates mRemovedEnd;
|
||||
|
||||
EditorState mBefore;
|
||||
EditorState mAfter;
|
||||
};
|
||||
|
||||
typedef std::vector<UndoRecord> UndoBuffer;
|
||||
|
||||
void ProcessInputs();
|
||||
void Colorize(int aFromLine = 0, int aCount = -1);
|
||||
void ColorizeRange(int aFromLine = 0, int aToLine = 0);
|
||||
void ColorizeInternal();
|
||||
float TextDistanceToLineStart(const Coordinates& aFrom) const;
|
||||
void EnsureCursorVisible();
|
||||
int GetPageSize() const;
|
||||
std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const;
|
||||
Coordinates GetActualCursorCoordinates() const;
|
||||
Coordinates SanitizeCoordinates(const Coordinates& aValue) const;
|
||||
void Advance(Coordinates& aCoordinates) const;
|
||||
void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd);
|
||||
int InsertTextAt(Coordinates& aWhere, const char* aValue);
|
||||
void AddUndo(UndoRecord& aValue);
|
||||
Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const;
|
||||
Coordinates FindWordStart(const Coordinates& aFrom) const;
|
||||
Coordinates FindWordEnd(const Coordinates& aFrom) const;
|
||||
Coordinates FindNextWord(const Coordinates& aFrom) const;
|
||||
int GetCharacterIndex(const Coordinates& aCoordinates) const;
|
||||
int GetCharacterColumn(int aLine, int aIndex) const;
|
||||
int GetLineCharacterCount(int aLine) const;
|
||||
int GetLineMaxColumn(int aLine) const;
|
||||
bool IsOnWordBoundary(const Coordinates& aAt) const;
|
||||
void RemoveLine(int aStart, int aEnd);
|
||||
void RemoveLine(int aIndex);
|
||||
Line& InsertLine(int aIndex);
|
||||
void EnterCharacter(ImWchar aChar, bool aShift);
|
||||
void Backspace();
|
||||
void DeleteSelection();
|
||||
std::string GetWordUnderCursor() const;
|
||||
std::string GetWordAt(const Coordinates& aCoords) const;
|
||||
ImU32 GetGlyphColor(const Glyph& aGlyph) const;
|
||||
|
||||
void HandleKeyboardInputs();
|
||||
void HandleMouseInputs();
|
||||
void Render();
|
||||
|
||||
float mLineSpacing;
|
||||
Lines mLines;
|
||||
EditorState mState;
|
||||
UndoBuffer mUndoBuffer;
|
||||
int mUndoIndex;
|
||||
|
||||
int mTabSize;
|
||||
bool mOverwrite;
|
||||
bool mReadOnly;
|
||||
bool mWithinRender;
|
||||
bool mScrollToCursor;
|
||||
bool mScrollToTop;
|
||||
bool mTextChanged;
|
||||
bool mColorizerEnabled;
|
||||
float mTextStart; // position (in pixels) where a code line starts relative to the left of the
|
||||
// TextEditor.
|
||||
int mLeftMargin;
|
||||
bool mCursorPositionChanged;
|
||||
int mColorRangeMin, mColorRangeMax;
|
||||
SelectionMode mSelectionMode;
|
||||
bool mHandleKeyboardInputs;
|
||||
bool mHandleMouseInputs;
|
||||
bool mIgnoreImGuiChild;
|
||||
bool mShowWhitespaces;
|
||||
|
||||
Palette mPaletteBase;
|
||||
Palette mPalette;
|
||||
LanguageDefinition mLanguageDefinition;
|
||||
RegexList mRegexList;
|
||||
|
||||
bool mCheckComments;
|
||||
Breakpoints mBreakpoints;
|
||||
ErrorMarkers mErrorMarkers;
|
||||
ImVec2 mCharAdvance;
|
||||
Coordinates mInteractiveStart, mInteractiveEnd;
|
||||
std::string mLineBuffer;
|
||||
uint64_t mStartTime;
|
||||
|
||||
float mLastClick;
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <zlib-ng.h>
|
||||
#include "common/io_file.h"
|
||||
#include "common/logging/formatter.h"
|
||||
#include "core/file_format/pkg.h"
|
||||
#include "core/file_format/pkg_type.h"
|
||||
|
||||
@ -349,7 +350,8 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem::
|
||||
auto parent_path = extract_path.parent_path();
|
||||
auto title_id = GetTitleID();
|
||||
|
||||
if (parent_path.filename() != title_id) {
|
||||
if (parent_path.filename() != title_id &&
|
||||
!fmt::UTF(extract_path.u8string()).data.ends_with("-UPDATE")) {
|
||||
extractPaths[ndinode_counter] = parent_path / title_id;
|
||||
} else {
|
||||
// DLCs path has different structure
|
||||
|
@ -227,6 +227,12 @@ void PSF::AddBinary(std::string key, std::vector<u8> value, bool update) {
|
||||
map_binaries.emplace(entry_list.size() - 1, std::move(value));
|
||||
}
|
||||
|
||||
void PSF::AddBinary(std::string key, uint64_t value, bool update) {
|
||||
std::vector<u8> data(8);
|
||||
std::memcpy(data.data(), &value, 8);
|
||||
return AddBinary(std::move(key), std::move(data), update);
|
||||
}
|
||||
|
||||
void PSF::AddString(std::string key, std::string value, bool update) {
|
||||
auto [it, index] = FindEntry(key);
|
||||
bool exist = it != entry_list.end();
|
||||
|
@ -67,6 +67,7 @@ public:
|
||||
std::optional<s32> GetInteger(std::string_view key) const;
|
||||
|
||||
void AddBinary(std::string key, std::vector<u8> value, bool update = false);
|
||||
void AddBinary(std::string key, uint64_t value, bool update = false); // rsv4 format
|
||||
void AddString(std::string key, std::string value, bool update = false);
|
||||
void AddInteger(std::string key, s32 value, bool update = false);
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/config.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
|
||||
@ -27,9 +28,9 @@ void MntPoints::UnmountAll() {
|
||||
m_mnt_pairs.clear();
|
||||
}
|
||||
|
||||
std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, bool* is_read_only) {
|
||||
std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) {
|
||||
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
|
||||
std::string corrected_path(guest_directory);
|
||||
std::string corrected_path(path);
|
||||
size_t pos = corrected_path.find("//");
|
||||
while (pos != std::string::npos) {
|
||||
corrected_path.replace(pos, 2, "/");
|
||||
@ -51,68 +52,83 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, b
|
||||
}
|
||||
|
||||
// Remove device (e.g /app0) from path to retrieve relative path.
|
||||
pos = mount->mount.size() + 1;
|
||||
const auto rel_path = std::string_view(corrected_path).substr(pos);
|
||||
const auto host_path = mount->host_path / rel_path;
|
||||
const auto rel_path = std::string_view{corrected_path}.substr(mount->mount.size() + 1);
|
||||
std::filesystem::path host_path = mount->host_path / rel_path;
|
||||
std::filesystem::path patch_path = mount->host_path;
|
||||
patch_path += "-UPDATE";
|
||||
patch_path /= rel_path;
|
||||
|
||||
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
|
||||
std::filesystem::exists(patch_path)) {
|
||||
return patch_path;
|
||||
}
|
||||
|
||||
if (!NeedsCaseInsensitiveSearch) {
|
||||
return host_path;
|
||||
}
|
||||
|
||||
// If the path does not exist attempt to verify this.
|
||||
// Retrieve parent path until we find one that exists.
|
||||
std::scoped_lock lk{m_mutex};
|
||||
path_parts.clear();
|
||||
auto current_path = host_path;
|
||||
while (!std::filesystem::exists(current_path)) {
|
||||
// We have probably cached this if it's a folder.
|
||||
if (auto it = path_cache.find(current_path); it != path_cache.end()) {
|
||||
current_path = it->second;
|
||||
break;
|
||||
const auto search = [&](const auto host_path) {
|
||||
// If the path does not exist attempt to verify this.
|
||||
// Retrieve parent path until we find one that exists.
|
||||
std::scoped_lock lk{m_mutex};
|
||||
path_parts.clear();
|
||||
auto current_path = host_path;
|
||||
while (!std::filesystem::exists(current_path)) {
|
||||
// We have probably cached this if it's a folder.
|
||||
if (auto it = path_cache.find(current_path); it != path_cache.end()) {
|
||||
current_path = it->second;
|
||||
break;
|
||||
}
|
||||
path_parts.emplace_back(current_path.filename());
|
||||
current_path = current_path.parent_path();
|
||||
}
|
||||
path_parts.emplace_back(current_path.filename());
|
||||
current_path = current_path.parent_path();
|
||||
}
|
||||
|
||||
// We have found an anchor. Traverse parts we recoded and see if they
|
||||
// exist in filesystem but in different case.
|
||||
auto guest_path = current_path;
|
||||
while (!path_parts.empty()) {
|
||||
const auto part = path_parts.back();
|
||||
const auto add_match = [&](const auto& host_part) {
|
||||
current_path /= host_part;
|
||||
guest_path /= part;
|
||||
path_cache[guest_path] = current_path;
|
||||
path_parts.pop_back();
|
||||
};
|
||||
|
||||
// Can happen when the mismatch is in upper folder.
|
||||
if (std::filesystem::exists(current_path / part)) {
|
||||
add_match(part);
|
||||
continue;
|
||||
}
|
||||
const auto part_low = Common::ToLower(part.string());
|
||||
bool found_match = false;
|
||||
for (const auto& path : std::filesystem::directory_iterator(current_path)) {
|
||||
const auto candidate = path.path().filename();
|
||||
const auto filename = Common::ToLower(candidate.string());
|
||||
// Check if a filename matches in case insensitive manner.
|
||||
if (filename != part_low) {
|
||||
// We have found an anchor. Traverse parts we recoded and see if they
|
||||
// exist in filesystem but in different case.
|
||||
auto guest_path = current_path;
|
||||
while (!path_parts.empty()) {
|
||||
const auto part = path_parts.back();
|
||||
const auto add_match = [&](const auto& host_part) {
|
||||
current_path /= host_part;
|
||||
guest_path /= part;
|
||||
path_cache[guest_path] = current_path;
|
||||
path_parts.pop_back();
|
||||
};
|
||||
// Can happen when the mismatch is in upper folder.
|
||||
if (std::filesystem::exists(current_path / part)) {
|
||||
add_match(part);
|
||||
continue;
|
||||
}
|
||||
// We found a match, record the actual path in the cache.
|
||||
add_match(candidate);
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
if (!found_match) {
|
||||
// Opening the guest path will surely fail but at least gives
|
||||
// a better error message than the empty path.
|
||||
return host_path;
|
||||
const auto part_low = Common::ToLower(part.string());
|
||||
bool found_match = false;
|
||||
for (const auto& path : std::filesystem::directory_iterator(current_path)) {
|
||||
const auto candidate = path.path().filename();
|
||||
const auto filename = Common::ToLower(candidate.string());
|
||||
// Check if a filename matches in case insensitive manner.
|
||||
if (filename != part_low) {
|
||||
continue;
|
||||
}
|
||||
// We found a match, record the actual path in the cache.
|
||||
add_match(candidate);
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
if (!found_match) {
|
||||
return std::optional<std::filesystem::path>({});
|
||||
}
|
||||
}
|
||||
return std::optional<std::filesystem::path>(current_path);
|
||||
};
|
||||
|
||||
if (const auto path = search(patch_path)) {
|
||||
return *path;
|
||||
}
|
||||
if (const auto path = search(host_path)) {
|
||||
return *path;
|
||||
}
|
||||
|
||||
// The path was found.
|
||||
return current_path;
|
||||
// Opening the guest path will surely fail but at least gives
|
||||
// a better error message than the empty path.
|
||||
return host_path;
|
||||
}
|
||||
|
||||
int HandleTable::CreateHandle() {
|
||||
|
@ -1,12 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#pragma clang optimize off
|
||||
#include <future>
|
||||
#include <numeric>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "ajm_at9.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/ajm/ajm.h"
|
||||
#include "core/libraries/ajm/ajm_error.h"
|
||||
@ -16,7 +16,6 @@
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
extern "C" {
|
||||
#include <error_codes.h>
|
||||
#include <libatrac9.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/opt.h>
|
||||
@ -28,18 +27,34 @@ namespace Libraries::Ajm {
|
||||
|
||||
static constexpr u32 AJM_INSTANCE_STATISTICS = 0x80000;
|
||||
|
||||
static constexpr u32 SCE_AJM_WAIT_INFINITE = -1;
|
||||
|
||||
static constexpr u32 MaxInstances = 0x2fff;
|
||||
|
||||
static constexpr u32 MaxBatches = 1000;
|
||||
|
||||
struct BatchInfo {
|
||||
u16 instance{};
|
||||
u16 offset_in_qwords{}; // Needed for AjmBatchError?
|
||||
bool waiting{};
|
||||
bool finished{};
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
int result{};
|
||||
};
|
||||
|
||||
struct AjmDevice {
|
||||
u32 max_prio;
|
||||
u32 min_prio;
|
||||
u32 max_prio{};
|
||||
u32 min_prio{};
|
||||
u32 curr_cursor{};
|
||||
u32 release_cursor{MaxInstances - 1};
|
||||
std::array<bool, NumAjmCodecs> is_registered{};
|
||||
std::array<u32, MaxInstances> free_instances{};
|
||||
std::array<std::unique_ptr<AjmAt9Decoder>, MaxInstances> instances;
|
||||
std::array<std::unique_ptr<AjmInstance>, MaxInstances> instances;
|
||||
std::vector<std::shared_ptr<BatchInfo>> batches{};
|
||||
std::mutex batches_mutex;
|
||||
|
||||
bool IsRegistered(AjmCodecType type) const {
|
||||
[[nodiscard]] bool IsRegistered(AjmCodecType type) const {
|
||||
return is_registered[static_cast<u32>(type)];
|
||||
}
|
||||
|
||||
@ -86,11 +101,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(AjmSingleJob* batch_pos, u32 in
|
||||
batch_pos->opcode.is_statistic = instance == AJM_INSTANCE_STATISTICS;
|
||||
batch_pos->opcode.is_control = true;
|
||||
|
||||
if (instance == AJM_INSTANCE_STATISTICS) {
|
||||
BREAKPOINT();
|
||||
}
|
||||
|
||||
AjmInOutJob* job = nullptr;
|
||||
AjmInOutJob* job;
|
||||
if (ret_addr == nullptr) {
|
||||
batch_pos->job_size = sizeof(AjmInOutJob);
|
||||
job = &batch_pos->job;
|
||||
@ -121,45 +132,10 @@ void* PS4_SYSV_ABI sceAjmBatchJobInlineBuffer(AjmSingleJob* batch_pos, const voi
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ParseWavHeader = [](void* buf, WavHeader* header) {
|
||||
if (!buf) {
|
||||
// buf is passed as nullptr in some cases (i.e. GetCodecInfo)
|
||||
return;
|
||||
}
|
||||
std::memcpy(header, buf, sizeof(WavHeader));
|
||||
|
||||
std::string riff(header->RIFF, 4);
|
||||
std::string wave(header->WAVE, 4);
|
||||
std::string fmt(header->fmt, 4);
|
||||
std::string dataID(header->Subchunk2ID, 4);
|
||||
|
||||
if (std::memcmp(header->RIFF, "RIFF", 4) != 0 || std::memcmp(header->WAVE, "WAVE", 4) != 0 ||
|
||||
std::memcmp(header->fmt, "fmt ", 4) != 0 ||
|
||||
std::memcmp(header->Subchunk2ID, "data", 4) != 0) {
|
||||
LOG_ERROR(Lib_Ajm, "Invalid WAV file.");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(Lib_Ajm, "RIFF header: {}", riff);
|
||||
LOG_INFO(Lib_Ajm, "WAVE header: {}", wave);
|
||||
LOG_INFO(Lib_Ajm, "FMT: {}", fmt);
|
||||
LOG_INFO(Lib_Ajm, "Data size: {}", header->ChunkSize);
|
||||
LOG_INFO(Lib_Ajm, "Sampling Rate: {}", header->SamplesPerSec);
|
||||
LOG_INFO(Lib_Ajm, "Number of bits used: {}", header->bitsPerSample);
|
||||
LOG_INFO(Lib_Ajm, "Number of channels: {}", header->NumOfChan);
|
||||
LOG_INFO(Lib_Ajm, "Number of bytes per second: {}", header->bytesPerSec);
|
||||
LOG_INFO(Lib_Ajm, "Data length: {}", header->Subchunk2Size);
|
||||
LOG_INFO(Lib_Ajm, "Audio Format: {}", header->AudioFormat);
|
||||
LOG_INFO(Lib_Ajm, "Block align: {}", header->blockAlign);
|
||||
LOG_INFO(Lib_Ajm, "Data string: {}", dataID);
|
||||
};
|
||||
|
||||
void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instance, AjmFlags flags,
|
||||
void* in_buffer, u32 in_size, u8* out_buffer,
|
||||
u8* in_buffer, u32 in_size, u8* out_buffer,
|
||||
const u32 out_size, u8* sideband_output,
|
||||
const u32 sideband_output_size, const void* ret_addr) {
|
||||
WavHeader header{};
|
||||
ParseWavHeader(in_buffer, &header);
|
||||
LOG_INFO(Lib_Ajm,
|
||||
"called instance = {:#x}, flags = {:#x}, cmd = {}, in_size = {:#x}, out_size = {:#x}, "
|
||||
"ret_addr = {}",
|
||||
@ -178,7 +154,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instan
|
||||
batch_pos->opcode.is_statistic = false;
|
||||
batch_pos->opcode.is_control = false;
|
||||
|
||||
AjmInOutJob* job = nullptr;
|
||||
AjmInOutJob* job;
|
||||
if (ret_addr == nullptr) {
|
||||
batch_pos->job_size = sizeof(AjmInOutJob) + 16;
|
||||
job = &batch_pos->job;
|
||||
@ -188,16 +164,17 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instan
|
||||
job = &batch_pos->ret.job;
|
||||
}
|
||||
|
||||
// todo: add some missing stuff
|
||||
// TODO: Check if all the fields are being set, might be missing some
|
||||
job->input.buf_size = in_size;
|
||||
job->input.buffer = static_cast<u8*>(in_buffer);
|
||||
job->input.buffer = in_buffer;
|
||||
job->flags = u32(flags.raw);
|
||||
job->unk1 = (job->unk1 & 0xfc000030) + (flags.raw >> 0x1a) + 4;
|
||||
job->output.buf_size = out_size;
|
||||
job->output.buffer = out_buffer;
|
||||
job->output.props &= 0xffffffe0;
|
||||
job->output.props |= 0x12;
|
||||
*reinterpret_cast<u8**>(reinterpret_cast<u8*>(job) + 32) = sideband_output;
|
||||
// *reinterpret_cast<u8**>(reinterpret_cast<u8*>(job) + 32) = sideband_output;
|
||||
job->sideband_output = sideband_output;
|
||||
return job;
|
||||
}
|
||||
|
||||
@ -213,6 +190,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in
|
||||
instance, flags.raw, magic_enum::enum_name(AjmJobRunFlags(flags.command)),
|
||||
magic_enum::enum_name(AjmJobSidebandFlags(flags.sideband)), num_in_buffers,
|
||||
num_out_buffers, fmt::ptr(ret_addr));
|
||||
|
||||
const u32 job_size = (num_in_buffers * 2 + 1 + num_out_buffers * 2) * 8;
|
||||
const bool is_debug = ret_addr != nullptr;
|
||||
batch_pos->opcode.instance = instance;
|
||||
@ -223,7 +201,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in
|
||||
batch_pos->opcode.is_statistic = false;
|
||||
batch_pos->opcode.is_control = false;
|
||||
|
||||
u32* job = nullptr;
|
||||
u32* job;
|
||||
if (!is_debug) {
|
||||
batch_pos->job_size = job_size + 16;
|
||||
job = batch_pos->job;
|
||||
@ -237,7 +215,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < num_in_buffers; i++) {
|
||||
AjmJobBuffer* in_buf = reinterpret_cast<AjmJobBuffer*>(job);
|
||||
auto* in_buf = reinterpret_cast<AjmJobBuffer*>(job);
|
||||
in_buf->props &= 0xffffffe0;
|
||||
in_buf->props |= 1;
|
||||
in_buf->buf_size = in_buffers[i].size;
|
||||
@ -250,7 +228,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in
|
||||
job += 2;
|
||||
|
||||
for (s32 i = 0; i < num_out_buffers; i++) {
|
||||
AjmJobBuffer* out_buf = reinterpret_cast<AjmJobBuffer*>(job);
|
||||
auto* out_buf = reinterpret_cast<AjmJobBuffer*>(job);
|
||||
out_buf->props &= 0xffffffe0;
|
||||
out_buf->props |= 0x11;
|
||||
out_buf->buf_size = out_buffers[i].size;
|
||||
@ -273,23 +251,23 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_
|
||||
return ORBIS_AJM_ERROR_MALFORMED_BATCH;
|
||||
}
|
||||
|
||||
static constexpr u32 MaxBatches = 1000;
|
||||
const auto batch_info = std::make_shared<BatchInfo>();
|
||||
|
||||
struct BatchInfo {
|
||||
u16 instance;
|
||||
u16 offset_in_qwords;
|
||||
};
|
||||
std::array<BatchInfo, MaxBatches> batches{};
|
||||
u32 num_batches = 0;
|
||||
|
||||
const u8* batch_ptr = batch;
|
||||
const u8* batch_end = batch + batch_size;
|
||||
while (batch_ptr < batch_end) {
|
||||
if (num_batches >= MaxBatches) {
|
||||
{
|
||||
if (dev->batches.size() >= MaxBatches) {
|
||||
LOG_ERROR(Lib_Ajm, "Too many batches in job!");
|
||||
return ORBIS_AJM_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
AjmJobHeader header;
|
||||
|
||||
*out_batch_id = static_cast<u32>(dev->batches.size());
|
||||
dev->batches.push_back(batch_info);
|
||||
}
|
||||
|
||||
const u8* batch_ptr = batch;
|
||||
const u8* batch_end = batch + batch_size;
|
||||
AjmJobHeader header{};
|
||||
|
||||
while (batch_ptr < batch_end) {
|
||||
std::memcpy(&header, batch_ptr, sizeof(u64));
|
||||
|
||||
const auto& opcode = header.opcode;
|
||||
@ -297,34 +275,57 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_
|
||||
const u8* job_ptr = batch_ptr + sizeof(AjmJobHeader) + opcode.is_debug * 16;
|
||||
|
||||
if (opcode.is_control) {
|
||||
// ASSERT_MSG(!opcode.is_statistic, "Statistic instance is not handled");
|
||||
const auto command = AjmJobControlFlags(opcode.command_flags);
|
||||
switch (command) {
|
||||
case AjmJobControlFlags::Reset: {
|
||||
LOG_INFO(Lib_Ajm, "Resetting instance {}", opcode.instance);
|
||||
dev->instances[opcode.instance]->Reset();
|
||||
LOG_INFO(Lib_Ajm, "Resetting instance {}", instance);
|
||||
dev->instances[instance]->Reset();
|
||||
break;
|
||||
}
|
||||
case (AjmJobControlFlags::Initialize | AjmJobControlFlags::Reset):
|
||||
LOG_INFO(Lib_Ajm, "Initializing instance {}", opcode.instance);
|
||||
case AjmJobControlFlags::Initialize:
|
||||
LOG_INFO(Lib_Ajm, "Initializing instance {}", instance);
|
||||
if (dev->instances[instance]->codec_type == AjmCodecType::At9Dec) {
|
||||
const auto at9_instance =
|
||||
dynamic_cast<AjmAt9Decoder*>(dev->instances[instance].get());
|
||||
const auto in_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr);
|
||||
std::memcpy(
|
||||
at9_instance->config_data,
|
||||
reinterpret_cast<const SceAjmDecAt9InitializeParameters*>(in_buffer->buffer)
|
||||
->config_data,
|
||||
SCE_AT9_CONFIG_DATA_SIZE);
|
||||
LOG_INFO(
|
||||
Lib_Ajm, "Initialize params: {}, config_data: {}, reserved: {}",
|
||||
fmt::ptr(reinterpret_cast<const SceAjmDecAt9InitializeParameters*>(
|
||||
in_buffer->buffer)),
|
||||
fmt::ptr(reinterpret_cast<const SceAjmDecAt9InitializeParameters*>(
|
||||
in_buffer->buffer)
|
||||
->config_data),
|
||||
reinterpret_cast<const SceAjmDecAt9InitializeParameters*>(in_buffer->buffer)
|
||||
->reserved);
|
||||
}
|
||||
break;
|
||||
case AjmJobControlFlags::Resample:
|
||||
LOG_INFO(Lib_Ajm, "Set resample params of instance {}", opcode.instance);
|
||||
LOG_INFO(Lib_Ajm, "Set resample params of instance {}", instance);
|
||||
break;
|
||||
case AjmJobControlFlags::StatisticsEngine:
|
||||
ASSERT_MSG(instance == AJM_INSTANCE_STATISTICS,
|
||||
"Expected AJM_INSTANCE_STATISTICS for StatisticsEngine command");
|
||||
LOG_TRACE(Lib_Ajm, "StatisticsEngine flag not implemented");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Write sideband structures.
|
||||
const AjmJobBuffer* out_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr + 24);
|
||||
const auto* out_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr + 24);
|
||||
auto* result = reinterpret_cast<AjmSidebandResult*>(out_buffer->buffer);
|
||||
result->result = 0;
|
||||
result->internal_result = 0;
|
||||
} else {
|
||||
const auto command = AjmJobRunFlags(opcode.command_flags);
|
||||
const auto sideband = AjmJobSidebandFlags(opcode.sideband_flags);
|
||||
const AjmJobBuffer* in_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr);
|
||||
const AjmJobBuffer* out_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr + 24);
|
||||
const auto* in_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr);
|
||||
const auto* out_buffer = reinterpret_cast<const AjmJobBuffer*>(job_ptr + 24);
|
||||
job_ptr += 24;
|
||||
|
||||
LOG_INFO(Lib_Ajm, "Decode job cmd = {}, sideband = {}, in_addr = {}, in_size = {}",
|
||||
@ -332,7 +333,7 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_
|
||||
fmt::ptr(in_buffer->buffer), in_buffer->buf_size);
|
||||
|
||||
// Decode as much of the input bitstream as possible.
|
||||
AjmAt9Decoder* decoder_instance = dev->instances[opcode.instance].get();
|
||||
AjmInstance* decoder_instance = dev->instances[instance].get();
|
||||
const auto [in_remain, out_remain, num_frames] = decoder_instance->Decode(
|
||||
in_buffer->buffer, in_buffer->buf_size, out_buffer->buffer, out_buffer->buf_size);
|
||||
|
||||
@ -357,27 +358,81 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_
|
||||
sideband_ptr += sizeof(AjmSidebandMFrame);
|
||||
}
|
||||
if (True(command & AjmJobRunFlags::GetCodecInfo)) {
|
||||
LOG_TRACE(Lib_Ajm, "GetCodecInfo called, supplying dummy info for now");
|
||||
auto* codec_info = reinterpret_cast<SceAjmSidebandDecAt9CodecInfo*>(sideband_ptr);
|
||||
codec_info->uiFrameSamples = 0;
|
||||
codec_info->uiFramesInSuperFrame = 0;
|
||||
codec_info->uiNextFrameSize = 0;
|
||||
codec_info->uiSuperFrameSize = 0;
|
||||
sideband_ptr += sizeof(SceAjmSidebandDecAt9CodecInfo);
|
||||
// TODO: Implement this for MP3
|
||||
|
||||
if (decoder_instance->codec_type == AjmCodecType::At9Dec) {
|
||||
const auto at9_decoder_instance =
|
||||
dynamic_cast<AjmAt9Decoder*>(decoder_instance);
|
||||
|
||||
Atrac9CodecInfo decoder_codec_info;
|
||||
Atrac9GetCodecInfo(at9_decoder_instance->handle, &decoder_codec_info);
|
||||
|
||||
auto* codec_info =
|
||||
reinterpret_cast<SceAjmSidebandDecAt9CodecInfo*>(sideband_ptr);
|
||||
codec_info->uiFrameSamples = decoder_codec_info.frameSamples;
|
||||
codec_info->uiFramesInSuperFrame = decoder_codec_info.framesInSuperframe;
|
||||
codec_info->uiNextFrameSize =
|
||||
static_cast<Atrac9Handle*>(at9_decoder_instance->handle)->Config.FrameBytes;
|
||||
codec_info->uiSuperFrameSize = decoder_codec_info.superframeSize;
|
||||
sideband_ptr += sizeof(SceAjmSidebandDecAt9CodecInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
batch_ptr += sizeof(AjmJobHeader) + header.job_size;
|
||||
}
|
||||
static int batch_id = 0;
|
||||
*out_batch_id = ++batch_id;
|
||||
|
||||
batch_info->finished = true;
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmBatchWait() {
|
||||
LOG_ERROR(Lib_Ajm, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
// useless for now because batch processing isn't asynchronous
|
||||
int PS4_SYSV_ABI sceAjmBatchWait(const u32 context, const u32 batch_id, const u32 timeout,
|
||||
AjmBatchError* const batch_error) {
|
||||
LOG_INFO(Lib_Ajm, "called context = {}, batch_id = {}, timeout = {}", context, batch_id,
|
||||
timeout);
|
||||
|
||||
if (batch_id > 0xFF) {
|
||||
return ORBIS_AJM_ERROR_INVALID_BATCH;
|
||||
}
|
||||
|
||||
if (batch_id >= dev->batches.size()) {
|
||||
return ORBIS_AJM_ERROR_INVALID_BATCH;
|
||||
}
|
||||
|
||||
const std::shared_ptr<BatchInfo> batch = dev->batches[batch_id];
|
||||
|
||||
if (batch->waiting) {
|
||||
return ORBIS_AJM_ERROR_BUSY;
|
||||
}
|
||||
|
||||
batch->waiting = true;
|
||||
|
||||
if (timeout > 0 && timeout != SCE_AJM_WAIT_INFINITE) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
|
||||
|
||||
if (!batch->finished) {
|
||||
batch->waiting = false;
|
||||
return ORBIS_AJM_ERROR_IN_PROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout == SCE_AJM_WAIT_INFINITE) {
|
||||
while (!batch->finished) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
// timeout == 0 = non-blocking poll
|
||||
if (!batch->finished) {
|
||||
batch->waiting = false;
|
||||
return ORBIS_AJM_ERROR_IN_PROGRESS;
|
||||
}
|
||||
|
||||
dev->batches.erase(dev->batches.begin() + batch_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData() {
|
||||
@ -433,16 +488,16 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context, AjmCodecType codec_type, AjmI
|
||||
}
|
||||
const u32 index = dev->free_instances[dev->curr_cursor++];
|
||||
dev->curr_cursor %= MaxInstances;
|
||||
std::unique_ptr<AjmAt9Decoder> instance;
|
||||
std::unique_ptr<AjmInstance> instance;
|
||||
switch (codec_type) {
|
||||
case AjmCodecType::Mp3Dec:
|
||||
instance = std::make_unique<AjmAt9Decoder>();
|
||||
instance = std::make_unique<AjmMp3Decoder>();
|
||||
break;
|
||||
case AjmCodecType::At9Dec:
|
||||
instance = std::make_unique<AjmAt9Decoder>();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
UNREACHABLE_MSG("Codec #{} not implemented", u32(codec_type));
|
||||
}
|
||||
instance->index = index;
|
||||
instance->codec_type = codec_type;
|
||||
|
@ -41,12 +41,14 @@ struct AjmInOutJob {
|
||||
u32 unk1;
|
||||
u32 flags;
|
||||
AjmJobBuffer output;
|
||||
void* sideband_output;
|
||||
};
|
||||
|
||||
enum class AjmJobControlFlags : u32 {
|
||||
Reset = 1 << 2,
|
||||
Initialize = 1 << 3,
|
||||
Resample = 1 << 4,
|
||||
StatisticsEngine = 1U << 31,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(AjmJobControlFlags)
|
||||
|
||||
@ -92,7 +94,7 @@ struct AjmSingleJob {
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(AjmSingleJob) == 0x40);
|
||||
static_assert(sizeof(AjmSingleJob) == 0x48);
|
||||
|
||||
struct AjmMultiJob {
|
||||
AjmHLEOpcode opcode;
|
||||
@ -149,7 +151,7 @@ void* PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(AjmSingleJob* batch_pos, u32 in
|
||||
void* PS4_SYSV_ABI sceAjmBatchJobInlineBuffer(AjmSingleJob* batch_pos, const void* in_buffer,
|
||||
size_t in_size, const void** batch_address);
|
||||
void* PS4_SYSV_ABI sceAjmBatchJobRunBufferRa(AjmSingleJob* batch_pos, u32 instance, AjmFlags flags,
|
||||
void * in_buffer, u32 in_size, u8* out_buffer,
|
||||
u8* in_buffer, u32 in_size, u8* out_buffer,
|
||||
const u32 out_size, u8* sideband_output,
|
||||
const u32 sideband_output_size, const void* ret_addr);
|
||||
void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 instance,
|
||||
@ -159,9 +161,10 @@ void* PS4_SYSV_ABI sceAjmBatchJobRunSplitBufferRa(AjmMultiJob* batch_pos, u32 in
|
||||
u64 num_output_buffers, void* sideband_output,
|
||||
u64 sideband_output_size, const void* ret_addr);
|
||||
int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_size,
|
||||
const int priority, AjmBatchError* patch_error,
|
||||
const int priority, AjmBatchError* batch_error,
|
||||
u32* out_batch_id);
|
||||
int PS4_SYSV_ABI sceAjmBatchWait();
|
||||
int PS4_SYSV_ABI sceAjmBatchWait(const u32 context, const u32 batch_id, const u32 timeout,
|
||||
AjmBatchError* const batch_error);
|
||||
int PS4_SYSV_ABI sceAjmDecAt9ParseConfigData();
|
||||
int PS4_SYSV_ABI sceAjmDecMp3ParseFrame(const u8* stream, u32 stream_size, int parse_ofl,
|
||||
AjmDecMp3ParseFrame* frame);
|
||||
|
@ -4,8 +4,10 @@
|
||||
#include <fmt/format.h>
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/ajm/ajm_at9.h"
|
||||
#include "error_codes.h"
|
||||
|
||||
extern "C" {
|
||||
#include <decoder.h>
|
||||
#include <libatrac9.h>
|
||||
}
|
||||
|
||||
@ -17,9 +19,14 @@ AjmAt9Decoder::AjmAt9Decoder() {
|
||||
AjmAt9Decoder::Reset();
|
||||
}
|
||||
|
||||
AjmAt9Decoder::~AjmAt9Decoder() {}
|
||||
AjmAt9Decoder::~AjmAt9Decoder() {
|
||||
Atrac9ReleaseHandle(handle);
|
||||
}
|
||||
|
||||
void AjmAt9Decoder::Reset() {
|
||||
Atrac9ReleaseHandle(handle);
|
||||
handle = Atrac9GetHandle();
|
||||
ASSERT_MSG(handle, "Atrac9GetHandle failed");
|
||||
decoded_samples = 0;
|
||||
static int filename = 0;
|
||||
file.close();
|
||||
@ -28,31 +35,40 @@ void AjmAt9Decoder::Reset() {
|
||||
|
||||
std::tuple<u32, u32, u32> AjmAt9Decoder::Decode(const u8* in_buf, u32 in_size, u8* out_buf,
|
||||
u32 out_size) {
|
||||
auto ParseWavHeader = [](const uint8_t* buf, WavHeader* header) {
|
||||
std::memcpy(header, buf, sizeof(WavHeader));
|
||||
};
|
||||
|
||||
if (in_size <= 0 || out_size <= 0) {
|
||||
return std::tuple(in_size, out_size, 0);
|
||||
}
|
||||
|
||||
WavHeader header{};
|
||||
ParseWavHeader(in_buf - 0x64, &header);
|
||||
Atrac9InitDecoder(handle, config_data);
|
||||
|
||||
if (!decoder_initialized) {
|
||||
Atrac9InitDecoder(handle, header.configData);
|
||||
decoder_initialized = true;
|
||||
}
|
||||
|
||||
// todo: do something with decoded data :p
|
||||
const auto decoder_handle = static_cast<Atrac9Handle*>(handle);
|
||||
|
||||
int frame_count;
|
||||
int bytes_used;
|
||||
Atrac9Decode(handle, in_buf, nullptr, &bytes_used);
|
||||
|
||||
Atrac9CodecInfo codec_info;
|
||||
Atrac9GetCodecInfo(handle, &codec_info);
|
||||
|
||||
return std::tuple(in_size, out_size, 0);
|
||||
decoded_samples = 0;
|
||||
|
||||
const auto size = codec_info.channels * codec_info.frameSamples * sizeof(u16);
|
||||
|
||||
for (frame_count = 0; frame_count < decoder_handle->Config.FramesPerSuperframe; frame_count++) {
|
||||
short pcm_buffer[size];
|
||||
int ret = Atrac9Decode(decoder_handle, in_buf, pcm_buffer, &bytes_used);
|
||||
ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed");
|
||||
in_buf += bytes_used;
|
||||
in_size -= bytes_used;
|
||||
std::memcpy(out_buf, pcm_buffer, size);
|
||||
file.write(reinterpret_cast<const char*>(pcm_buffer), size);
|
||||
out_buf += size;
|
||||
out_size -= size;
|
||||
decoded_samples += decoder_handle->Config.FrameSamples;
|
||||
}
|
||||
|
||||
LOG_TRACE(Lib_Ajm, "Decoded {} samples, frame count: {}", decoded_samples, frame_count);
|
||||
|
||||
return std::tuple(in_size, out_size, frame_count);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Ajm
|
||||
|
@ -4,7 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <guiddef.h>
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/ajm/ajm_instance.h"
|
||||
|
||||
@ -16,42 +15,17 @@ namespace Libraries::Ajm {
|
||||
|
||||
constexpr unsigned int SCE_AT9_CONFIG_DATA_SIZE = 4;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct WavHeader {
|
||||
/* RIFF Chunk Descriptor */
|
||||
char RIFF[4]; // RIFF Header Magic header
|
||||
uint32_t ChunkSize; // RIFF Chunk Size
|
||||
char WAVE[4]; // WAVE Header
|
||||
/* "fmt" sub-chunk */
|
||||
char fmt[4]; // FMT header
|
||||
uint32_t Subchunk1Size; // Size of the fmt chunk
|
||||
uint16_t AudioFormat; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law,
|
||||
// 259=ADPCM
|
||||
uint16_t NumOfChan; // Number of channels 1=Mono 2=Sterio
|
||||
uint32_t SamplesPerSec; // Sampling Frequency in Hz
|
||||
uint32_t bytesPerSec; // bytes per second
|
||||
uint16_t blockAlign; // 2=16-bit mono, 4=16-bit stereo
|
||||
uint16_t bitsPerSample; // Number of bits per sample
|
||||
u16 cbSize; // Extension size
|
||||
u16 samplesPerBlock;
|
||||
u32 channelMask;
|
||||
GUID subFormat;
|
||||
u32 versionInfo;
|
||||
u8 configData[SCE_AT9_CONFIG_DATA_SIZE]; // the juicy part
|
||||
struct SceAjmDecAt9InitializeParameters {
|
||||
u8 config_data[SCE_AT9_CONFIG_DATA_SIZE];
|
||||
u32 reserved;
|
||||
/* "fact" sub-chunk */
|
||||
char Subchunk2ID[4]; // "fact" string
|
||||
uint32_t Subchunk2Size; // Sampled data length
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(WavHeader) == 80);
|
||||
|
||||
struct AjmAt9Decoder final : AjmInstance {
|
||||
void* handle;
|
||||
bool decoder_initialized = false;
|
||||
std::fstream file;
|
||||
int length;
|
||||
u8 config_data[SCE_AT9_CONFIG_DATA_SIZE];
|
||||
|
||||
explicit AjmAt9Decoder();
|
||||
~AjmAt9Decoder() override;
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/enum.h"
|
||||
#include "common/types.h"
|
||||
|
||||
extern "C" {
|
||||
@ -19,6 +20,7 @@ enum class AjmCodecType : u32 {
|
||||
M4aacDec = 2,
|
||||
Max = 23,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(AjmCodecType);
|
||||
static constexpr u32 NumAjmCodecs = u32(AjmCodecType::Max);
|
||||
|
||||
enum class AjmFormatEncoding : u32 {
|
||||
@ -28,8 +30,8 @@ enum class AjmFormatEncoding : u32 {
|
||||
};
|
||||
|
||||
struct AjmSidebandResult {
|
||||
s32 result;
|
||||
s32 internal_result;
|
||||
s32 result;
|
||||
s32 internal_result;
|
||||
};
|
||||
|
||||
struct AjmSidebandMFrame {
|
||||
@ -64,8 +66,8 @@ struct AjmInstance {
|
||||
|
||||
virtual void Reset() = 0;
|
||||
|
||||
virtual std::tuple<u32, u32, u32> Decode(const u8* in_buf, u32 in_size,
|
||||
u8* out_buf, u32 out_size) = 0;
|
||||
virtual std::tuple<u32, u32, u32> Decode(const u8* in_buf, u32 in_size, u8* out_buf,
|
||||
u32 out_size) = 0;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Ajm
|
||||
|
@ -15,7 +15,9 @@ namespace Libraries::Ajm {
|
||||
|
||||
// Following tables have been reversed from AJM library
|
||||
static constexpr std::array<std::array<s32, 3>, 3> SamplerateTable = {{
|
||||
{0x5622, 0x5DC0, 0x3E80}, {0xAC44, 0xBB80, 0x7D00}, {0x2B11, 0x2EE0, 0x1F40},
|
||||
{0x5622, 0x5DC0, 0x3E80},
|
||||
{0xAC44, 0xBB80, 0x7D00},
|
||||
{0x2B11, 0x2EE0, 0x1F40},
|
||||
}};
|
||||
|
||||
static constexpr std::array<std::array<s32, 15>, 2> BitrateTable = {{
|
||||
@ -78,14 +80,13 @@ void AjmMp3Decoder::Reset() {
|
||||
file.open(fmt::format("inst{}_{}.raw", index, ++filename), std::ios::out | std::ios::binary);
|
||||
}
|
||||
|
||||
std::tuple<u32, u32, u32> AjmMp3Decoder::Decode(const u8* buf, u32 in_size,
|
||||
u8* out_buf, u32 out_size) {
|
||||
std::tuple<u32, u32, u32> AjmMp3Decoder::Decode(const u8* buf, u32 in_size, u8* out_buf,
|
||||
u32 out_size) {
|
||||
u32 num_frames = 0;
|
||||
AVPacket* pkt = av_packet_alloc();
|
||||
while (in_size > 0 && out_size > 0) {
|
||||
int ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
|
||||
buf, in_size,
|
||||
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
|
||||
int ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, buf, in_size, AV_NOPTS_VALUE,
|
||||
AV_NOPTS_VALUE, 0);
|
||||
ASSERT_MSG(ret >= 0, "Error while parsing {}", ret);
|
||||
buf += ret;
|
||||
in_size -= ret;
|
||||
|
@ -15,17 +15,11 @@ struct AVCodecParserContext;
|
||||
|
||||
namespace Libraries::Ajm {
|
||||
|
||||
enum class AjmDecMp3OflType : u32 {
|
||||
None = 0,
|
||||
Lame = 1,
|
||||
Vbri = 2,
|
||||
Fgh = 3,
|
||||
VbriAndFgh = 4
|
||||
};
|
||||
enum class AjmDecMp3OflType : u32 { None = 0, Lame = 1, Vbri = 2, Fgh = 3, VbriAndFgh = 4 };
|
||||
|
||||
// 11-bit syncword if MPEG 2.5 extensions are enabled
|
||||
static constexpr u8 SYNCWORDH = 0xff;
|
||||
static constexpr u8 SYNCWORDL = 0xe0;
|
||||
static constexpr u8 SYNCWORDL = 0xe0;
|
||||
|
||||
struct AjmDecMp3ParseFrame {
|
||||
u64 frame_size;
|
||||
@ -73,8 +67,8 @@ struct AjmMp3Decoder : public AjmInstance {
|
||||
|
||||
void Reset() override;
|
||||
|
||||
std::tuple<u32, u32, u32> Decode(const u8* in_buf, u32 in_size,
|
||||
u8* out_buf, u32 out_size) override;
|
||||
std::tuple<u32, u32, u32> Decode(const u8* in_buf, u32 in_size, u8* out_buf,
|
||||
u32 out_size) override;
|
||||
|
||||
static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl,
|
||||
AjmDecMp3ParseFrame* frame);
|
||||
|
@ -1,7 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <fmt/chrono.h>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "audio_core/sdl_audio.h"
|
||||
@ -335,6 +338,10 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, const void* ptr) {
|
||||
|
||||
int PS4_SYSV_ABI sceAudioOutOutputs(OrbisAudioOutOutputParam* param, u32 num) {
|
||||
for (u32 i = 0; i < num; i++) {
|
||||
std::fstream file;
|
||||
file.open(fmt::format("handle{}.raw", param[i].handle), std::ios::out | std::ios::binary);
|
||||
file.write(static_cast<const char*>(param[i].ptr), 1024);
|
||||
file.close();
|
||||
if (const auto err = sceAudioOutOutput(param[i].handle, param[i].ptr); err != 0)
|
||||
return err;
|
||||
}
|
||||
|
@ -6,8 +6,10 @@
|
||||
#include "avplayer_impl.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/libkernel.h"
|
||||
#include "core/linker.h"
|
||||
|
||||
using namespace Libraries::Kernel;
|
||||
|
||||
@ -17,28 +19,32 @@ void* PS4_SYSV_ABI AvPlayer::Allocate(void* handle, u32 alignment, u32 size) {
|
||||
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
|
||||
const auto allocate = self->m_init_data_original.memory_replacement.allocate;
|
||||
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
|
||||
return allocate(ptr, alignment, size);
|
||||
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->ExecuteGuest(allocate, ptr, alignment, size);
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI AvPlayer::Deallocate(void* handle, void* memory) {
|
||||
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
|
||||
const auto deallocate = self->m_init_data_original.memory_replacement.deallocate;
|
||||
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
|
||||
return deallocate(ptr, memory);
|
||||
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->ExecuteGuest(deallocate, ptr, memory);
|
||||
}
|
||||
|
||||
void* PS4_SYSV_ABI AvPlayer::AllocateTexture(void* handle, u32 alignment, u32 size) {
|
||||
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
|
||||
const auto allocate = self->m_init_data_original.memory_replacement.allocate_texture;
|
||||
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
|
||||
return allocate(ptr, alignment, size);
|
||||
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->ExecuteGuest(allocate, ptr, alignment, size);
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI AvPlayer::DeallocateTexture(void* handle, void* memory) {
|
||||
const auto* const self = reinterpret_cast<AvPlayer*>(handle);
|
||||
const auto deallocate = self->m_init_data_original.memory_replacement.deallocate_texture;
|
||||
const auto ptr = self->m_init_data_original.memory_replacement.object_ptr;
|
||||
return deallocate(ptr, memory);
|
||||
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->ExecuteGuest(deallocate, ptr, memory);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) {
|
||||
@ -47,7 +53,8 @@ int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) {
|
||||
|
||||
const auto open = self->m_init_data_original.file_replacement.open;
|
||||
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
|
||||
return open(ptr, filename);
|
||||
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->ExecuteGuest(open, ptr, filename);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) {
|
||||
@ -56,7 +63,8 @@ int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) {
|
||||
|
||||
const auto close = self->m_init_data_original.file_replacement.close;
|
||||
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
|
||||
return close(ptr);
|
||||
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->ExecuteGuest(close, ptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length) {
|
||||
@ -65,7 +73,8 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position
|
||||
|
||||
const auto read_offset = self->m_init_data_original.file_replacement.readOffset;
|
||||
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
|
||||
return read_offset(ptr, buffer, position, length);
|
||||
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->ExecuteGuest(read_offset, ptr, buffer, position, length);
|
||||
}
|
||||
|
||||
u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) {
|
||||
@ -74,7 +83,8 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) {
|
||||
|
||||
const auto size = self->m_init_data_original.file_replacement.size;
|
||||
const auto ptr = self->m_init_data_original.file_replacement.object_ptr;
|
||||
return size(ptr);
|
||||
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->ExecuteGuest(size, ptr);
|
||||
}
|
||||
|
||||
SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) {
|
||||
@ -102,7 +112,7 @@ AvPlayer::AvPlayer(const SceAvPlayerInitData& data)
|
||||
m_state(std::make_unique<AvPlayerState>(m_init_data)) {}
|
||||
|
||||
s32 AvPlayer::PostInit(const SceAvPlayerPostInitData& data) {
|
||||
m_post_init_data = data;
|
||||
m_state->PostInit(data);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,6 @@ private:
|
||||
|
||||
SceAvPlayerInitData m_init_data{};
|
||||
SceAvPlayerInitData m_init_data_original{};
|
||||
SceAvPlayerPostInitData m_post_init_data{};
|
||||
std::mutex m_file_io_mutex{};
|
||||
|
||||
std::atomic_bool m_has_source{};
|
||||
|
@ -37,7 +37,8 @@ namespace Libraries::AvPlayer {
|
||||
|
||||
using namespace Kernel;
|
||||
|
||||
AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state) : m_state(state) {}
|
||||
AvPlayerSource::AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2)
|
||||
: m_state(state), m_use_vdec2(use_vdec2) {}
|
||||
|
||||
AvPlayerSource::~AvPlayerSource() {
|
||||
Stop();
|
||||
@ -129,18 +130,25 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info
|
||||
LOG_WARNING(Lib_AvPlayer, "Stream {} language is unknown", stream_index);
|
||||
}
|
||||
switch (info.type) {
|
||||
case SCE_AVPLAYER_VIDEO:
|
||||
case SCE_AVPLAYER_VIDEO: {
|
||||
LOG_INFO(Lib_AvPlayer, "Stream {} is a video stream.", stream_index);
|
||||
info.details.video.aspect_ratio =
|
||||
f32(p_stream->codecpar->width) / p_stream->codecpar->height;
|
||||
info.details.video.width = Common::AlignUp(u32(p_stream->codecpar->width), 16);
|
||||
info.details.video.height = Common::AlignUp(u32(p_stream->codecpar->height), 16);
|
||||
auto width = u32(p_stream->codecpar->width);
|
||||
auto height = u32(p_stream->codecpar->height);
|
||||
if (!m_use_vdec2) {
|
||||
width = Common::AlignUp(width, 16);
|
||||
height = Common::AlignUp(height, 16);
|
||||
}
|
||||
info.details.video.width = width;
|
||||
info.details.video.height = height;
|
||||
if (p_lang_node != nullptr) {
|
||||
std::memcpy(info.details.video.language_code, p_lang_node->value,
|
||||
std::min(strlen(p_lang_node->value), size_t(3)));
|
||||
}
|
||||
break;
|
||||
case SCE_AVPLAYER_AUDIO:
|
||||
}
|
||||
case SCE_AVPLAYER_AUDIO: {
|
||||
LOG_INFO(Lib_AvPlayer, "Stream {} is an audio stream.", stream_index);
|
||||
info.details.audio.channel_count = p_stream->codecpar->ch_layout.nb_channels;
|
||||
info.details.audio.sample_rate = p_stream->codecpar->sample_rate;
|
||||
@ -150,7 +158,8 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info
|
||||
std::min(strlen(p_lang_node->value), size_t(3)));
|
||||
}
|
||||
break;
|
||||
case SCE_AVPLAYER_TIMEDTEXT:
|
||||
}
|
||||
case SCE_AVPLAYER_TIMEDTEXT: {
|
||||
LOG_WARNING(Lib_AvPlayer, "Stream {} is a timedtext stream.", stream_index);
|
||||
info.details.subs.font_size = 12;
|
||||
info.details.subs.text_size = 12;
|
||||
@ -159,10 +168,12 @@ bool AvPlayerSource::GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info
|
||||
std::min(strlen(p_lang_node->value), size_t(3)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
LOG_ERROR(Lib_AvPlayer, "Stream {} type is unknown: {}.", stream_index, info.type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -189,8 +200,12 @@ bool AvPlayerSource::EnableStream(u32 stream_index) {
|
||||
LOG_ERROR(Lib_AvPlayer, "Could not open avcodec for video stream {}.", stream_index);
|
||||
return false;
|
||||
}
|
||||
const auto width = Common::AlignUp(u32(m_video_codec_context->width), 16);
|
||||
const auto height = Common::AlignUp(u32(m_video_codec_context->height), 16);
|
||||
auto width = u32(m_video_codec_context->width);
|
||||
auto height = u32(m_video_codec_context->height);
|
||||
if (!m_use_vdec2) {
|
||||
width = Common::AlignUp(width, 16);
|
||||
height = Common::AlignUp(height, 16);
|
||||
}
|
||||
const auto size = (width * height * 3) / 2;
|
||||
for (u64 index = 0; index < m_num_output_video_framebuffers; ++index) {
|
||||
m_video_buffers.Push(FrameBuffer(m_memory_replacement, 0x100, size));
|
||||
@ -316,7 +331,7 @@ bool AvPlayerSource::GetVideoData(SceAvPlayerFrameInfoEx& video_info) {
|
||||
|
||||
auto frame = m_video_frames.Pop();
|
||||
if (!frame.has_value()) {
|
||||
LOG_WARNING(Lib_AvPlayer, "Could get video frame. EOF reached.");
|
||||
LOG_TRACE(Lib_AvPlayer, "Could get video frame. EOF reached.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -351,7 +366,7 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) {
|
||||
|
||||
auto frame = m_audio_frames.Pop();
|
||||
if (!frame.has_value()) {
|
||||
LOG_WARNING(Lib_AvPlayer, "Could get audio frame. EOF reached.");
|
||||
LOG_TRACE(Lib_AvPlayer, "Could get audio frame. EOF reached.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -537,9 +552,13 @@ AvPlayerSource::AVFramePtr AvPlayerSource::ConvertVideoFrame(const AVFrame& fram
|
||||
return nv12_frame;
|
||||
}
|
||||
|
||||
static void CopyNV12Data(u8* dst, const AVFrame& src) {
|
||||
const auto width = Common::AlignUp(u32(src.width), 16);
|
||||
const auto height = Common::AlignUp(u32(src.height), 16);
|
||||
static void CopyNV12Data(u8* dst, const AVFrame& src, bool use_vdec2) {
|
||||
auto width = u32(src.width);
|
||||
auto height = u32(src.height);
|
||||
if (!use_vdec2) {
|
||||
width = Common::AlignUp(width, 16);
|
||||
height = Common::AlignUp(height, 16);
|
||||
}
|
||||
|
||||
if (src.width == width) {
|
||||
std::memcpy(dst, src.data[0], src.width * src.height);
|
||||
@ -561,7 +580,7 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame
|
||||
ASSERT(frame.format == AV_PIX_FMT_NV12);
|
||||
|
||||
auto p_buffer = buffer.GetBuffer();
|
||||
CopyNV12Data(p_buffer, frame);
|
||||
CopyNV12Data(p_buffer, frame, m_use_vdec2);
|
||||
|
||||
const auto pkt_dts = u64(frame.pkt_dts) * 1000;
|
||||
const auto stream = m_avformat_context->streams[m_video_stream_index.value()];
|
||||
@ -570,8 +589,12 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame
|
||||
const auto num = time_base.num;
|
||||
const auto timestamp = (num != 0 && den > 1) ? (pkt_dts * num) / den : pkt_dts;
|
||||
|
||||
const auto width = Common::AlignUp(u32(frame.width), 16);
|
||||
const auto height = Common::AlignUp(u32(frame.height), 16);
|
||||
auto width = u32(frame.width);
|
||||
auto height = u32(frame.height);
|
||||
if (!m_use_vdec2) {
|
||||
width = Common::AlignUp(width, 16);
|
||||
height = Common::AlignUp(height, 16);
|
||||
}
|
||||
|
||||
return Frame{
|
||||
.buffer = std::move(buffer),
|
||||
@ -583,8 +606,8 @@ Frame AvPlayerSource::PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame
|
||||
{
|
||||
.video =
|
||||
{
|
||||
.width = u32(width),
|
||||
.height = u32(height),
|
||||
.width = width,
|
||||
.height = height,
|
||||
.aspect_ratio = AVRationalToF32(frame.sample_aspect_ratio),
|
||||
.crop_left_offset = u32(frame.crop_left),
|
||||
.crop_right_offset = u32(frame.crop_right + (width - frame.width)),
|
||||
|
@ -120,7 +120,7 @@ private:
|
||||
|
||||
class AvPlayerSource {
|
||||
public:
|
||||
AvPlayerSource(AvPlayerStateCallback& state);
|
||||
AvPlayerSource(AvPlayerStateCallback& state, bool use_vdec2);
|
||||
~AvPlayerSource();
|
||||
|
||||
bool Init(const SceAvPlayerInitData& init_data, std::string_view path);
|
||||
@ -168,6 +168,7 @@ private:
|
||||
Frame PrepareVideoFrame(FrameBuffer buffer, const AVFrame& frame);
|
||||
|
||||
AvPlayerStateCallback& m_state;
|
||||
bool m_use_vdec2 = false;
|
||||
|
||||
SceAvPlayerMemAllocator m_memory_replacement{};
|
||||
u32 m_num_output_video_framebuffers{};
|
||||
|
@ -87,7 +87,12 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass other events to the game
|
||||
DefaultEventCallback(opaque, event_id, 0, event_data);
|
||||
}
|
||||
|
||||
void AvPlayerState::DefaultEventCallback(void* opaque, SceAvPlayerEvents event_id, s32 source_id,
|
||||
void* event_data) {
|
||||
auto const self = reinterpret_cast<AvPlayerState*>(opaque);
|
||||
const auto callback = self->m_event_replacement.event_callback;
|
||||
const auto ptr = self->m_event_replacement.object_ptr;
|
||||
if (callback != nullptr) {
|
||||
@ -102,8 +107,10 @@ AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data)
|
||||
if (m_event_replacement.event_callback == nullptr || init_data.auto_start) {
|
||||
m_auto_start = true;
|
||||
m_init_data.event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback;
|
||||
m_init_data.event_replacement.object_ptr = this;
|
||||
} else {
|
||||
m_init_data.event_replacement.event_callback = &AvPlayerState::DefaultEventCallback;
|
||||
}
|
||||
m_init_data.event_replacement.object_ptr = this;
|
||||
if (init_data.default_language != nullptr) {
|
||||
std::memcpy(m_default_language, init_data.default_language, sizeof(m_default_language));
|
||||
}
|
||||
@ -123,6 +130,10 @@ AvPlayerState::~AvPlayerState() {
|
||||
m_event_queue.Clear();
|
||||
}
|
||||
|
||||
void AvPlayerState::PostInit(const SceAvPlayerPostInitData& post_init_data) {
|
||||
m_post_init_data = post_init_data;
|
||||
}
|
||||
|
||||
// Called inside GAME thread
|
||||
bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType source_type) {
|
||||
if (path.empty()) {
|
||||
@ -137,7 +148,9 @@ bool AvPlayerState::AddSource(std::string_view path, SceAvPlayerSourceType sourc
|
||||
return false;
|
||||
}
|
||||
|
||||
m_up_source = std::make_unique<AvPlayerSource>(*this);
|
||||
m_up_source = std::make_unique<AvPlayerSource>(
|
||||
*this, m_post_init_data.video_decoder_init.decoderType.video_type ==
|
||||
SCE_AVPLAYER_VIDEO_DECODER_TYPE_SOFTWARE2);
|
||||
if (!m_up_source->Init(m_init_data, path)) {
|
||||
SetState(AvState::Error);
|
||||
m_up_source.reset();
|
||||
@ -367,8 +380,7 @@ void AvPlayerState::EmitEvent(SceAvPlayerEvents event_id, void* event_data) {
|
||||
const auto callback = m_init_data.event_replacement.event_callback;
|
||||
if (callback) {
|
||||
const auto ptr = m_init_data.event_replacement.object_ptr;
|
||||
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
linker->ExecuteGuest(callback, ptr, event_id, 0, event_data);
|
||||
callback(ptr, event_id, 0, event_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ public:
|
||||
AvPlayerState(const SceAvPlayerInitData& init_data);
|
||||
~AvPlayerState();
|
||||
|
||||
void PostInit(const SceAvPlayerPostInitData& post_init_data);
|
||||
bool AddSource(std::string_view filename, SceAvPlayerSourceType source_type);
|
||||
s32 GetStreamCount();
|
||||
bool GetStreamInfo(u32 stream_index, SceAvPlayerStreamInfo& info);
|
||||
@ -42,6 +43,9 @@ private:
|
||||
static void PS4_SYSV_ABI AutoPlayEventCallback(void* handle, SceAvPlayerEvents event_id,
|
||||
s32 source_id, void* event_data);
|
||||
|
||||
static void PS4_SYSV_ABI DefaultEventCallback(void* handle, SceAvPlayerEvents event_id,
|
||||
s32 source_id, void* event_data);
|
||||
|
||||
void OnWarning(u32 id) override;
|
||||
void OnError() override;
|
||||
void OnEOF() override;
|
||||
@ -65,6 +69,7 @@ private:
|
||||
std::unique_ptr<AvPlayerSource> m_up_source;
|
||||
|
||||
SceAvPlayerInitData m_init_data{};
|
||||
SceAvPlayerPostInitData m_post_init_data{};
|
||||
SceAvPlayerEventReplacement m_event_replacement{};
|
||||
bool m_auto_start{};
|
||||
u8 m_default_language[4]{};
|
||||
|
@ -75,7 +75,7 @@ public:
|
||||
std::min(io.DisplaySize.y, 300.0f),
|
||||
};
|
||||
|
||||
CentralizeWindow();
|
||||
CentralizeNextWindow();
|
||||
SetNextWindowSize(window_size);
|
||||
SetNextWindowCollapsed(false);
|
||||
if (first_render || !io.NavActive) {
|
||||
|
@ -1,28 +1,75 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <magic_enum.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "ime_dialog.h"
|
||||
#include "ime_dialog_ui.h"
|
||||
|
||||
static constexpr std::array<float, 2> MAX_X_POSITIONS = {3840.0f, 1920.0f};
|
||||
static constexpr std::array<float, 2> MAX_Y_POSITIONS = {2160.0f, 1080.0f};
|
||||
|
||||
namespace Libraries::ImeDialog {
|
||||
|
||||
static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_NONE;
|
||||
static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::NONE;
|
||||
static OrbisImeDialogResult g_ime_dlg_result{};
|
||||
static ImeDialogState g_ime_dlg_state{};
|
||||
static ImeDialogUi g_ime_dlg_ui;
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogAbort() {
|
||||
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) {
|
||||
if (False(~option &
|
||||
(OrbisImeDialogOption::MULTILINE | OrbisImeDialogOption::NO_AUTO_COMPLETION))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (True(option & OrbisImeDialogOption::MULTILINE) && type != OrbisImeType::DEFAULT &&
|
||||
type != OrbisImeType::BASIC_LATIN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (True(option & OrbisImeDialogOption::NO_AUTO_COMPLETION) && type != OrbisImeType::NUMBER &&
|
||||
type != OrbisImeType::BASIC_LATIN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogForceClose() {
|
||||
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceImeDialogAbort() {
|
||||
if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) {
|
||||
LOG_INFO(Lib_ImeDialog, "IME dialog not in use");
|
||||
return Error::DIALOG_NOT_IN_USE;
|
||||
}
|
||||
|
||||
if (g_ime_dlg_status != OrbisImeDialogStatus::RUNNING) {
|
||||
LOG_INFO(Lib_ImeDialog, "IME dialog not running");
|
||||
return Error::DIALOG_NOT_RUNNING;
|
||||
}
|
||||
|
||||
g_ime_dlg_status = OrbisImeDialogStatus::FINISHED;
|
||||
g_ime_dlg_result.endstatus = OrbisImeDialogEndStatus::ABORTED;
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogForTestFunction() {
|
||||
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceImeDialogForceClose() {
|
||||
if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) {
|
||||
LOG_INFO(Lib_ImeDialog, "IME dialog not in use");
|
||||
return Error::DIALOG_NOT_IN_USE;
|
||||
}
|
||||
|
||||
g_ime_dlg_status = OrbisImeDialogStatus::NONE;
|
||||
g_ime_dlg_ui = ImeDialogUi();
|
||||
g_ime_dlg_state = ImeDialogState();
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceImeDialogForTestFunction() {
|
||||
return Error::INTERNAL;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogGetCurrentStarState() {
|
||||
@ -45,26 +92,118 @@ int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) {
|
||||
result->endstatus = OrbisImeDialogEndStatus::ORBIS_IME_DIALOG_END_STATUS_OK;
|
||||
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) {
|
||||
if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) {
|
||||
LOG_INFO(Lib_ImeDialog, "IME dialog is not running");
|
||||
return Error::DIALOG_NOT_IN_USE;
|
||||
}
|
||||
|
||||
if (result == nullptr) {
|
||||
LOG_INFO(Lib_ImeDialog, "called with result (NULL)");
|
||||
return Error::INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
result->endstatus = g_ime_dlg_result.endstatus;
|
||||
|
||||
if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) {
|
||||
return Error::DIALOG_NOT_FINISHED;
|
||||
}
|
||||
|
||||
g_ime_dlg_state.CopyTextToOrbisBuffer();
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogGetStatus() {
|
||||
if (g_ime_dlg_status == OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_RUNNING) {
|
||||
return OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_FINISHED;
|
||||
OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() {
|
||||
if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) {
|
||||
g_ime_dlg_state.CallTextFilter();
|
||||
}
|
||||
|
||||
return g_ime_dlg_status;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) {
|
||||
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called");
|
||||
const std::wstring_view text = L"shadPS4";
|
||||
param->maxTextLength = text.size();
|
||||
std::memcpy(param->inputTextBuffer, text.data(), text.size() * sizeof(wchar_t));
|
||||
g_ime_dlg_status = OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_RUNNING;
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) {
|
||||
if (g_ime_dlg_status != OrbisImeDialogStatus::NONE) {
|
||||
LOG_INFO(Lib_ImeDialog, "IME dialog is already running");
|
||||
return Error::BUSY;
|
||||
}
|
||||
|
||||
if (param == nullptr) {
|
||||
LOG_INFO(Lib_ImeDialog, "called with param (NULL)");
|
||||
return Error::INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (!magic_enum::enum_contains(param->type)) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid param->type");
|
||||
return Error::INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
// TODO: do correct param->option validation
|
||||
// TODO: do correct param->supportedLanguages validation
|
||||
|
||||
if (param->posx < 0.0f ||
|
||||
param->posx >=
|
||||
MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid param->posx");
|
||||
return Error::INVALID_POSX;
|
||||
}
|
||||
|
||||
if (param->posy < 0.0f ||
|
||||
param->posy >=
|
||||
MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid param->posy");
|
||||
return Error::INVALID_POSY;
|
||||
}
|
||||
|
||||
if (!magic_enum::enum_contains(param->horizontalAlignment)) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid param->horizontalAlignment");
|
||||
return Error::INVALID_HORIZONTALIGNMENT;
|
||||
}
|
||||
|
||||
if (!magic_enum::enum_contains(param->verticalAlignment)) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid param->verticalAlignment");
|
||||
return Error::INVALID_VERTICALALIGNMENT;
|
||||
}
|
||||
|
||||
if (!IsValidOption(param->option, param->type)) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid param->option");
|
||||
return Error::INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (param->inputTextBuffer == nullptr) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid param->inputTextBuffer");
|
||||
return Error::INVALID_INPUT_TEXT_BUFFER;
|
||||
}
|
||||
|
||||
if (extended) {
|
||||
if (!magic_enum::enum_contains(extended->priority)) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid extended->priority");
|
||||
return Error::INVALID_EXTENDED;
|
||||
}
|
||||
|
||||
// TODO: do correct extended->option validation
|
||||
|
||||
if ((extended->extKeyboardMode & 0xe3fffffc) != 0) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid extended->extKeyboardMode");
|
||||
return Error::INVALID_EXTENDED;
|
||||
}
|
||||
|
||||
if (extended->disableDevice > 7) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid extended->disableDevice");
|
||||
return Error::INVALID_EXTENDED;
|
||||
}
|
||||
}
|
||||
|
||||
if (param->maxTextLength > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) {
|
||||
LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength");
|
||||
return Error::INVALID_MAX_TEXT_LENGTH;
|
||||
}
|
||||
|
||||
g_ime_dlg_result = {};
|
||||
g_ime_dlg_state = ImeDialogState(param, extended);
|
||||
g_ime_dlg_status = OrbisImeDialogStatus::RUNNING;
|
||||
g_ime_dlg_ui = ImeDialogUi(&g_ime_dlg_state, &g_ime_dlg_status, &g_ime_dlg_result);
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogInitInternal() {
|
||||
@ -87,10 +226,22 @@ int PS4_SYSV_ABI sceImeDialogSetPanelPosition() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogTerm() {
|
||||
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called");
|
||||
g_ime_dlg_status = OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_NONE;
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceImeDialogTerm() {
|
||||
if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) {
|
||||
LOG_INFO(Lib_ImeDialog, "IME dialog not in use");
|
||||
return Error::DIALOG_NOT_IN_USE;
|
||||
}
|
||||
|
||||
if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) {
|
||||
LOG_INFO(Lib_ImeDialog, "IME dialog is still running");
|
||||
return Error::DIALOG_NOT_FINISHED;
|
||||
}
|
||||
|
||||
g_ime_dlg_status = OrbisImeDialogStatus::NONE;
|
||||
g_ime_dlg_ui = ImeDialogUi();
|
||||
g_ime_dlg_state = ImeDialogState();
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/enum.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
@ -11,71 +12,150 @@ class SymbolsResolver;
|
||||
|
||||
namespace Libraries::ImeDialog {
|
||||
|
||||
enum OrbisImeDialogStatus {
|
||||
ORBIS_IME_DIALOG_STATUS_NONE = 0,
|
||||
ORBIS_IME_DIALOG_STATUS_RUNNING = 1,
|
||||
ORBIS_IME_DIALOG_STATUS_FINISHED = 2
|
||||
constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 0x78;
|
||||
|
||||
enum class Error : u32 {
|
||||
OK = 0x0,
|
||||
BUSY = 0x80bc0001,
|
||||
NOT_OPENED = 0x80bc0002,
|
||||
NO_MEMORY = 0x80bc0003,
|
||||
CONNECTION_FAILED = 0x80bc0004,
|
||||
TOO_MANY_REQUESTS = 0x80bc0005,
|
||||
INVALID_TEXT = 0x80bc0006,
|
||||
EVENT_OVERFLOW = 0x80bc0007,
|
||||
NOT_ACTIVE = 0x80bc0008,
|
||||
IME_SUSPENDING = 0x80bc0009,
|
||||
DEVICE_IN_USE = 0x80bc000a,
|
||||
INVALID_USER_ID = 0x80bc0010,
|
||||
INVALID_TYPE = 0x80bc0011,
|
||||
INVALID_SUPPORTED_LANGUAGES = 0x80bc0012,
|
||||
INVALID_ENTER_LABEL = 0x80bc0013,
|
||||
INVALID_INPUT_METHOD = 0x80bc0014,
|
||||
INVALID_OPTION = 0x80bc0015,
|
||||
INVALID_MAX_TEXT_LENGTH = 0x80bc0016,
|
||||
INVALID_INPUT_TEXT_BUFFER = 0x80bc0017,
|
||||
INVALID_POSX = 0x80bc0018,
|
||||
INVALID_POSY = 0x80bc0019,
|
||||
INVALID_HORIZONTALIGNMENT = 0x80bc001a,
|
||||
INVALID_VERTICALALIGNMENT = 0x80bc001b,
|
||||
INVALID_EXTENDED = 0x80bc001c,
|
||||
INVALID_KEYBOARD_TYPE = 0x80bc001d,
|
||||
INVALID_WORK = 0x80bc0020,
|
||||
INVALID_ARG = 0x80bc0021,
|
||||
INVALID_HANDLER = 0x80bc0022,
|
||||
NO_RESOURCE_ID = 0x80bc0023,
|
||||
INVALID_MODE = 0x80bc0024,
|
||||
INVALID_PARAM = 0x80bc0030,
|
||||
INVALID_ADDRESS = 0x80bc0031,
|
||||
INVALID_RESERVED = 0x80bc0032,
|
||||
INVALID_TIMING = 0x80bc0033,
|
||||
INTERNAL = 0x80bc00ff,
|
||||
DIALOG_INVALID_TITLE = 0x80bc0101,
|
||||
DIALOG_NOT_RUNNING = 0x80bc0105,
|
||||
DIALOG_NOT_FINISHED = 0x80bc0106,
|
||||
DIALOG_NOT_IN_USE = 0x80bc0107,
|
||||
};
|
||||
|
||||
enum OrbisImeDialogEndStatus {
|
||||
ORBIS_IME_DIALOG_END_STATUS_OK = 0,
|
||||
ORBIS_IME_DIALOG_END_STATUS_USER_CANCELED = 1,
|
||||
ORBIS_IME_DIALOG_END_STATUS_ABORTED = 2
|
||||
enum class OrbisImeDialogStatus : u32 {
|
||||
NONE = 0,
|
||||
RUNNING = 1,
|
||||
FINISHED = 2,
|
||||
};
|
||||
|
||||
struct OrbisImeDialogResult {
|
||||
OrbisImeDialogEndStatus endstatus;
|
||||
s32 reserved[12];
|
||||
enum class OrbisImeDialogEndStatus : u32 {
|
||||
OK = 0,
|
||||
USER_CANCELED = 1,
|
||||
ABORTED = 2,
|
||||
};
|
||||
|
||||
enum OrbisImeType {
|
||||
ORBIS_IME_TYPE_DEFAULT = 0,
|
||||
ORBIS_IME_TYPE_BASIC_LATIN = 1,
|
||||
ORBIS_IME_TYPE_URL = 2,
|
||||
ORBIS_IME_TYPE_MAIL = 3,
|
||||
ORBIS_IME_TYPE_NUMBER = 4
|
||||
enum class OrbisImeType : u32 {
|
||||
DEFAULT = 0,
|
||||
BASIC_LATIN = 1,
|
||||
URL = 2,
|
||||
MAIL = 3,
|
||||
NUMBER = 4,
|
||||
};
|
||||
|
||||
enum OrbisImeEnterLabel {
|
||||
ORBIS_IME_ENTER_LABEL_DEFAULT = 0,
|
||||
ORBIS_IME_ENTER_LABEL_SEND = 1,
|
||||
ORBIS_IME_ENTER_LABEL_SEARCH = 2,
|
||||
ORBIS_IME_ENTER_LABEL_GO = 3
|
||||
};
|
||||
enum OrbiImeInputMethod { ORBIS_IME_INPUT_METHOD_DEFAULT = 0 };
|
||||
|
||||
typedef int (*OrbisImeTextFilter)(wchar_t* outText, u32* outTextLength, const wchar_t* srcText,
|
||||
u32 srcTextLength);
|
||||
|
||||
enum OrbisImeHorizontalAlignment {
|
||||
ORBIS_IME_HALIGN_LEFT = 0,
|
||||
ORBIS_IME_HALIGN_CENTER = 1,
|
||||
ORBIS_IME_HALIGN_RIGHT = 2
|
||||
enum class OrbisImeEnterLabel : u32 {
|
||||
DEFAULT = 0,
|
||||
SEND = 1,
|
||||
SEARCH = 2,
|
||||
GO = 3,
|
||||
};
|
||||
|
||||
enum OrbisImeVerticalAlignment {
|
||||
ORBIS_IME_VALIGN_TOP = 0,
|
||||
ORBIS_IME_VALIGN_CENTER = 1,
|
||||
ORBIS_IME_VALIGN_BOTTOM = 2
|
||||
enum class OrbisImeDialogOption : u32 {
|
||||
DEFAULT = 0,
|
||||
MULTILINE = 1,
|
||||
NO_AUTO_CORRECTION = 2,
|
||||
NO_AUTO_COMPLETION = 4,
|
||||
// TODO: Document missing options
|
||||
LARGE_RESOLUTION = 1024,
|
||||
};
|
||||
|
||||
struct OrbisImeDialogParam {
|
||||
s32 userId;
|
||||
OrbisImeType type;
|
||||
u64 supportedLanguages;
|
||||
OrbisImeEnterLabel enterLabel;
|
||||
OrbiImeInputMethod inputMethod;
|
||||
OrbisImeTextFilter filter;
|
||||
u32 option;
|
||||
u32 maxTextLength;
|
||||
wchar_t* inputTextBuffer;
|
||||
float posx;
|
||||
float posy;
|
||||
OrbisImeHorizontalAlignment horizontalAlignment;
|
||||
OrbisImeVerticalAlignment verticalAlignment;
|
||||
const wchar_t* placeholder;
|
||||
const wchar_t* title;
|
||||
s8 reserved[16];
|
||||
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption)
|
||||
|
||||
enum class OrbisImeInputMethod : u32 {
|
||||
DEFAULT = 0,
|
||||
};
|
||||
|
||||
enum class OrbisImeHorizontalAlignment : u32 {
|
||||
LEFT = 0,
|
||||
CENTER = 1,
|
||||
RIGHT = 2,
|
||||
};
|
||||
|
||||
enum class OrbisImeVerticalAlignment : u32 {
|
||||
TOP = 0,
|
||||
CENTER = 1,
|
||||
BOTTOM = 2,
|
||||
};
|
||||
|
||||
enum class OrbisImePanelPriority : u32 {
|
||||
DEFAULT = 0,
|
||||
ALPHABET = 1,
|
||||
SYMBOL = 2,
|
||||
ACCENT = 3,
|
||||
};
|
||||
|
||||
enum class OrbisImeKeyboardType : u32 {
|
||||
NONE = 0,
|
||||
DANISH = 1,
|
||||
GERMAN = 2,
|
||||
GERMAN_SW = 3,
|
||||
ENGLISH_US = 4,
|
||||
ENGLISH_GB = 5,
|
||||
SPANISH = 6,
|
||||
SPANISH_LA = 7,
|
||||
FINNISH = 8,
|
||||
FRENCH = 9,
|
||||
FRENCH_BR = 10,
|
||||
FRENCH_CA = 11,
|
||||
FRENCH_SW = 12,
|
||||
ITALIAN = 13,
|
||||
DUTCH = 14,
|
||||
NORWEGIAN = 15,
|
||||
POLISH = 16,
|
||||
PORTUGUESE_BR = 17,
|
||||
PORTUGUESE_PT = 18,
|
||||
RUSSIAN = 19,
|
||||
SWEDISH = 20,
|
||||
TURKISH = 21,
|
||||
JAPANESE_ROMAN = 22,
|
||||
JAPANESE_KANA = 23,
|
||||
KOREAN = 24,
|
||||
SM_CHINESE = 25,
|
||||
TR_CHINESE_ZY = 26,
|
||||
TR_CHINESE_PY_HK = 27,
|
||||
TR_CHINESE_PY_TW = 28,
|
||||
TR_CHINESE_CG = 29,
|
||||
ARABIC_AR = 30,
|
||||
THAI = 31,
|
||||
CZECH = 32,
|
||||
GREEK = 33,
|
||||
INDONESIAN = 34,
|
||||
VIETNAMESE = 35,
|
||||
ROMANIAN = 36,
|
||||
HUNGARIAN = 37,
|
||||
};
|
||||
|
||||
struct OrbisImeColor {
|
||||
@ -85,57 +165,14 @@ struct OrbisImeColor {
|
||||
u8 a;
|
||||
};
|
||||
|
||||
enum OrbisImePanelPriority {
|
||||
ORBIS_IME_PANEL_PRIORITY_DEFAULT = 0,
|
||||
ORBIS_IME_PANEL_PRIORITY_ALPHABET = 1,
|
||||
ORBIS_IME_PANEL_PRIORITY_SYMBOL = 2,
|
||||
ORBIS_IME_PANEL_PRIORITY_ACCENT = 3
|
||||
};
|
||||
|
||||
enum OrbisImeKeyboardType {
|
||||
ORBIS_IME_KEYBOARD_TYPE_NONE = 0,
|
||||
ORBIS_IME_KEYBOARD_TYPE_DANISH = 1,
|
||||
ORBIS_IME_KEYBOARD_TYPE_GERMAN = 2,
|
||||
ORBIS_IME_KEYBOARD_TYPE_GERMAN_SW = 3,
|
||||
ORBIS_IME_KEYBOARD_TYPE_ENGLISH_US = 4,
|
||||
ORBIS_IME_KEYBOARD_TYPE_ENGLISH_GB = 5,
|
||||
ORBIS_IME_KEYBOARD_TYPE_SPANISH = 6,
|
||||
ORBIS_IME_KEYBOARD_TYPE_SPANISH_LA = 7,
|
||||
ORBIS_IME_KEYBOARD_TYPE_FINNISH = 8,
|
||||
ORBIS_IME_KEYBOARD_TYPE_FRENCH = 9,
|
||||
ORBIS_IME_KEYBOARD_TYPE_FRENCH_BR = 10,
|
||||
ORBIS_IME_KEYBOARD_TYPE_FRENCH_CA = 11,
|
||||
ORBIS_IME_KEYBOARD_TYPE_FRENCH_SW = 12,
|
||||
ORBIS_IME_KEYBOARD_TYPE_ITALIAN = 13,
|
||||
ORBIS_IME_KEYBOARD_TYPE_DUTCH = 14,
|
||||
ORBIS_IME_KEYBOARD_TYPE_NORWEGIAN = 15,
|
||||
ORBIS_IME_KEYBOARD_TYPE_POLISH = 16,
|
||||
ORBIS_IME_KEYBOARD_TYPE_PORTUGUESE_BR = 17,
|
||||
ORBIS_IME_KEYBOARD_TYPE_PORTUGUESE_PT = 18,
|
||||
ORBIS_IME_KEYBOARD_TYPE_RUSSIAN = 19,
|
||||
ORBIS_IME_KEYBOARD_TYPE_SWEDISH = 20,
|
||||
ORBIS_IME_KEYBOARD_TYPE_TURKISH = 21,
|
||||
ORBIS_IME_KEYBOARD_TYPE_JAPANESE_ROMAN = 22,
|
||||
ORBIS_IME_KEYBOARD_TYPE_JAPANESE_KANA = 23,
|
||||
ORBIS_IME_KEYBOARD_TYPE_KOREAN = 24,
|
||||
ORBIS_IME_KEYBOARD_TYPE_SM_CHINESE = 25,
|
||||
ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_ZY = 26,
|
||||
ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_PY_HK = 27,
|
||||
ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_PY_TW = 28,
|
||||
ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_CG = 29,
|
||||
ORBIS_IME_KEYBOARD_TYPE_ARABIC_AR = 30,
|
||||
ORBIS_IME_KEYBOARD_TYPE_THAI = 31,
|
||||
ORBIS_IME_KEYBOARD_TYPE_CZECH = 32,
|
||||
ORBIS_IME_KEYBOARD_TYPE_GREEK = 33,
|
||||
ORBIS_IME_KEYBOARD_TYPE_INDONESIAN = 34,
|
||||
ORBIS_IME_KEYBOARD_TYPE_VIETNAMESE = 35,
|
||||
ORBIS_IME_KEYBOARD_TYPE_ROMANIAN = 36,
|
||||
ORBIS_IME_KEYBOARD_TYPE_HUNGARIAN = 37
|
||||
struct OrbisImeDialogResult {
|
||||
OrbisImeDialogEndStatus endstatus;
|
||||
s32 reserved[12];
|
||||
};
|
||||
|
||||
struct OrbisImeKeycode {
|
||||
u16 keycode;
|
||||
wchar_t character;
|
||||
char16_t character;
|
||||
u32 status;
|
||||
OrbisImeKeyboardType type;
|
||||
s32 userId;
|
||||
@ -143,11 +180,34 @@ struct OrbisImeKeycode {
|
||||
u64 timestamp;
|
||||
};
|
||||
|
||||
typedef int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode, u16* outKeycode,
|
||||
u32* outStatus, void* reserved);
|
||||
typedef PS4_SYSV_ABI int (*OrbisImeTextFilter)(char16_t* outText, u32* outTextLength,
|
||||
const char16_t* srcText, u32 srcTextLength);
|
||||
|
||||
typedef PS4_SYSV_ABI int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode,
|
||||
u16* outKeycode, u32* outStatus,
|
||||
void* reserved);
|
||||
|
||||
struct OrbisImeDialogParam {
|
||||
s32 userId;
|
||||
OrbisImeType type;
|
||||
u64 supportedLanguages;
|
||||
OrbisImeEnterLabel enterLabel;
|
||||
OrbisImeInputMethod inputMethod;
|
||||
OrbisImeTextFilter filter;
|
||||
OrbisImeDialogOption option;
|
||||
u32 maxTextLength;
|
||||
char16_t* inputTextBuffer;
|
||||
float posx;
|
||||
float posy;
|
||||
OrbisImeHorizontalAlignment horizontalAlignment;
|
||||
OrbisImeVerticalAlignment verticalAlignment;
|
||||
const char16_t* placeholder;
|
||||
const char16_t* title;
|
||||
s8 reserved[16];
|
||||
};
|
||||
|
||||
struct OrbisImeParamExtended {
|
||||
u32 option;
|
||||
u32 option; // OrbisImeDialogOptionExtended
|
||||
OrbisImeColor colorBase;
|
||||
OrbisImeColor colorLine;
|
||||
OrbisImeColor colorTextField;
|
||||
@ -165,21 +225,21 @@ struct OrbisImeParamExtended {
|
||||
int8_t reserved[60];
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceImeDialogAbort();
|
||||
int PS4_SYSV_ABI sceImeDialogForceClose();
|
||||
int PS4_SYSV_ABI sceImeDialogForTestFunction();
|
||||
Error PS4_SYSV_ABI sceImeDialogAbort();
|
||||
Error PS4_SYSV_ABI sceImeDialogForceClose();
|
||||
Error PS4_SYSV_ABI sceImeDialogForTestFunction();
|
||||
int PS4_SYSV_ABI sceImeDialogGetCurrentStarState();
|
||||
int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm();
|
||||
int PS4_SYSV_ABI sceImeDialogGetPanelSize();
|
||||
int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended();
|
||||
int PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result);
|
||||
/*OrbisImeDialogStatus*/ int PS4_SYSV_ABI sceImeDialogGetStatus();
|
||||
int PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended);
|
||||
Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result);
|
||||
OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus();
|
||||
Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended);
|
||||
int PS4_SYSV_ABI sceImeDialogInitInternal();
|
||||
int PS4_SYSV_ABI sceImeDialogInitInternal2();
|
||||
int PS4_SYSV_ABI sceImeDialogInitInternal3();
|
||||
int PS4_SYSV_ABI sceImeDialogSetPanelPosition();
|
||||
int PS4_SYSV_ABI sceImeDialogTerm();
|
||||
Error PS4_SYSV_ABI sceImeDialogTerm();
|
||||
|
||||
void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::ImeDialog
|
390
src/core/libraries/dialogs/ime_dialog_ui.cpp
Normal file
390
src/core/libraries/dialogs/ime_dialog_ui.cpp
Normal file
@ -0,0 +1,390 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cwchar>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/libraries/dialogs/ime_dialog.h"
|
||||
#include "core/libraries/dialogs/ime_dialog_ui.h"
|
||||
#include "core/linker.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
|
||||
using namespace ImGui;
|
||||
|
||||
static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
|
||||
|
||||
namespace Libraries::ImeDialog {
|
||||
|
||||
ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param,
|
||||
const OrbisImeParamExtended* extended) {
|
||||
if (!param)
|
||||
return;
|
||||
|
||||
userId = param->userId;
|
||||
is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE);
|
||||
is_numeric = param->type == OrbisImeType::NUMBER;
|
||||
type = param->type;
|
||||
enter_label = param->enterLabel;
|
||||
text_filter = param->filter;
|
||||
keyboard_filter = extended ? extended->extKeyboardFilter : nullptr;
|
||||
max_text_length = param->maxTextLength;
|
||||
text_buffer = param->inputTextBuffer;
|
||||
|
||||
if (param->title) {
|
||||
std::size_t title_len = std::char_traits<char16_t>::length(param->title);
|
||||
title.resize(title_len * 4 + 1);
|
||||
title[title_len * 4] = '\0';
|
||||
|
||||
if (!ConvertOrbisToUTF8(param->title, title_len, &title[0], title_len * 4)) {
|
||||
LOG_ERROR(Lib_ImeDialog, "Failed to convert title to utf8 encoding");
|
||||
}
|
||||
}
|
||||
|
||||
if (param->placeholder) {
|
||||
std::size_t placeholder_len = std::char_traits<char16_t>::length(param->placeholder);
|
||||
placeholder.resize(placeholder_len * 4 + 1);
|
||||
placeholder[placeholder_len * 4] = '\0';
|
||||
|
||||
if (!ConvertOrbisToUTF8(param->placeholder, placeholder_len, &placeholder[0],
|
||||
placeholder_len * 4)) {
|
||||
LOG_ERROR(Lib_ImeDialog, "Failed to convert placeholder to utf8 encoding");
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t text_len = std::char_traits<char16_t>::length(text_buffer);
|
||||
if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(),
|
||||
ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4)) {
|
||||
LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding");
|
||||
}
|
||||
}
|
||||
|
||||
ImeDialogState::ImeDialogState(ImeDialogState&& other) noexcept
|
||||
: input_changed(other.input_changed), userId(other.userId), is_multiLine(other.is_multiLine),
|
||||
is_numeric(other.is_numeric), type(other.type), enter_label(other.enter_label),
|
||||
text_filter(other.text_filter), keyboard_filter(other.keyboard_filter),
|
||||
max_text_length(other.max_text_length), text_buffer(other.text_buffer),
|
||||
title(std::move(other.title)), placeholder(std::move(other.placeholder)),
|
||||
current_text(other.current_text) {
|
||||
|
||||
other.text_buffer = nullptr;
|
||||
}
|
||||
|
||||
ImeDialogState& ImeDialogState::operator=(ImeDialogState&& other) {
|
||||
if (this != &other) {
|
||||
input_changed = other.input_changed;
|
||||
userId = other.userId;
|
||||
is_multiLine = other.is_multiLine;
|
||||
is_numeric = other.is_numeric;
|
||||
type = other.type;
|
||||
enter_label = other.enter_label;
|
||||
text_filter = other.text_filter;
|
||||
keyboard_filter = other.keyboard_filter;
|
||||
max_text_length = other.max_text_length;
|
||||
text_buffer = other.text_buffer;
|
||||
title = std::move(other.title);
|
||||
placeholder = std::move(other.placeholder);
|
||||
current_text = other.current_text;
|
||||
|
||||
other.text_buffer = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool ImeDialogState::CopyTextToOrbisBuffer() {
|
||||
if (!text_buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ConvertUTF8ToOrbis(current_text.begin(), current_text.capacity(), text_buffer,
|
||||
max_text_length);
|
||||
}
|
||||
|
||||
bool ImeDialogState::CallTextFilter() {
|
||||
if (!text_filter || !input_changed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
input_changed = false;
|
||||
|
||||
char16_t src_text[ORBIS_IME_DIALOG_MAX_TEXT_LENGTH + 1] = {0};
|
||||
u32 src_text_length = current_text.size();
|
||||
char16_t out_text[ORBIS_IME_DIALOG_MAX_TEXT_LENGTH + 1] = {0};
|
||||
u32 out_text_length = ORBIS_IME_DIALOG_MAX_TEXT_LENGTH;
|
||||
|
||||
if (!ConvertUTF8ToOrbis(current_text.begin(), src_text_length, src_text,
|
||||
ORBIS_IME_DIALOG_MAX_TEXT_LENGTH)) {
|
||||
LOG_ERROR(Lib_ImeDialog, "Failed to convert text to orbis encoding");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
int ret =
|
||||
linker->ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length);
|
||||
|
||||
if (ret != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ConvertOrbisToUTF8(out_text, out_text_length, current_text.begin(),
|
||||
ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4)) {
|
||||
LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode,
|
||||
u32* out_status) {
|
||||
if (!keyboard_filter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
int ret = linker->ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr);
|
||||
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
bool ImeDialogState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len,
|
||||
char* utf8_text, std::size_t utf8_text_len) {
|
||||
|
||||
std::fill(utf8_text, utf8_text + utf8_text_len, '\0');
|
||||
const ImWchar* orbis_text_ptr = reinterpret_cast<const ImWchar*>(orbis_text);
|
||||
ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImeDialogState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len,
|
||||
char16_t* orbis_text, std::size_t orbis_text_len) {
|
||||
|
||||
std::fill(orbis_text, orbis_text + orbis_text_len, u'\0');
|
||||
ImTextStrFromUtf8(reinterpret_cast<ImWchar*>(orbis_text), orbis_text_len, utf8_text, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ImeDialogUi::ImeDialogUi(ImeDialogState* state, OrbisImeDialogStatus* status,
|
||||
OrbisImeDialogResult* result)
|
||||
: state(state), status(status), result(result) {
|
||||
|
||||
if (state && *status == OrbisImeDialogStatus::RUNNING) {
|
||||
AddLayer(this);
|
||||
}
|
||||
}
|
||||
|
||||
ImeDialogUi::~ImeDialogUi() {
|
||||
std::scoped_lock lock(draw_mutex);
|
||||
|
||||
Free();
|
||||
}
|
||||
|
||||
ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept
|
||||
: state(other.state), status(other.status), result(other.result),
|
||||
first_render(other.first_render) {
|
||||
|
||||
std::scoped_lock lock(draw_mutex, other.draw_mutex);
|
||||
other.state = nullptr;
|
||||
other.status = nullptr;
|
||||
other.result = nullptr;
|
||||
|
||||
if (state && *status == OrbisImeDialogStatus::RUNNING) {
|
||||
AddLayer(this);
|
||||
}
|
||||
}
|
||||
|
||||
ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi&& other) {
|
||||
std::scoped_lock lock(draw_mutex, other.draw_mutex);
|
||||
Free();
|
||||
|
||||
state = other.state;
|
||||
status = other.status;
|
||||
result = other.result;
|
||||
first_render = other.first_render;
|
||||
other.state = nullptr;
|
||||
other.status = nullptr;
|
||||
other.result = nullptr;
|
||||
|
||||
if (state && *status == OrbisImeDialogStatus::RUNNING) {
|
||||
AddLayer(this);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ImeDialogUi::Free() {
|
||||
RemoveLayer(this);
|
||||
}
|
||||
|
||||
void ImeDialogUi::Draw() {
|
||||
std::unique_lock lock{draw_mutex};
|
||||
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!status || *status != OrbisImeDialogStatus::RUNNING) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& ctx = *GetCurrentContext();
|
||||
const auto& io = ctx.IO;
|
||||
|
||||
ImVec2 window_size;
|
||||
|
||||
if (state->is_multiLine) {
|
||||
window_size = {500.0f, 300.0f};
|
||||
} else {
|
||||
window_size = {500.0f, 150.0f};
|
||||
}
|
||||
|
||||
CentralizeNextWindow();
|
||||
SetNextWindowSize(window_size);
|
||||
SetNextWindowCollapsed(false);
|
||||
|
||||
if (first_render || !io.NavActive) {
|
||||
SetNextWindowFocus();
|
||||
}
|
||||
|
||||
if (Begin("IME Dialog##ImeDialog", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) {
|
||||
DrawPrettyBackground();
|
||||
|
||||
if (!state->title.empty()) {
|
||||
SetWindowFontScale(1.7f);
|
||||
TextUnformatted(state->title.data());
|
||||
SetWindowFontScale(1.0f);
|
||||
}
|
||||
|
||||
if (state->is_multiLine) {
|
||||
DrawMultiLineInputText();
|
||||
} else {
|
||||
DrawInputText();
|
||||
}
|
||||
|
||||
SetCursorPosY(GetCursorPosY() + 10.0f);
|
||||
|
||||
const char* button_text;
|
||||
|
||||
switch (state->enter_label) {
|
||||
case OrbisImeEnterLabel::GO:
|
||||
button_text = "Go##ImeDialogOK";
|
||||
break;
|
||||
case OrbisImeEnterLabel::SEARCH:
|
||||
button_text = "Search##ImeDialogOK";
|
||||
break;
|
||||
case OrbisImeEnterLabel::SEND:
|
||||
button_text = "Send##ImeDialogOK";
|
||||
break;
|
||||
case OrbisImeEnterLabel::DEFAULT:
|
||||
default:
|
||||
button_text = "OK##ImeDialogOK";
|
||||
break;
|
||||
}
|
||||
|
||||
float button_spacing = 10.0f;
|
||||
float total_button_width = BUTTON_SIZE.x * 2 + button_spacing;
|
||||
float button_start_pos = (window_size.x - total_button_width) / 2.0f;
|
||||
|
||||
SetCursorPosX(button_start_pos);
|
||||
|
||||
if (Button(button_text, BUTTON_SIZE) ||
|
||||
(!state->is_multiLine && IsKeyPressed(ImGuiKey_Enter))) {
|
||||
*status = OrbisImeDialogStatus::FINISHED;
|
||||
result->endstatus = OrbisImeDialogEndStatus::OK;
|
||||
}
|
||||
|
||||
SameLine(0.0f, button_spacing);
|
||||
|
||||
if (Button("Cancel##ImeDialogCancel", BUTTON_SIZE)) {
|
||||
*status = OrbisImeDialogStatus::FINISHED;
|
||||
result->endstatus = OrbisImeDialogEndStatus::USER_CANCELED;
|
||||
}
|
||||
}
|
||||
End();
|
||||
|
||||
first_render = false;
|
||||
}
|
||||
|
||||
void ImeDialogUi::DrawInputText() {
|
||||
ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f};
|
||||
SetCursorPosX(20.0f);
|
||||
if (first_render) {
|
||||
SetKeyboardFocusHere();
|
||||
}
|
||||
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
|
||||
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
|
||||
state->max_text_length, input_size, ImGuiInputTextFlags_CallbackCharFilter,
|
||||
InputTextCallback, this)) {
|
||||
state->input_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ImeDialogUi::DrawMultiLineInputText() {
|
||||
ImVec2 input_size = {GetWindowWidth() - 40.0f, 200.0f};
|
||||
SetCursorPosX(20.0f);
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackCharFilter |
|
||||
static_cast<ImGuiInputTextFlags>(ImGuiInputTextFlags_Multiline);
|
||||
if (first_render) {
|
||||
SetKeyboardFocusHere();
|
||||
}
|
||||
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
|
||||
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
|
||||
state->max_text_length, input_size, flags, InputTextCallback, this)) {
|
||||
state->input_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
|
||||
ImeDialogUi* ui = static_cast<ImeDialogUi*>(data->UserData);
|
||||
|
||||
ASSERT(ui);
|
||||
|
||||
// Should we filter punctuation?
|
||||
if (ui->state->is_numeric && (data->EventChar < '0' || data->EventChar > '9') &&
|
||||
data->EventChar != '\b' && data->EventChar != ',' && data->EventChar != '.') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ui->state->keyboard_filter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ImGui encodes ImWchar32 as multi-byte UTF-8 characters
|
||||
char* event_char = reinterpret_cast<char*>(&data->EventChar);
|
||||
|
||||
// Call the keyboard filter
|
||||
OrbisImeKeycode src_keycode = {
|
||||
.keycode = 0,
|
||||
.character = 0,
|
||||
.status = 1, // ??? 1 = key pressed, 0 = key released
|
||||
.type = OrbisImeKeyboardType::ENGLISH_US, // TODO set this to the correct value (maybe use
|
||||
// the current language?)
|
||||
.userId = ui->state->userId,
|
||||
.resourceId = 0,
|
||||
.timestamp = 0};
|
||||
|
||||
if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) {
|
||||
LOG_ERROR(Lib_ImeDialog, "Failed to convert orbis char to utf8");
|
||||
return 0;
|
||||
}
|
||||
src_keycode.keycode = src_keycode.character; // TODO set this to the correct value
|
||||
|
||||
u16 out_keycode;
|
||||
u32 out_status;
|
||||
|
||||
ui->state->CallKeyboardFilter(&src_keycode, &out_keycode, &out_status);
|
||||
|
||||
// TODO. set the keycode
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Libraries::ImeDialog
|
84
src/core/libraries/dialogs/ime_dialog_ui.h
Normal file
84
src/core/libraries/dialogs/ime_dialog_ui.h
Normal file
@ -0,0 +1,84 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <imgui.h>
|
||||
#include "common/cstring.h"
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/dialogs/ime_dialog.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
|
||||
namespace Libraries::ImeDialog {
|
||||
|
||||
class ImeDialogUi;
|
||||
|
||||
class ImeDialogState final {
|
||||
friend ImeDialogUi;
|
||||
|
||||
bool input_changed = false;
|
||||
|
||||
s32 userId{};
|
||||
bool is_multiLine{};
|
||||
bool is_numeric{};
|
||||
OrbisImeType type{};
|
||||
OrbisImeEnterLabel enter_label{};
|
||||
OrbisImeTextFilter text_filter{};
|
||||
OrbisImeExtKeyboardFilter keyboard_filter{};
|
||||
u32 max_text_length{};
|
||||
char16_t* text_buffer{};
|
||||
std::vector<char> title;
|
||||
std::vector<char> placeholder;
|
||||
|
||||
// A character can hold up to 4 bytes in UTF-8
|
||||
Common::CString<ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4> current_text;
|
||||
|
||||
public:
|
||||
ImeDialogState(const OrbisImeDialogParam* param = nullptr,
|
||||
const OrbisImeParamExtended* extended = nullptr);
|
||||
ImeDialogState(const ImeDialogState& other) = delete;
|
||||
ImeDialogState(ImeDialogState&& other) noexcept;
|
||||
ImeDialogState& operator=(ImeDialogState&& other);
|
||||
|
||||
bool CopyTextToOrbisBuffer();
|
||||
bool CallTextFilter();
|
||||
|
||||
private:
|
||||
bool CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status);
|
||||
|
||||
bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text,
|
||||
std::size_t native_text_len);
|
||||
bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len,
|
||||
char16_t* orbis_text, std::size_t orbis_text_len);
|
||||
};
|
||||
|
||||
class ImeDialogUi final : public ImGui::Layer {
|
||||
ImeDialogState* state{};
|
||||
OrbisImeDialogStatus* status{};
|
||||
OrbisImeDialogResult* result{};
|
||||
|
||||
bool first_render = true;
|
||||
std::mutex draw_mutex;
|
||||
|
||||
public:
|
||||
explicit ImeDialogUi(ImeDialogState* state = nullptr, OrbisImeDialogStatus* status = nullptr,
|
||||
OrbisImeDialogResult* result = nullptr);
|
||||
~ImeDialogUi() override;
|
||||
ImeDialogUi(const ImeDialogUi& other) = delete;
|
||||
ImeDialogUi(ImeDialogUi&& other) noexcept;
|
||||
ImeDialogUi& operator=(ImeDialogUi&& other);
|
||||
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
void Free();
|
||||
|
||||
void DrawInputText();
|
||||
void DrawMultiLineInputText();
|
||||
|
||||
static int InputTextCallback(ImGuiInputTextCallbackData* data);
|
||||
};
|
||||
|
||||
} // namespace Libraries::ImeDialog
|
@ -438,6 +438,7 @@ constexpr int ORBIS_USER_SERVICE_ERROR_BUFFER_TOO_SHORT = 0x8096000A;
|
||||
|
||||
// SystemService library
|
||||
constexpr int ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER = 0x80A10003;
|
||||
constexpr int ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT = 0x80A10004;
|
||||
|
||||
// NpTrophy library
|
||||
constexpr int ORBIS_NP_TROPHY_ERROR_UNKNOWN = 0x80551600;
|
||||
@ -499,3 +500,11 @@ constexpr int ORBIS_AVPLAYER_ERROR_INFO_OTHER_ENCRY = 0x806A00BF;
|
||||
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;
|
||||
|
||||
// Fiber library
|
||||
constexpr int ORBIS_FIBER_ERROR_NULL = 0x80590001;
|
||||
constexpr int ORBIS_FIBER_ERROR_ALIGNMENT = 0x80590002;
|
||||
constexpr int ORBIS_FIBER_ERROR_RANGE = 0x80590003;
|
||||
constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004;
|
||||
constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005;
|
||||
constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006;
|
284
src/core/libraries/fiber/fiber.cpp
Normal file
284
src/core/libraries/fiber/fiber.cpp
Normal file
@ -0,0 +1,284 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "fiber.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/linker.h"
|
||||
|
||||
#ifdef _WIN64
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Libraries::Fiber {
|
||||
|
||||
constexpr static u64 kFiberSignature = 0x054ad954;
|
||||
|
||||
thread_local SceFiber* gCurrentFiber = nullptr;
|
||||
thread_local void* gFiberThread = nullptr;
|
||||
|
||||
void FiberEntry(void* param) {
|
||||
SceFiber* fiber = static_cast<SceFiber*>(param);
|
||||
u64 argRun = 0;
|
||||
u64 argRet = 0;
|
||||
|
||||
gCurrentFiber = fiber;
|
||||
|
||||
if (fiber->pArgRun != nullptr) {
|
||||
argRun = *fiber->pArgRun;
|
||||
}
|
||||
|
||||
const auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
linker->ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun);
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry,
|
||||
u64 argOnInitialize, void* addrContext, u64 sizeContext,
|
||||
const SceFiberOptParam* optParam) {
|
||||
LOG_INFO(Lib_Fiber, "called: name = {}", name);
|
||||
|
||||
if (!fiber || !name || !entry) {
|
||||
return ORBIS_FIBER_ERROR_NULL;
|
||||
}
|
||||
|
||||
fiber->signature = kFiberSignature;
|
||||
|
||||
fiber->entry = entry;
|
||||
fiber->argOnInitialize = argOnInitialize;
|
||||
|
||||
fiber->argRun = 0;
|
||||
fiber->pArgRun = &fiber->argRun;
|
||||
fiber->argReturn = 0;
|
||||
fiber->pArgReturn = &fiber->argReturn;
|
||||
|
||||
fiber->sizeContext = sizeContext;
|
||||
|
||||
fiber->state = FiberState::Init;
|
||||
#ifdef _WIN64
|
||||
fiber->handle = CreateFiber(sizeContext, FiberEntry, fiber);
|
||||
#else
|
||||
UNREACHABLE_MSG("Missing implementation");
|
||||
#endif
|
||||
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam) {
|
||||
LOG_ERROR(Lib_Fiber, "called");
|
||||
|
||||
if (!optParam) {
|
||||
return ORBIS_FIBER_ERROR_NULL;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber) {
|
||||
LOG_TRACE(Lib_Fiber, "called");
|
||||
|
||||
if (!fiber) {
|
||||
return ORBIS_FIBER_ERROR_NULL;
|
||||
}
|
||||
if ((u64)fiber % 8 != 0) {
|
||||
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||
}
|
||||
if (fiber->signature != kFiberSignature) {
|
||||
return ORBIS_FIBER_ERROR_INVALID;
|
||||
}
|
||||
if (fiber->state != FiberState::Run) {
|
||||
return ORBIS_FIBER_ERROR_STATE;
|
||||
}
|
||||
|
||||
fiber->signature = 0;
|
||||
fiber->state = FiberState::None;
|
||||
|
||||
#ifdef _WIN64
|
||||
DeleteFiber(fiber->handle);
|
||||
#else
|
||||
UNREACHABLE_MSG("Missing implementation");
|
||||
#endif
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn) {
|
||||
LOG_TRACE(Lib_Fiber, "called");
|
||||
|
||||
if (!fiber) {
|
||||
return ORBIS_FIBER_ERROR_NULL;
|
||||
}
|
||||
if ((u64)fiber % 8 != 0) {
|
||||
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||
}
|
||||
if (fiber->signature != kFiberSignature) {
|
||||
return ORBIS_FIBER_ERROR_INVALID;
|
||||
}
|
||||
if (fiber->state == FiberState::Run) {
|
||||
return ORBIS_FIBER_ERROR_STATE;
|
||||
}
|
||||
|
||||
if (gFiberThread == nullptr) {
|
||||
#ifdef _WIN64
|
||||
gFiberThread = ConvertThreadToFiber(nullptr);
|
||||
#else
|
||||
UNREACHABLE_MSG("Missing implementation");
|
||||
#endif
|
||||
}
|
||||
|
||||
gCurrentFiber = fiber;
|
||||
|
||||
if (fiber->pArgRun != nullptr) {
|
||||
*fiber->pArgRun = argOnRunTo;
|
||||
}
|
||||
|
||||
fiber->pArgReturn = argOnReturn;
|
||||
fiber->state = FiberState::Run;
|
||||
#ifdef _WIN64
|
||||
SwitchToFiber(fiber->handle);
|
||||
#else
|
||||
UNREACHABLE_MSG("Missing implementation");
|
||||
#endif
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun) {
|
||||
LOG_TRACE(Lib_Fiber, "called");
|
||||
|
||||
if (!fiber) {
|
||||
return ORBIS_FIBER_ERROR_NULL;
|
||||
}
|
||||
if ((u64)fiber % 8 != 0) {
|
||||
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||
}
|
||||
if (fiber->signature != kFiberSignature) {
|
||||
return ORBIS_FIBER_ERROR_INVALID;
|
||||
}
|
||||
if (gCurrentFiber == nullptr) {
|
||||
return ORBIS_FIBER_ERROR_PERMISSION;
|
||||
}
|
||||
if (fiber->state == FiberState::Run) {
|
||||
return ORBIS_FIBER_ERROR_STATE;
|
||||
}
|
||||
|
||||
gCurrentFiber->state = FiberState::Suspend;
|
||||
|
||||
// TODO: argOnRun
|
||||
|
||||
*fiber->pArgRun = argOnRunTo;
|
||||
fiber->state = FiberState::Run;
|
||||
|
||||
gCurrentFiber = fiber;
|
||||
#ifdef _WIN64
|
||||
SwitchToFiber(fiber->handle);
|
||||
#else
|
||||
UNREACHABLE_MSG("Missing implementation");
|
||||
#endif
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber) {
|
||||
LOG_TRACE(Lib_Fiber, "called");
|
||||
|
||||
if (!fiber || !gCurrentFiber) {
|
||||
return ORBIS_FIBER_ERROR_NULL;
|
||||
}
|
||||
if (gCurrentFiber->signature != kFiberSignature) {
|
||||
return ORBIS_FIBER_ERROR_PERMISSION;
|
||||
}
|
||||
|
||||
*fiber = gCurrentFiber;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun) {
|
||||
LOG_TRACE(Lib_Fiber, "called");
|
||||
|
||||
if (gCurrentFiber->signature != kFiberSignature) {
|
||||
return ORBIS_FIBER_ERROR_PERMISSION;
|
||||
}
|
||||
|
||||
if (gCurrentFiber->pArgReturn != nullptr) {
|
||||
*gCurrentFiber->pArgReturn = argOnReturn;
|
||||
}
|
||||
|
||||
// TODO: argOnRun
|
||||
gCurrentFiber->state = FiberState::Suspend;
|
||||
gCurrentFiber = nullptr;
|
||||
#ifdef _WIN64
|
||||
SwitchToFiber(gFiberThread);
|
||||
#else
|
||||
UNREACHABLE_MSG("Missing implementation");
|
||||
#endif
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo) {
|
||||
LOG_INFO(Lib_Fiber, "called");
|
||||
|
||||
if (!fiber || !fiberInfo) {
|
||||
return ORBIS_FIBER_ERROR_NULL;
|
||||
}
|
||||
|
||||
fiberInfo->entry = fiber->entry;
|
||||
fiberInfo->argOnInitialize = fiber->argOnInitialize;
|
||||
fiberInfo->addrContext = nullptr;
|
||||
fiberInfo->sizeContext = fiber->sizeContext;
|
||||
fiberInfo->sizeContextMargin = 0;
|
||||
|
||||
strncpy(fiberInfo->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) {
|
||||
LOG_ERROR(Lib_Fiber, "called");
|
||||
|
||||
if (flags != 0) {
|
||||
return ORBIS_FIBER_ERROR_INVALID;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() {
|
||||
LOG_ERROR(Lib_Fiber, "called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name) {
|
||||
LOG_INFO(Lib_Fiber, "called, name = {}", name);
|
||||
|
||||
if (!fiber || !name) {
|
||||
return ORBIS_FIBER_ERROR_NULL;
|
||||
}
|
||||
if ((u64)fiber % 8 != 0) {
|
||||
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||
}
|
||||
|
||||
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
|
||||
LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize);
|
||||
LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize);
|
||||
|
||||
LIB_FUNCTION("a0LLrZWac0M", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRun);
|
||||
LIB_FUNCTION("PFT2S-tJ7Uk", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberSwitch);
|
||||
LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf);
|
||||
LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread);
|
||||
|
||||
LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo);
|
||||
LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1,
|
||||
sceFiberStartContextSizeCheck);
|
||||
LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1,
|
||||
sceFiberStopContextSizeCheck);
|
||||
LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Fiber
|
83
src/core/libraries/fiber/fiber.h
Normal file
83
src/core/libraries/fiber/fiber.h
Normal file
@ -0,0 +1,83 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
namespace Libraries::Fiber {
|
||||
|
||||
#define ORBIS_FIBER_MAX_NAME_LENGTH (31)
|
||||
|
||||
typedef void PS4_SYSV_ABI (*SceFiberEntry)(u64 argOnInitialize, u64 argOnRun);
|
||||
|
||||
enum FiberState : u32 {
|
||||
None = 0u,
|
||||
Init = 1u,
|
||||
Run = 2u,
|
||||
Suspend = 3u,
|
||||
};
|
||||
|
||||
struct SceFiber {
|
||||
u64 signature;
|
||||
FiberState state;
|
||||
SceFiberEntry entry;
|
||||
|
||||
u64 argOnInitialize;
|
||||
|
||||
u64 argRun;
|
||||
u64* pArgRun;
|
||||
|
||||
u64 argReturn;
|
||||
u64* pArgReturn;
|
||||
|
||||
u64 sizeContext;
|
||||
|
||||
char name[ORBIS_FIBER_MAX_NAME_LENGTH];
|
||||
void* handle;
|
||||
};
|
||||
static_assert(sizeof(SceFiber) <= 256);
|
||||
|
||||
struct SceFiberInfo {
|
||||
u64 size;
|
||||
SceFiberEntry entry;
|
||||
u64 argOnInitialize;
|
||||
void* addrContext;
|
||||
u64 sizeContext;
|
||||
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
|
||||
u64 sizeContextMargin;
|
||||
};
|
||||
static_assert(sizeof(SceFiberInfo) <= 128);
|
||||
|
||||
typedef void* SceFiberOptParam;
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry,
|
||||
u64 argOnInitialize, void* addrContext, u64 sizeContext,
|
||||
const SceFiberOptParam* optParam);
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam);
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber);
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn);
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun);
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber);
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun);
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo);
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags);
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void);
|
||||
|
||||
s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name);
|
||||
|
||||
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Fiber
|
354
src/core/libraries/game_live_streaming/gamelivestreaming.cpp
Normal file
354
src/core/libraries/game_live_streaming/gamelivestreaming.cpp
Normal file
@ -0,0 +1,354 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "gamelivestreaming.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::GameLiveStreaming {
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStartDebugBroadcast() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStopDebugBroadcast() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingApplySocialFeedbackMessageFilter() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingCheckCallback() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingClearPresetSocialFeedbackCommands() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingClearSocialFeedbackMessages() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingClearSpoilerTag() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingEnableLiveStreaming() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingEnableSocialFeedback() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentBroadcastScreenLayout() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus(OrbisGameLiveStreamingStatus* status) {
|
||||
memset(status, 0, sizeof(*status));
|
||||
status->isOnAir = false;
|
||||
LOG_DEBUG(Lib_GameLiveStreaming, "(STUBBED) called userid = {}", status->userId);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus2() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetProgramInfo() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessages() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessagesCount() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingInitialize() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewer() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewerA() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingPermitLiveStreaming() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingPermitServerSideRecording() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingPostSocialMessage() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingRegisterCallback() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenCloseSeparateMode() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenConfigureSeparateMode() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenInitialize() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenInitializeSeparateModeParameter() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenOpenSeparateMode() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenSetMode() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenTerminate() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetCameraFrameSetting() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetDefaultServiceProviderPermission() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetGuardAreas() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetInvitationSessionId() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetLinkCommentPreset() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetMaxBitrate() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetMetadata() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommands() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetServiceProviderPermission() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetSpoilerTag() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetStandbyScreenResource() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStartGenerateStandbyScreenResource() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStartSocialFeedbackMessageFiltering() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStopGenerateStandbyScreenResource() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStopSocialFeedbackMessageFiltering() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingTerminate() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingUnregisterCallback() {
|
||||
LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterlibSceGameLiveStreaming(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("caqgDl+V9qA", "libSceGameLiveStreaming_debug", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingStartDebugBroadcast);
|
||||
LIB_FUNCTION("0i8Lrllxwow", "libSceGameLiveStreaming_debug", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingStopDebugBroadcast);
|
||||
LIB_FUNCTION("NqkTzemliC0", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingApplySocialFeedbackMessageFilter);
|
||||
LIB_FUNCTION("PC4jq87+YQI", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingCheckCallback);
|
||||
LIB_FUNCTION("FcHBfHjFXkA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingClearPresetSocialFeedbackCommands);
|
||||
LIB_FUNCTION("lZ2Sd0uEvpo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingClearSocialFeedbackMessages);
|
||||
LIB_FUNCTION("6c2zGtThFww", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingClearSpoilerTag);
|
||||
LIB_FUNCTION("dWM80AX39o4", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingEnableLiveStreaming);
|
||||
LIB_FUNCTION("wBOQWjbWMfU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingEnableSocialFeedback);
|
||||
LIB_FUNCTION("aRSQNqbats4", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingGetCurrentBroadcastScreenLayout);
|
||||
LIB_FUNCTION("CoPMx369EqM", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingGetCurrentStatus);
|
||||
LIB_FUNCTION("lK8dLBNp9OE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingGetCurrentStatus2);
|
||||
LIB_FUNCTION("OIIm19xu+NM", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingGetProgramInfo);
|
||||
LIB_FUNCTION("PMx7N4WqNdo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingGetSocialFeedbackMessages);
|
||||
LIB_FUNCTION("yeQKjHETi40", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingGetSocialFeedbackMessagesCount);
|
||||
LIB_FUNCTION("kvYEw2lBndk", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingInitialize);
|
||||
LIB_FUNCTION("ysWfX5PPbfc", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingLaunchLiveViewer);
|
||||
LIB_FUNCTION("cvRCb7DTAig", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingLaunchLiveViewerA);
|
||||
LIB_FUNCTION("K0QxEbD7q+c", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingPermitLiveStreaming);
|
||||
LIB_FUNCTION("-EHnU68gExU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingPermitServerSideRecording);
|
||||
LIB_FUNCTION("hggKhPySVgI", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingPostSocialMessage);
|
||||
LIB_FUNCTION("nFP8qT9YXbo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingRegisterCallback);
|
||||
LIB_FUNCTION("b5RaMD2J0So", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingScreenCloseSeparateMode);
|
||||
LIB_FUNCTION("hBdd8n6kuvE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingScreenConfigureSeparateMode);
|
||||
LIB_FUNCTION("uhCmn81s-mU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingScreenInitialize);
|
||||
LIB_FUNCTION("fo5B8RUaBxQ", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingScreenInitializeSeparateModeParameter);
|
||||
LIB_FUNCTION("iorzW0pKOiA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingScreenOpenSeparateMode);
|
||||
LIB_FUNCTION("gDSvt78H3Oo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingScreenSetMode);
|
||||
LIB_FUNCTION("HE93dr-5rx4", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingScreenTerminate);
|
||||
LIB_FUNCTION("3PSiwAzFISE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetCameraFrameSetting);
|
||||
LIB_FUNCTION("TwuUzTKKeek", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetDefaultServiceProviderPermission);
|
||||
LIB_FUNCTION("Gw6S4oqlY7E", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetGuardAreas);
|
||||
LIB_FUNCTION("QmQYwQ7OTJI", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetInvitationSessionId);
|
||||
LIB_FUNCTION("Sb5bAXyUt5c", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetLinkCommentPreset);
|
||||
LIB_FUNCTION("q-kxuaF7URU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetMaxBitrate);
|
||||
LIB_FUNCTION("hUY-mSOyGL0", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetMetadata);
|
||||
LIB_FUNCTION("ycodiP2I0xo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetPresetSocialFeedbackCommands);
|
||||
LIB_FUNCTION("x6deXUpQbBo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription);
|
||||
LIB_FUNCTION("mCoz3k3zPmA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetServiceProviderPermission);
|
||||
LIB_FUNCTION("ZuX+zzz2DkA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetSpoilerTag);
|
||||
LIB_FUNCTION("MLvYI86FFAo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingSetStandbyScreenResource);
|
||||
LIB_FUNCTION("y0KkAydy9xE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingStartGenerateStandbyScreenResource);
|
||||
LIB_FUNCTION("Y1WxX7dPMCw", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingStartSocialFeedbackMessageFiltering);
|
||||
LIB_FUNCTION("D7dg5QJ4FlE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingStopGenerateStandbyScreenResource);
|
||||
LIB_FUNCTION("bYuGUBuIsaY", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingStopSocialFeedbackMessageFiltering);
|
||||
LIB_FUNCTION("9yK6Fk8mKOQ", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingTerminate);
|
||||
LIB_FUNCTION("5XHaH3kL+bA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingUnregisterCallback);
|
||||
LIB_FUNCTION("caqgDl+V9qA", "libSceGameLiveStreaming_direct_streaming", 1,
|
||||
"libSceGameLiveStreaming", 1, 1, sceGameLiveStreamingStartDebugBroadcast);
|
||||
LIB_FUNCTION("0i8Lrllxwow", "libSceGameLiveStreaming_direct_streaming", 1,
|
||||
"libSceGameLiveStreaming", 1, 1, sceGameLiveStreamingStopDebugBroadcast);
|
||||
LIB_FUNCTION("CoPMx369EqM", "libSceGameLiveStreamingCompat", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingGetCurrentStatus);
|
||||
LIB_FUNCTION("ysWfX5PPbfc", "libSceGameLiveStreamingCompat", 1, "libSceGameLiveStreaming", 1, 1,
|
||||
sceGameLiveStreamingLaunchLiveViewer);
|
||||
};
|
||||
|
||||
} // namespace Libraries::GameLiveStreaming
|
81
src/core/libraries/game_live_streaming/gamelivestreaming.h
Normal file
81
src/core/libraries/game_live_streaming/gamelivestreaming.h
Normal file
@ -0,0 +1,81 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::GameLiveStreaming {
|
||||
|
||||
struct OrbisGameLiveStreamingStatus {
|
||||
bool isOnAir;
|
||||
u8 align[3];
|
||||
u32 spectatorCounts;
|
||||
s32 userId;
|
||||
u8 reserved[60];
|
||||
};
|
||||
struct OrbisGameLiveStreamingStatus2 {
|
||||
s32 userId;
|
||||
bool isOnAir;
|
||||
u8 align[3];
|
||||
u32 spectatorCounts;
|
||||
u32 textMessageCounts;
|
||||
u32 commandMessageCounts;
|
||||
u32 broadcastVideoResolution;
|
||||
u8 reserved[48];
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStartDebugBroadcast();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStopDebugBroadcast();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingApplySocialFeedbackMessageFilter();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingCheckCallback();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingClearPresetSocialFeedbackCommands();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingClearSocialFeedbackMessages();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingClearSpoilerTag();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingEnableLiveStreaming();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingEnableSocialFeedback();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentBroadcastScreenLayout();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus(OrbisGameLiveStreamingStatus* status);
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus2();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetProgramInfo();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessages();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessagesCount();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingInitialize();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewer();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewerA();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingPermitLiveStreaming();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingPermitServerSideRecording();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingPostSocialMessage();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingRegisterCallback();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenCloseSeparateMode();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenConfigureSeparateMode();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenInitialize();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenInitializeSeparateModeParameter();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenOpenSeparateMode();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenSetMode();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingScreenTerminate();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetCameraFrameSetting();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetDefaultServiceProviderPermission();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetGuardAreas();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetInvitationSessionId();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetLinkCommentPreset();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetMaxBitrate();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetMetadata();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommands();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetServiceProviderPermission();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetSpoilerTag();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingSetStandbyScreenResource();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStartGenerateStandbyScreenResource();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStartSocialFeedbackMessageFiltering();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStopGenerateStandbyScreenResource();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingStopSocialFeedbackMessageFiltering();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingTerminate();
|
||||
int PS4_SYSV_ABI sceGameLiveStreamingUnregisterCallback();
|
||||
|
||||
void RegisterlibSceGameLiveStreaming(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::GameLiveStreaming
|
@ -519,10 +519,12 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) {
|
||||
// Dumping them using the current ring pointer would result in files containing only the
|
||||
// `IndirectBuffer` command. To access the actual command stream, we need to unwrap the IB.
|
||||
auto acb = acb_span;
|
||||
auto base_addr = reinterpret_cast<uintptr_t>(acb_ptr);
|
||||
const auto* indirect_buffer =
|
||||
reinterpret_cast<const PM4CmdIndirectBuffer*>(acb_span.data());
|
||||
if (indirect_buffer->header.opcode == PM4ItOpcode::IndirectBuffer) {
|
||||
acb = {indirect_buffer->Address<const u32>(), indirect_buffer->ib_size};
|
||||
base_addr = reinterpret_cast<uintptr_t>(indirect_buffer->Address<const u32>());
|
||||
acb = {reinterpret_cast<const u32*>(base_addr), indirect_buffer->ib_size};
|
||||
}
|
||||
|
||||
using namespace DebugStateType;
|
||||
@ -532,9 +534,9 @@ void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw) {
|
||||
.submit_num = seq_num,
|
||||
.num2 = gnm_vqid,
|
||||
.data = {acb.begin(), acb.end()},
|
||||
.base_addr = base_addr,
|
||||
});
|
||||
}
|
||||
|
||||
liverpool->SubmitAsc(vqid, acb_span);
|
||||
|
||||
*asc_queue.read_addr += acb_size;
|
||||
@ -1076,9 +1078,27 @@ s32 PS4_SYSV_ABI sceGnmInsertPopMarker(u32* cmdbuf, u32 size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmInsertPushColorMarker() {
|
||||
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
s32 PS4_SYSV_ABI sceGnmInsertPushColorMarker(u32* cmdbuf, u32 size, const char* marker, u32 color) {
|
||||
LOG_TRACE(Lib_GnmDriver, "called");
|
||||
|
||||
if (cmdbuf && marker) {
|
||||
const auto len = std::strlen(marker);
|
||||
const u32 packet_size = ((len + 0xc) >> 2) + ((len + 0x10) >> 3) * 2;
|
||||
if (packet_size + 2 == size) {
|
||||
auto* nop = reinterpret_cast<PM4CmdNop*>(cmdbuf);
|
||||
nop->header =
|
||||
PM4Type3Header{PM4ItOpcode::Nop, packet_size, PM4ShaderType::ShaderGraphics};
|
||||
nop->data_block[0] = PM4CmdNop::PayloadType::DebugColorMarkerPush;
|
||||
const auto marker_len = len + 1;
|
||||
std::memcpy(&nop->data_block[1], marker, marker_len);
|
||||
*reinterpret_cast<u32*>(reinterpret_cast<u8*>(&nop->data_block[1]) + marker_len + 8) =
|
||||
color;
|
||||
std::memset(reinterpret_cast<u8*>(&nop->data_block[1]) + marker_len + 8 + sizeof(u32),
|
||||
0, packet_size * 4 - marker_len - 8 - sizeof(u32));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceGnmInsertPushMarker(u32* cmdbuf, u32 size, const char* marker) {
|
||||
@ -1107,9 +1127,25 @@ int PS4_SYSV_ABI sceGnmInsertSetColorMarker() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmInsertSetMarker(u32 *param_1,int param_2,char *param_3) {
|
||||
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
s32 PS4_SYSV_ABI sceGnmInsertSetMarker(u32* cmdbuf, u32 size, const char* marker) {
|
||||
LOG_TRACE(Lib_GnmDriver, "called");
|
||||
|
||||
if (cmdbuf && marker) {
|
||||
const auto len = std::strlen(marker);
|
||||
const u32 packet_size = ((len + 8) >> 2) + ((len + 0xc) >> 3) * 2;
|
||||
if (packet_size + 2 == size) {
|
||||
auto* nop = reinterpret_cast<PM4CmdNop*>(cmdbuf);
|
||||
nop->header =
|
||||
PM4Type3Header{PM4ItOpcode::Nop, packet_size, PM4ShaderType::ShaderGraphics};
|
||||
nop->data_block[0] = PM4CmdNop::PayloadType::DebugSetMarker;
|
||||
const auto marker_len = len + 1;
|
||||
std::memcpy(&nop->data_block[1], marker, marker_len);
|
||||
std::memset(reinterpret_cast<u8*>(&nop->data_block[1]) + marker_len, 0,
|
||||
packet_size * 4 - marker_len);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmInsertThreadTraceMarker() {
|
||||
@ -2145,15 +2181,16 @@ s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[
|
||||
.submit_num = seq_num,
|
||||
.num2 = cbpair,
|
||||
.data = {dcb_span.begin(), dcb_span.end()},
|
||||
.base_addr = reinterpret_cast<uintptr_t>(dcb_gpu_addrs[cbpair]),
|
||||
});
|
||||
DebugState.PushQueueDump({
|
||||
.type = QueueType::ccb,
|
||||
.submit_num = seq_num,
|
||||
.num2 = cbpair,
|
||||
.data = {ccb_span.begin(), ccb_span.end()},
|
||||
.base_addr = reinterpret_cast<uintptr_t>(ccb),
|
||||
});
|
||||
}
|
||||
|
||||
liverpool->SubmitGfx(dcb_span, ccb_span);
|
||||
}
|
||||
|
||||
|
@ -105,10 +105,10 @@ int PS4_SYSV_ABI sceGnmGpuPaDebugEnter();
|
||||
int PS4_SYSV_ABI sceGnmGpuPaDebugLeave();
|
||||
int PS4_SYSV_ABI sceGnmInsertDingDongMarker();
|
||||
s32 PS4_SYSV_ABI sceGnmInsertPopMarker(u32* cmdbuf, u32 size);
|
||||
int PS4_SYSV_ABI sceGnmInsertPushColorMarker();
|
||||
s32 PS4_SYSV_ABI sceGnmInsertPushColorMarker(u32* cmdbuf, u32 size, const char* marker, u32 color);
|
||||
s32 PS4_SYSV_ABI sceGnmInsertPushMarker(u32* cmdbuf, u32 size, const char* marker);
|
||||
int PS4_SYSV_ABI sceGnmInsertSetColorMarker();
|
||||
int PS4_SYSV_ABI sceGnmInsertSetMarker(u32 *param_1,int param_2,char *param_3);
|
||||
s32 PS4_SYSV_ABI sceGnmInsertSetMarker(u32* cmdbuf, u32 size, const char* marker);
|
||||
int PS4_SYSV_ABI sceGnmInsertThreadTraceMarker();
|
||||
s32 PS4_SYSV_ABI sceGnmInsertWaitFlipDone(u32* cmdbuf, u32 size, s32 vo_handle, u32 buf_idx);
|
||||
int PS4_SYSV_ABI sceGnmIsCoredumpValid();
|
||||
|
340
src/core/libraries/ime/ime.cpp
Normal file
340
src/core/libraries/ime/ime.cpp
Normal file
@ -0,0 +1,340 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "ime.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Ime {
|
||||
|
||||
int PS4_SYSV_ABI FinalizeImeModule() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI InitializeImeModule() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeCheckFilterText() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeCheckRemoteEventParam() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeCheckUpdateTextInfo() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeClose() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeConfigGet() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeConfigSet() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeConfirmCandidate() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDicAddWord() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDicDeleteLearnDics() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDicDeleteUserDics() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDicDeleteWord() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDicGetWords() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDicReplaceWord() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeDisableController() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeFilterText() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeForTestFunction() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeGetPanelPositionAndForm() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeGetPanelSize() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeKeyboardClose() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeKeyboardGetInfo() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeKeyboardGetResourceId() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeKeyboardOpen() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeKeyboardOpenInternal() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeKeyboardSetMode() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeKeyboardUpdate() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeOpen() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeOpenInternal() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeParamInit() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeSetCandidateIndex() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeSetCaret() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeSetText() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeSetTextGeometry() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeUpdate() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshClearPreedit() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshClose() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshConfirmPreedit() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshDisableController() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshGetPanelPositionAndForm() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshInformConfirmdString() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshInformConfirmdString2() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshOpen() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshSendTextInfo() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshSetCaretGeometry() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshSetCaretIndexInPreedit() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshSetPanelPosition() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshSetParam() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshSetPreeditGeometry() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshSetSelectGeometry() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshSetSelectionText() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshUpdate() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshUpdateContext() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceImeVshUpdateContext2() {
|
||||
LOG_ERROR(Lib_Ime, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("mN+ZoSN-8hQ", "libSceIme", 1, "libSceIme", 1, 1, FinalizeImeModule);
|
||||
LIB_FUNCTION("uTW+63goeJs", "libSceIme", 1, "libSceIme", 1, 1, InitializeImeModule);
|
||||
LIB_FUNCTION("Lf3DeGWC6xg", "libSceIme", 1, "libSceIme", 1, 1, sceImeCheckFilterText);
|
||||
LIB_FUNCTION("zHuMUGb-AQI", "libSceIme", 1, "libSceIme", 1, 1, sceImeCheckRemoteEventParam);
|
||||
LIB_FUNCTION("OTb0Mg+1i1k", "libSceIme", 1, "libSceIme", 1, 1, sceImeCheckUpdateTextInfo);
|
||||
LIB_FUNCTION("TmVP8LzcFcY", "libSceIme", 1, "libSceIme", 1, 1, sceImeClose);
|
||||
LIB_FUNCTION("Ho5NVQzpKHo", "libSceIme", 1, "libSceIme", 1, 1, sceImeConfigGet);
|
||||
LIB_FUNCTION("P5dPeiLwm-M", "libSceIme", 1, "libSceIme", 1, 1, sceImeConfigSet);
|
||||
LIB_FUNCTION("tKLmVIUkpyM", "libSceIme", 1, "libSceIme", 1, 1, sceImeConfirmCandidate);
|
||||
LIB_FUNCTION("NYDsL9a0oEo", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicAddWord);
|
||||
LIB_FUNCTION("l01GKoyiQrY", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicDeleteLearnDics);
|
||||
LIB_FUNCTION("E2OcGgi-FPY", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicDeleteUserDics);
|
||||
LIB_FUNCTION("JAiMBkOTYKI", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicDeleteWord);
|
||||
LIB_FUNCTION("JoPdCUXOzMU", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicGetWords);
|
||||
LIB_FUNCTION("FuEl46uHDyo", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicReplaceWord);
|
||||
LIB_FUNCTION("E+f1n8e8DAw", "libSceIme", 1, "libSceIme", 1, 1, sceImeDisableController);
|
||||
LIB_FUNCTION("evjOsE18yuI", "libSceIme", 1, "libSceIme", 1, 1, sceImeFilterText);
|
||||
LIB_FUNCTION("wVkehxutK-U", "libSceIme", 1, "libSceIme", 1, 1, sceImeForTestFunction);
|
||||
LIB_FUNCTION("T6FYjZXG93o", "libSceIme", 1, "libSceIme", 1, 1, sceImeGetPanelPositionAndForm);
|
||||
LIB_FUNCTION("ziPDcIjO0Vk", "libSceIme", 1, "libSceIme", 1, 1, sceImeGetPanelSize);
|
||||
LIB_FUNCTION("PMVehSlfZ94", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardClose);
|
||||
LIB_FUNCTION("VkqLPArfFdc", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardGetInfo);
|
||||
LIB_FUNCTION("dKadqZFgKKQ", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardGetResourceId);
|
||||
LIB_FUNCTION("eaFXjfJv3xs", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardOpen);
|
||||
LIB_FUNCTION("oYkJlMK51SA", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardOpenInternal);
|
||||
LIB_FUNCTION("ua+13Hk9kKs", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardSetMode);
|
||||
LIB_FUNCTION("3Hx2Uw9xnv8", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardUpdate);
|
||||
LIB_FUNCTION("RPydv-Jr1bc", "libSceIme", 1, "libSceIme", 1, 1, sceImeOpen);
|
||||
LIB_FUNCTION("16UI54cWRQk", "libSceIme", 1, "libSceIme", 1, 1, sceImeOpenInternal);
|
||||
LIB_FUNCTION("WmYDzdC4EHI", "libSceIme", 1, "libSceIme", 1, 1, sceImeParamInit);
|
||||
LIB_FUNCTION("TQaogSaqkEk", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetCandidateIndex);
|
||||
LIB_FUNCTION("WLxUN2WMim8", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetCaret);
|
||||
LIB_FUNCTION("ieCNrVrzKd4", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetText);
|
||||
LIB_FUNCTION("TXYHFRuL8UY", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetTextGeometry);
|
||||
LIB_FUNCTION("-4GCfYdNF1s", "libSceIme", 1, "libSceIme", 1, 1, sceImeUpdate);
|
||||
LIB_FUNCTION("oOwl47ouxoM", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshClearPreedit);
|
||||
LIB_FUNCTION("gtoTsGM9vEY", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshClose);
|
||||
LIB_FUNCTION("wTKF4mUlSew", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshConfirmPreedit);
|
||||
LIB_FUNCTION("rM-1hkuOhh0", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshDisableController);
|
||||
LIB_FUNCTION("42xMaQ+GLeQ", "libSceIme", 1, "libSceIme", 1, 1,
|
||||
sceImeVshGetPanelPositionAndForm);
|
||||
LIB_FUNCTION("ZmmV6iukhyo", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshInformConfirmdString);
|
||||
LIB_FUNCTION("EQBusz6Uhp8", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshInformConfirmdString2);
|
||||
LIB_FUNCTION("LBicRa-hj3A", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshOpen);
|
||||
LIB_FUNCTION("-IAOwd2nO7g", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSendTextInfo);
|
||||
LIB_FUNCTION("qDagOjvJdNk", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetCaretGeometry);
|
||||
LIB_FUNCTION("tNOlmxee-Nk", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetCaretIndexInPreedit);
|
||||
LIB_FUNCTION("rASXozKkQ9g", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetPanelPosition);
|
||||
LIB_FUNCTION("idvMaIu5H+k", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetParam);
|
||||
LIB_FUNCTION("ga5GOgThbjo", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetPreeditGeometry);
|
||||
LIB_FUNCTION("RuSca8rS6yA", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetSelectGeometry);
|
||||
LIB_FUNCTION("J7COZrgSFRA", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetSelectionText);
|
||||
LIB_FUNCTION("WqAayyok5p0", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdate);
|
||||
LIB_FUNCTION("O7Fdd+Oc-qQ", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdateContext);
|
||||
LIB_FUNCTION("fwcPR7+7Rks", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdateContext2);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Ime
|
70
src/core/libraries/ime/ime.h
Normal file
70
src/core/libraries/ime/ime.h
Normal file
@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Ime {
|
||||
|
||||
int PS4_SYSV_ABI FinalizeImeModule();
|
||||
int PS4_SYSV_ABI InitializeImeModule();
|
||||
int PS4_SYSV_ABI sceImeCheckFilterText();
|
||||
int PS4_SYSV_ABI sceImeCheckRemoteEventParam();
|
||||
int PS4_SYSV_ABI sceImeCheckUpdateTextInfo();
|
||||
int PS4_SYSV_ABI sceImeClose();
|
||||
int PS4_SYSV_ABI sceImeConfigGet();
|
||||
int PS4_SYSV_ABI sceImeConfigSet();
|
||||
int PS4_SYSV_ABI sceImeConfirmCandidate();
|
||||
int PS4_SYSV_ABI sceImeDicAddWord();
|
||||
int PS4_SYSV_ABI sceImeDicDeleteLearnDics();
|
||||
int PS4_SYSV_ABI sceImeDicDeleteUserDics();
|
||||
int PS4_SYSV_ABI sceImeDicDeleteWord();
|
||||
int PS4_SYSV_ABI sceImeDicGetWords();
|
||||
int PS4_SYSV_ABI sceImeDicReplaceWord();
|
||||
int PS4_SYSV_ABI sceImeDisableController();
|
||||
int PS4_SYSV_ABI sceImeFilterText();
|
||||
int PS4_SYSV_ABI sceImeForTestFunction();
|
||||
int PS4_SYSV_ABI sceImeGetPanelPositionAndForm();
|
||||
int PS4_SYSV_ABI sceImeGetPanelSize();
|
||||
int PS4_SYSV_ABI sceImeKeyboardClose();
|
||||
int PS4_SYSV_ABI sceImeKeyboardGetInfo();
|
||||
int PS4_SYSV_ABI sceImeKeyboardGetResourceId();
|
||||
int PS4_SYSV_ABI sceImeKeyboardOpen();
|
||||
int PS4_SYSV_ABI sceImeKeyboardOpenInternal();
|
||||
int PS4_SYSV_ABI sceImeKeyboardSetMode();
|
||||
int PS4_SYSV_ABI sceImeKeyboardUpdate();
|
||||
int PS4_SYSV_ABI sceImeOpen();
|
||||
int PS4_SYSV_ABI sceImeOpenInternal();
|
||||
int PS4_SYSV_ABI sceImeParamInit();
|
||||
int PS4_SYSV_ABI sceImeSetCandidateIndex();
|
||||
int PS4_SYSV_ABI sceImeSetCaret();
|
||||
int PS4_SYSV_ABI sceImeSetText();
|
||||
int PS4_SYSV_ABI sceImeSetTextGeometry();
|
||||
int PS4_SYSV_ABI sceImeUpdate();
|
||||
int PS4_SYSV_ABI sceImeVshClearPreedit();
|
||||
int PS4_SYSV_ABI sceImeVshClose();
|
||||
int PS4_SYSV_ABI sceImeVshConfirmPreedit();
|
||||
int PS4_SYSV_ABI sceImeVshDisableController();
|
||||
int PS4_SYSV_ABI sceImeVshGetPanelPositionAndForm();
|
||||
int PS4_SYSV_ABI sceImeVshInformConfirmdString();
|
||||
int PS4_SYSV_ABI sceImeVshInformConfirmdString2();
|
||||
int PS4_SYSV_ABI sceImeVshOpen();
|
||||
int PS4_SYSV_ABI sceImeVshSendTextInfo();
|
||||
int PS4_SYSV_ABI sceImeVshSetCaretGeometry();
|
||||
int PS4_SYSV_ABI sceImeVshSetCaretIndexInPreedit();
|
||||
int PS4_SYSV_ABI sceImeVshSetPanelPosition();
|
||||
int PS4_SYSV_ABI sceImeVshSetParam();
|
||||
int PS4_SYSV_ABI sceImeVshSetPreeditGeometry();
|
||||
int PS4_SYSV_ABI sceImeVshSetSelectGeometry();
|
||||
int PS4_SYSV_ABI sceImeVshSetSelectionText();
|
||||
int PS4_SYSV_ABI sceImeVshUpdate();
|
||||
int PS4_SYSV_ABI sceImeVshUpdateContext();
|
||||
int PS4_SYSV_ABI sceImeVshUpdateContext2();
|
||||
|
||||
void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Ime
|
@ -321,26 +321,26 @@ int PS4_SYSV_ABI sceKernelRmdir(const char* path) {
|
||||
const std::filesystem::path dir_name = mnt->GetHostPath(path, &ro);
|
||||
|
||||
if (dir_name.empty()) {
|
||||
LOG_INFO(Kernel_Fs, "Failed to remove directory: {}, permission denied",
|
||||
fmt::UTF(dir_name.u8string()));
|
||||
LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, permission denied",
|
||||
fmt::UTF(dir_name.u8string()));
|
||||
return SCE_KERNEL_ERROR_EACCES;
|
||||
}
|
||||
|
||||
if (ro) {
|
||||
LOG_INFO(Kernel_Fs, "Failed to remove directory: {}, directory is read only",
|
||||
fmt::UTF(dir_name.u8string()));
|
||||
LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, directory is read only",
|
||||
fmt::UTF(dir_name.u8string()));
|
||||
return SCE_KERNEL_ERROR_EROFS;
|
||||
}
|
||||
|
||||
if (!std::filesystem::is_directory(dir_name)) {
|
||||
LOG_INFO(Kernel_Fs, "Failed to remove directory: {}, path is not a directory",
|
||||
fmt::UTF(dir_name.u8string()));
|
||||
LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, path is not a directory",
|
||||
fmt::UTF(dir_name.u8string()));
|
||||
return ORBIS_KERNEL_ERROR_ENOTDIR;
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(dir_name)) {
|
||||
LOG_INFO(Kernel_Fs, "Failed to remove directory: {}, no such file or directory",
|
||||
fmt::UTF(dir_name.u8string()));
|
||||
LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, no such file or directory",
|
||||
fmt::UTF(dir_name.u8string()));
|
||||
return ORBIS_KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
@ -348,7 +348,7 @@ int PS4_SYSV_ABI sceKernelRmdir(const char* path) {
|
||||
int result = std::filesystem::remove_all(dir_name, ec);
|
||||
|
||||
if (!ec) {
|
||||
LOG_DEBUG(Kernel_Fs, "Removed directory: {}", fmt::UTF(dir_name.u8string()));
|
||||
LOG_INFO(Kernel_Fs, "Removed directory: {}", fmt::UTF(dir_name.u8string()));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, error_code={}",
|
||||
|
@ -157,6 +157,7 @@ void SetPosixErrno(int e) {
|
||||
g_posix_errno = e;
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset,
|
||||
void** res) {
|
||||
LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}",
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "common/types.h"
|
||||
|
||||
constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE = 5056_MB; // ~ 5GB
|
||||
// TODO: Confirm this value on hardware.
|
||||
constexpr u64 SCE_KERNEL_MAIN_DMEM_SIZE_PRO = 5568_MB; // ~ 5.5GB
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
|
@ -36,6 +36,10 @@ void init_pthreads() {
|
||||
ScePthreadMutexattr default_mutexattr = nullptr;
|
||||
scePthreadMutexattrInit(&default_mutexattr);
|
||||
g_pthread_cxt->setDefaultMutexattr(default_mutexattr);
|
||||
ScePthreadMutexattr adaptive_mutexattr = nullptr;
|
||||
scePthreadMutexattrInit(&adaptive_mutexattr);
|
||||
scePthreadMutexattrSettype(&adaptive_mutexattr, ORBIS_PTHREAD_MUTEX_ADAPTIVE);
|
||||
g_pthread_cxt->setAdaptiveMutexattr(adaptive_mutexattr);
|
||||
// default cond init
|
||||
ScePthreadCondattr default_condattr = nullptr;
|
||||
scePthreadCondattrInit(&default_condattr);
|
||||
@ -412,7 +416,8 @@ int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u
|
||||
}
|
||||
|
||||
ScePthreadMutex* createMutex(ScePthreadMutex* addr) {
|
||||
if (addr == nullptr || *addr != nullptr) {
|
||||
if (addr == nullptr ||
|
||||
(*addr != nullptr && *addr != ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER)) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
@ -429,14 +434,14 @@ int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMut
|
||||
if (mutex == nullptr) {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (mutex_attr == nullptr) {
|
||||
attr = g_pthread_cxt->getDefaultMutexattr();
|
||||
} else {
|
||||
if (*mutex_attr == nullptr) {
|
||||
attr = g_pthread_cxt->getDefaultMutexattr();
|
||||
if (mutex_attr == nullptr || *mutex_attr == nullptr) {
|
||||
if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) {
|
||||
attr = g_pthread_cxt->getAdaptiveMutexattr();
|
||||
} else {
|
||||
attr = mutex_attr;
|
||||
attr = g_pthread_cxt->getDefaultMutexattr();
|
||||
}
|
||||
} else {
|
||||
attr = mutex_attr;
|
||||
}
|
||||
|
||||
*mutex = new PthreadMutexInternal{};
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
#define ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER (reinterpret_cast<ScePthreadMutex>(1))
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
@ -134,6 +136,12 @@ public:
|
||||
void setDefaultMutexattr(ScePthreadMutexattr attr) {
|
||||
m_default_mutexattr = attr;
|
||||
}
|
||||
ScePthreadMutexattr* getAdaptiveMutexattr() {
|
||||
return &m_adaptive_mutexattr;
|
||||
}
|
||||
void setAdaptiveMutexattr(ScePthreadMutexattr attr) {
|
||||
m_adaptive_mutexattr = attr;
|
||||
}
|
||||
ScePthreadCondattr* getDefaultCondattr() {
|
||||
return &m_default_condattr;
|
||||
}
|
||||
@ -161,6 +169,7 @@ public:
|
||||
|
||||
private:
|
||||
ScePthreadMutexattr m_default_mutexattr = nullptr;
|
||||
ScePthreadMutexattr m_adaptive_mutexattr = nullptr;
|
||||
ScePthreadCondattr m_default_condattr = nullptr;
|
||||
ScePthreadAttr m_default_attr = nullptr;
|
||||
PThreadPool* m_pthread_pool = nullptr;
|
||||
|
@ -59,7 +59,7 @@ public:
|
||||
for (auto it = wait_list.begin(); it != wait_list.end();) {
|
||||
auto* waiter = *it;
|
||||
if (waiter->need_count > token_count) {
|
||||
it++;
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
it = wait_list.erase(it);
|
||||
@ -148,7 +148,7 @@ public:
|
||||
// Find the first with priority less then us and insert right before it.
|
||||
auto it = wait_list.begin();
|
||||
while (it != wait_list.end() && (*it)->priority > waiter->priority) {
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
wait_list.insert(it, waiter);
|
||||
return it;
|
||||
|
@ -148,7 +148,7 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
|
||||
|
||||
#ifdef _WIN64
|
||||
FILETIME filetime;
|
||||
GetSystemTimeAsFileTime(&filetime);
|
||||
GetSystemTimePreciseAsFileTime(&filetime);
|
||||
|
||||
constexpr u64 UNIX_TIME_START = 0x295E9648864000;
|
||||
constexpr u64 TICKS_PER_SECOND = 1000000;
|
||||
|
@ -11,7 +11,9 @@
|
||||
#include "core/libraries/dialogs/error_dialog.h"
|
||||
#include "core/libraries/dialogs/ime_dialog.h"
|
||||
#include "core/libraries/disc_map/disc_map.h"
|
||||
#include "core/libraries/game_live_streaming/gamelivestreaming.h"
|
||||
#include "core/libraries/gnmdriver/gnmdriver.h"
|
||||
#include "core/libraries/ime/ime.h"
|
||||
#include "core/libraries/kernel/libkernel.h"
|
||||
#include "core/libraries/libc_internal/libc_internal.h"
|
||||
#include "core/libraries/libpng/pngdec.h"
|
||||
@ -26,10 +28,12 @@
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "core/libraries/playgo/playgo.h"
|
||||
#include "core/libraries/random/random.h"
|
||||
#include "core/libraries/remote_play/remoteplay.h"
|
||||
#include "core/libraries/rtc/rtc.h"
|
||||
#include "core/libraries/save_data/dialog/savedatadialog.h"
|
||||
#include "core/libraries/save_data/savedata.h"
|
||||
#include "core/libraries/screenshot/screenshot.h"
|
||||
#include "core/libraries/share_play/shareplay.h"
|
||||
#include "core/libraries/system/commondialog.h"
|
||||
#include "core/libraries/system/msgdialog.h"
|
||||
#include "core/libraries/system/posix.h"
|
||||
@ -77,6 +81,10 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
||||
Libraries::ImeDialog::RegisterlibSceImeDialog(sym);
|
||||
Libraries::AvPlayer::RegisterlibSceAvPlayer(sym);
|
||||
Libraries::Audio3d::RegisterlibSceAudio3d(sym);
|
||||
Libraries::Ime::RegisterlibSceIme(sym);
|
||||
Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym);
|
||||
Libraries::SharePlay::RegisterlibSceSharePlay(sym);
|
||||
Libraries::Remoteplay::RegisterlibSceRemoteplay(sym);
|
||||
}
|
||||
|
||||
} // namespace Libraries
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
static thread_local int32_t net_errno = 0;
|
||||
|
||||
int PS4_SYSV_ABI in6addr_any() {
|
||||
LOG_ERROR(Lib_Net, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
@ -563,9 +565,9 @@ int PS4_SYSV_ABI sceNetEpollWait() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetErrnoLoc() {
|
||||
int* PS4_SYSV_ABI sceNetErrnoLoc() {
|
||||
LOG_ERROR(Lib_Net, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
return &net_errno;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetEtherNtostr() {
|
||||
@ -732,9 +734,16 @@ int PS4_SYSV_ABI sceNetInetNtopWithScopeId() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetInetPton() {
|
||||
LOG_ERROR(Lib_Net, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
int PS4_SYSV_ABI sceNetInetPton(int af, const char* src, void* dst) {
|
||||
#ifdef WIN32
|
||||
int res = InetPtonA(af, src, dst);
|
||||
#else
|
||||
int res = inet_pton(af, src, dst);
|
||||
#endif
|
||||
if (res < 0) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetInetPtonEx() {
|
||||
|
@ -136,7 +136,7 @@ int PS4_SYSV_ABI sceNetEpollControl();
|
||||
int PS4_SYSV_ABI sceNetEpollCreate();
|
||||
int PS4_SYSV_ABI sceNetEpollDestroy();
|
||||
int PS4_SYSV_ABI sceNetEpollWait();
|
||||
int PS4_SYSV_ABI sceNetErrnoLoc();
|
||||
int* PS4_SYSV_ABI sceNetErrnoLoc();
|
||||
int PS4_SYSV_ABI sceNetEtherNtostr();
|
||||
int PS4_SYSV_ABI sceNetEtherStrton();
|
||||
int PS4_SYSV_ABI sceNetEventCallbackCreate();
|
||||
@ -169,7 +169,7 @@ u64 PS4_SYSV_ABI sceNetHtonll(u64 host64);
|
||||
u16 PS4_SYSV_ABI sceNetHtons(u16 host16);
|
||||
const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size);
|
||||
int PS4_SYSV_ABI sceNetInetNtopWithScopeId();
|
||||
int PS4_SYSV_ABI sceNetInetPton();
|
||||
int PS4_SYSV_ABI sceNetInetPton(int af, const char* src, void* dst);
|
||||
int PS4_SYSV_ABI sceNetInetPtonEx();
|
||||
int PS4_SYSV_ABI sceNetInetPtonWithScopeId();
|
||||
int PS4_SYSV_ABI sceNetInfoDumpStart();
|
||||
|
@ -26,3 +26,11 @@ constexpr int ORBIS_NET_CTL_STATE_IPOBTAINED = 3;
|
||||
constexpr int ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED = 1;
|
||||
constexpr int ORBIS_SCE_NET_CTL_EVENT_TYPE_DISCONNECT_REQ_FINISHED = 2;
|
||||
constexpr int ORBIS_NET_CTL_EVENT_TYPE_IPOBTAINED = 3;
|
||||
|
||||
// get info codes
|
||||
// device
|
||||
constexpr int ORBIS_NET_CTL_DEVICE_WIRED = 0;
|
||||
constexpr int ORBIS_NET_CTL_DEVICE_WIRELESS = 1;
|
||||
// link
|
||||
constexpr int ORBIS_NET_CTL_LINK_DISCONNECTED = 0;
|
||||
constexpr int ORBIS_NET_CTL_LINK_CONNECTED = 1;
|
||||
|
0
src/core/libraries/network/net_obj.cpp
Normal file
0
src/core/libraries/network/net_obj.cpp
Normal file
0
src/core/libraries/network/net_obj.h
Normal file
0
src/core/libraries/network/net_obj.h
Normal file
@ -1,6 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef WIN32
|
||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
#include <Ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
@ -149,15 +160,32 @@ int PS4_SYSV_ABI sceNetCtlGetIfStat() {
|
||||
int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) {
|
||||
switch (code) {
|
||||
case ORBIS_NET_CTL_INFO_DEVICE:
|
||||
info->device = 0;
|
||||
info->device = ORBIS_NET_CTL_DEVICE_WIRED;
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_LINK:
|
||||
info->link = 0; // disconnected
|
||||
info->link = ORBIS_NET_CTL_LINK_DISCONNECTED;
|
||||
break;
|
||||
case ORBIS_NET_CTL_INFO_IP_ADDRESS: {
|
||||
strcpy(info->ip_address,
|
||||
"127.0.0.1"); // placeholder in case gethostbyname can't find another ip
|
||||
char devname[80];
|
||||
gethostname(devname, 80);
|
||||
struct hostent* resolved = gethostbyname(devname);
|
||||
for (int i = 0; resolved->h_addr_list[i] != nullptr; ++i) {
|
||||
struct in_addr addrIn;
|
||||
memcpy(&addrIn, resolved->h_addr_list[i], sizeof(u32));
|
||||
char* addr = inet_ntoa(addrIn);
|
||||
if (strcmp(addr, "127.0.0.1") != 0) {
|
||||
strcpy(info->ip_address, addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Lib_NetCtl, "{} unsupported code", code);
|
||||
}
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
LOG_DEBUG(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -187,7 +215,10 @@ int PS4_SYSV_ABI sceNetCtlGetNetEvConfigInfoIpcInt() {
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlGetResult(int eventType, int* errorCode) {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called eventType = {} ", eventType);
|
||||
if (!errorCode) {
|
||||
return ORBIS_NET_CTL_ERROR_INVALID_ADDR;
|
||||
}
|
||||
LOG_DEBUG(Lib_NetCtl, "(STUBBED) called eventType = {} ", eventType);
|
||||
*errorCode = 0;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ typedef union OrbisNetCtlInfo {
|
||||
// GetInfo codes
|
||||
constexpr int ORBIS_NET_CTL_INFO_DEVICE = 1;
|
||||
constexpr int ORBIS_NET_CTL_INFO_LINK = 4;
|
||||
constexpr int ORBIS_NET_CTL_INFO_IP_ADDRESS = 14;
|
||||
|
||||
int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt();
|
||||
int PS4_SYSV_ABI sceNetBweClearEventIpcInt();
|
||||
|
@ -902,12 +902,13 @@ int PS4_SYSV_ABI sceNpCreateAsyncRequest() {
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpCreateRequest() {
|
||||
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
LOG_ERROR(Lib_NpManager, "(DUMMY) called");
|
||||
static int id = 0;
|
||||
return ++id;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpDeleteRequest() {
|
||||
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceNpDeleteRequest(int reqId) {
|
||||
LOG_ERROR(Lib_NpManager, "(DUMMY) called reqId = {}", reqId);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -985,8 +986,12 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpGetOnlineId() {
|
||||
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId) {
|
||||
LOG_DEBUG(Lib_NpManager, "userId {}", userId);
|
||||
std::string name = Config::getUserName();
|
||||
// Fill the unused stuffs to 0
|
||||
memset(onlineId, 0, sizeof(*onlineId));
|
||||
strcpy(onlineId->data, name.c_str());
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -218,7 +218,7 @@ int PS4_SYSV_ABI sceNpCheckNpReachability();
|
||||
int PS4_SYSV_ABI sceNpCheckPlus();
|
||||
int PS4_SYSV_ABI sceNpCreateAsyncRequest();
|
||||
int PS4_SYSV_ABI sceNpCreateRequest();
|
||||
int PS4_SYSV_ABI sceNpDeleteRequest();
|
||||
int PS4_SYSV_ABI sceNpDeleteRequest(int reqId);
|
||||
int PS4_SYSV_ABI sceNpGetAccountAge();
|
||||
int PS4_SYSV_ABI sceNpGetAccountCountry();
|
||||
int PS4_SYSV_ABI sceNpGetAccountCountryA();
|
||||
@ -233,7 +233,7 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatus();
|
||||
int PS4_SYSV_ABI sceNpGetGamePresenceStatusA();
|
||||
int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId userId, OrbisNpId* npId);
|
||||
int PS4_SYSV_ABI sceNpGetNpReachabilityState();
|
||||
int PS4_SYSV_ABI sceNpGetOnlineId();
|
||||
int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId);
|
||||
int PS4_SYSV_ABI sceNpGetParentalControlInfo();
|
||||
int PS4_SYSV_ABI sceNpGetParentalControlInfoA();
|
||||
int PS4_SYSV_ABI sceNpGetState(s32 userId, OrbisNpState* state);
|
||||
|
@ -520,7 +520,7 @@ s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context,
|
||||
|
||||
if (!result) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Failed to open trophy xml : {}", result.description());
|
||||
return ORBIS_OK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int num_trophies = 0;
|
||||
|
@ -88,7 +88,7 @@ int PS4_SYSV_ABI scePadGetCapability() {
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerInformation* pInfo) {
|
||||
LOG_INFO(Lib_Pad, "called handle = {}", handle);
|
||||
LOG_DEBUG(Lib_Pad, "called handle = {}", handle);
|
||||
if (handle < 0) {
|
||||
pInfo->touchPadInfo.pixelDensity = 1;
|
||||
pInfo->touchPadInfo.resolution.x = 1920;
|
||||
@ -368,12 +368,13 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
|
||||
pData->angularVelocity.x = 0.0f;
|
||||
pData->angularVelocity.y = 0.0f;
|
||||
pData->angularVelocity.z = 0.0f;
|
||||
pData->touchData.touchNum = 0;
|
||||
pData->touchData.touch[0].x = 0;
|
||||
pData->touchData.touch[0].y = 0;
|
||||
pData->touchData.touchNum =
|
||||
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
|
||||
pData->touchData.touch[0].x = state.touchpad[0].x;
|
||||
pData->touchData.touch[0].y = state.touchpad[0].y;
|
||||
pData->touchData.touch[0].id = 1;
|
||||
pData->touchData.touch[1].x = 0;
|
||||
pData->touchData.touch[1].y = 0;
|
||||
pData->touchData.touch[1].x = state.touchpad[1].x;
|
||||
pData->touchData.touch[1].y = state.touchpad[1].y;
|
||||
pData->touchData.touch[1].id = 2;
|
||||
pData->timestamp = state.time;
|
||||
pData->connected = true; // isConnected; //TODO fix me proper
|
||||
|
309
src/core/libraries/remote_play/remoteplay.cpp
Normal file
309
src/core/libraries/remote_play/remoteplay.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "remoteplay.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Remoteplay {
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayApprove() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayChangeEnterKey() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayClearAllRegistData() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayClearConnectHistory() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayConfirmDeviceRegist() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayDisconnect() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayGeneratePinCode() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayGetApMode() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectHistory() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(s32 userId, int* pStatus) {
|
||||
*pStatus = ORBIS_REMOTEPLAY_CONNECTION_STATUS_DISCONNECT;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectUserId() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayGetMbusDeviceInfo() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayGetOperationStatus() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayGetRemoteplayStatus() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayGetRpMode() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayImeClose() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayImeFilterResult() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayImeGetEvent() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayImeNotify() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayImeNotifyEventResult() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayImeOpen() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayImeSetCaret() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayImeSetText() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayInitialize() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayIsRemoteOskReady() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayIsRemotePlaying() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayNotifyMbusDeviceRegistComplete() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayNotifyNpPushWakeup() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayNotifyPinCodeError() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayNotifyUserDelete() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayPrintAllRegistData() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayProhibit() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayProhibitStreaming() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayServerLock() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayServerUnLock() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplaySetApMode() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplaySetLogLevel() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplaySetProhibition() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplaySetProhibitionForVsh() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplaySetRpMode() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayTerminate() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_1D5EE365ED5FADB3() {
|
||||
LOG_ERROR(Lib_Remoteplay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterlibSceRemoteplay(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("xQeIryTX7dY", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayApprove);
|
||||
LIB_FUNCTION("IYZ+Mu+8tPo", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayChangeEnterKey);
|
||||
LIB_FUNCTION("ZYUsJtcAnqA", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayClearAllRegistData);
|
||||
LIB_FUNCTION("cCheyCbF7qw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayClearConnectHistory);
|
||||
LIB_FUNCTION("tPYT-kGbZh8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayConfirmDeviceRegist);
|
||||
LIB_FUNCTION("6Lg4BNleJWc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayDisconnect);
|
||||
LIB_FUNCTION("j98LdSGy4eY", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayGeneratePinCode);
|
||||
LIB_FUNCTION("L+cL-M-DP3w", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayGetApMode);
|
||||
LIB_FUNCTION("g4K51cY+PEw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayGetConnectHistory);
|
||||
LIB_FUNCTION("g3PNjYKWqnQ", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayGetConnectionStatus);
|
||||
LIB_FUNCTION("3eBNV9A0BUM", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayGetConnectUserId);
|
||||
LIB_FUNCTION("ufesWMVX6iU", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayGetMbusDeviceInfo);
|
||||
LIB_FUNCTION("DxU4JGh4S2k", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayGetOperationStatus);
|
||||
LIB_FUNCTION("n5OxFJEvPlc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayGetRemoteplayStatus);
|
||||
LIB_FUNCTION("Cekhs6LSHC0", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayGetRpMode);
|
||||
LIB_FUNCTION("ig1ocbR7Ptw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayImeClose);
|
||||
LIB_FUNCTION("gV9-8cJPM3I", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayImeFilterResult);
|
||||
LIB_FUNCTION("cMk57DZXe6c", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayImeGetEvent);
|
||||
LIB_FUNCTION("-gwkQpOCl68", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayImeNotify);
|
||||
LIB_FUNCTION("58v9tSlRxc8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayImeNotifyEventResult);
|
||||
LIB_FUNCTION("C3r2zT5ebMg", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayImeOpen);
|
||||
LIB_FUNCTION("oB730zwoz0s", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayImeSetCaret);
|
||||
LIB_FUNCTION("rOTg1Nljp8w", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayImeSetText);
|
||||
LIB_FUNCTION("k1SwgkMSOM8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayInitialize);
|
||||
LIB_FUNCTION("R8RZC1ZIkzU", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayIsRemoteOskReady);
|
||||
LIB_FUNCTION("uYhiELUtLgA", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayIsRemotePlaying);
|
||||
LIB_FUNCTION("d-BBSEq1nfc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayNotifyMbusDeviceRegistComplete);
|
||||
LIB_FUNCTION("Yytq7NE38R8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayNotifyNpPushWakeup);
|
||||
LIB_FUNCTION("Wg-w8xjMZA4", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayNotifyPinCodeError);
|
||||
LIB_FUNCTION("yheulqylKwI", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayNotifyUserDelete);
|
||||
LIB_FUNCTION("t5ZvUiZ1hpE", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayPrintAllRegistData);
|
||||
LIB_FUNCTION("mrNh78tBpmg", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayProhibit);
|
||||
LIB_FUNCTION("7QLrixwVHcU", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayProhibitStreaming);
|
||||
LIB_FUNCTION("-ThIlThsN80", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayServerLock);
|
||||
LIB_FUNCTION("0Z-Pm5rZJOI", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayServerUnLock);
|
||||
LIB_FUNCTION("xSrhtSLIjOc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplaySetApMode);
|
||||
LIB_FUNCTION("5-2agAeaE+c", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplaySetLogLevel);
|
||||
LIB_FUNCTION("Rf0XMVR7xPw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplaySetProhibition);
|
||||
LIB_FUNCTION("n4l3FTZtNQM", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplaySetProhibitionForVsh);
|
||||
LIB_FUNCTION("-BPcEQ1w8xc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplaySetRpMode);
|
||||
LIB_FUNCTION("BOwybKVa3Do", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
sceRemoteplayTerminate);
|
||||
LIB_FUNCTION("HV7jZe1frbM", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0,
|
||||
Func_1D5EE365ED5FADB3);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Remoteplay
|
62
src/core/libraries/remote_play/remoteplay.h
Normal file
62
src/core/libraries/remote_play/remoteplay.h
Normal file
@ -0,0 +1,62 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
// returning codes in sceRemoteplayGetConnectionStatus pstatus
|
||||
constexpr int ORBIS_REMOTEPLAY_CONNECTION_STATUS_DISCONNECT = 0;
|
||||
constexpr int ORBIS_REMOTEPLAY_CONNECTION_STATUS_CONNECT = 1;
|
||||
|
||||
namespace Libraries::Remoteplay {
|
||||
|
||||
int PS4_SYSV_ABI sceRemoteplayApprove();
|
||||
int PS4_SYSV_ABI sceRemoteplayChangeEnterKey();
|
||||
int PS4_SYSV_ABI sceRemoteplayClearAllRegistData();
|
||||
int PS4_SYSV_ABI sceRemoteplayClearConnectHistory();
|
||||
int PS4_SYSV_ABI sceRemoteplayConfirmDeviceRegist();
|
||||
int PS4_SYSV_ABI sceRemoteplayDisconnect();
|
||||
int PS4_SYSV_ABI sceRemoteplayGeneratePinCode();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetApMode();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectHistory();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(s32 userId, int* pStatus);
|
||||
int PS4_SYSV_ABI sceRemoteplayGetConnectUserId();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetMbusDeviceInfo();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetOperationStatus();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetRemoteplayStatus();
|
||||
int PS4_SYSV_ABI sceRemoteplayGetRpMode();
|
||||
int PS4_SYSV_ABI sceRemoteplayImeClose();
|
||||
int PS4_SYSV_ABI sceRemoteplayImeFilterResult();
|
||||
int PS4_SYSV_ABI sceRemoteplayImeGetEvent();
|
||||
int PS4_SYSV_ABI sceRemoteplayImeNotify();
|
||||
int PS4_SYSV_ABI sceRemoteplayImeNotifyEventResult();
|
||||
int PS4_SYSV_ABI sceRemoteplayImeOpen();
|
||||
int PS4_SYSV_ABI sceRemoteplayImeSetCaret();
|
||||
int PS4_SYSV_ABI sceRemoteplayImeSetText();
|
||||
int PS4_SYSV_ABI sceRemoteplayInitialize();
|
||||
int PS4_SYSV_ABI sceRemoteplayIsRemoteOskReady();
|
||||
int PS4_SYSV_ABI sceRemoteplayIsRemotePlaying();
|
||||
int PS4_SYSV_ABI sceRemoteplayNotifyMbusDeviceRegistComplete();
|
||||
int PS4_SYSV_ABI sceRemoteplayNotifyNpPushWakeup();
|
||||
int PS4_SYSV_ABI sceRemoteplayNotifyPinCodeError();
|
||||
int PS4_SYSV_ABI sceRemoteplayNotifyUserDelete();
|
||||
int PS4_SYSV_ABI sceRemoteplayPrintAllRegistData();
|
||||
int PS4_SYSV_ABI sceRemoteplayProhibit();
|
||||
int PS4_SYSV_ABI sceRemoteplayProhibitStreaming();
|
||||
int PS4_SYSV_ABI sceRemoteplayServerLock();
|
||||
int PS4_SYSV_ABI sceRemoteplayServerUnLock();
|
||||
int PS4_SYSV_ABI sceRemoteplaySetApMode();
|
||||
int PS4_SYSV_ABI sceRemoteplaySetLogLevel();
|
||||
int PS4_SYSV_ABI sceRemoteplaySetProhibition();
|
||||
int PS4_SYSV_ABI sceRemoteplaySetProhibitionForVsh();
|
||||
int PS4_SYSV_ABI sceRemoteplaySetRpMode();
|
||||
int PS4_SYSV_ABI sceRemoteplayTerminate();
|
||||
int PS4_SYSV_ABI Func_1D5EE365ED5FADB3();
|
||||
|
||||
void RegisterlibSceRemoteplay(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Remoteplay
|
@ -98,7 +98,7 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) {
|
||||
param_sfo.Open(param_sfo_path);
|
||||
|
||||
auto last_write = param_sfo.GetLastWrite();
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) && !defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__)
|
||||
auto utc_time = std::chrono::file_clock::to_utc(last_write);
|
||||
#else
|
||||
auto utc_time = std::chrono::file_clock::to_sys(last_write);
|
||||
@ -402,7 +402,7 @@ void SaveDialogUi::Draw() {
|
||||
};
|
||||
}
|
||||
|
||||
CentralizeWindow();
|
||||
CentralizeNextWindow();
|
||||
SetNextWindowSize(window_size);
|
||||
SetNextWindowCollapsed(false);
|
||||
if (first_render || !io.NavActive) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user