mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-04 08:22:32 +00:00
Merge branch 'shadps4-emu:main' into disable-heap-malloc
This commit is contained in:
commit
0fa3ef165a
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
|
||||
|
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
@ -3,11 +3,7 @@
|
||||
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
on: [push, pull_request]
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.event_name }}-${{ github.ref }}
|
||||
@ -291,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
|
||||
@ -347,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
|
||||
@ -372,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
|
||||
@ -386,7 +382,7 @@ jobs:
|
||||
path: Shadps4-qt.AppImage
|
||||
|
||||
pre-release:
|
||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||
if: github.ref == 'refs/heads/main' && github.repository == 'shadps4-emu/shadPS4' && github.event_name == 'push'
|
||||
needs: [get-info, windows-sdl, windows-qt, macos-sdl, macos-qt, linux-sdl, linux-qt]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -94,4 +94,8 @@
|
||||
[submodule "externals/pugixml"]
|
||||
path = externals/pugixml
|
||||
url = https://github.com/zeux/pugixml.git
|
||||
shallow = true
|
||||
[submodule "externals/discord-rpc"]
|
||||
path = externals/discord-rpc
|
||||
url = https://github.com/shadps4-emu/ext-discord-rpc.git
|
||||
shallow = true
|
47
CMakeLists.txt
Normal file → Executable file
47
CMakeLists.txt
Normal file → Executable file
@ -282,6 +282,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
|
||||
@ -297,6 +305,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
|
||||
@ -324,6 +334,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
|
||||
@ -340,15 +354,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
|
||||
@ -374,6 +398,8 @@ set(COMMON src/common/logging/backend.cpp
|
||||
src/common/debug.h
|
||||
src/common/decoder.cpp
|
||||
src/common/decoder.h
|
||||
src/common/discord_rpc_handler.cpp
|
||||
src/common/discord_rpc_handler.h
|
||||
src/common/elf_info.h
|
||||
src/common/endian.h
|
||||
src/common/enum.h
|
||||
@ -462,6 +488,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
|
||||
@ -518,6 +545,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
|
||||
src/shader_recompiler/frontend/translate/data_share.cpp
|
||||
src/shader_recompiler/frontend/translate/export.cpp
|
||||
src/shader_recompiler/frontend/translate/scalar_alu.cpp
|
||||
src/shader_recompiler/frontend/translate/scalar_flow.cpp
|
||||
src/shader_recompiler/frontend/translate/scalar_memory.cpp
|
||||
src/shader_recompiler/frontend/translate/translate.cpp
|
||||
src/shader_recompiler/frontend/translate/translate.h
|
||||
@ -526,6 +554,8 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
|
||||
src/shader_recompiler/frontend/translate/vector_memory.cpp
|
||||
src/shader_recompiler/frontend/control_flow_graph.cpp
|
||||
src/shader_recompiler/frontend/control_flow_graph.h
|
||||
src/shader_recompiler/frontend/copy_shader.cpp
|
||||
src/shader_recompiler/frontend/copy_shader.h
|
||||
src/shader_recompiler/frontend/decode.cpp
|
||||
src/shader_recompiler/frontend/decode.h
|
||||
src/shader_recompiler/frontend/fetch_shader.cpp
|
||||
@ -542,6 +572,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
|
||||
src/shader_recompiler/ir/passes/ir_passes.h
|
||||
src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp
|
||||
src/shader_recompiler/ir/passes/resource_tracking_pass.cpp
|
||||
src/shader_recompiler/ir/passes/ring_access_elimination.cpp
|
||||
src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp
|
||||
src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp
|
||||
src/shader_recompiler/ir/abstract_syntax_list.h
|
||||
@ -574,6 +605,7 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
|
||||
src/video_core/amdgpu/pm4_cmds.h
|
||||
src/video_core/amdgpu/pm4_opcodes.h
|
||||
src/video_core/amdgpu/resource.h
|
||||
src/video_core/amdgpu/types.h
|
||||
src/video_core/amdgpu/default_context.cpp
|
||||
src/video_core/buffer_cache/buffer.cpp
|
||||
src/video_core/buffer_cache/buffer.h
|
||||
@ -686,6 +718,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
|
||||
@ -852,4 +886,15 @@ if (UNIX AND NOT APPLE)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Discord RPC
|
||||
target_link_libraries(shadps4 PRIVATE discord-rpc)
|
||||
|
||||
# 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
|
||||
|
@ -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
|
||||
|
19
externals/CMakeLists.txt
vendored
19
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
|
||||
@ -77,7 +77,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()
|
||||
|
||||
@ -184,5 +184,10 @@ if (NOT TARGET pugixml::pugixml)
|
||||
add_subdirectory(pugixml)
|
||||
endif()
|
||||
|
||||
# Discord RPC
|
||||
set(BUILD_EXAMPLES OFF)
|
||||
add_subdirectory(discord-rpc/)
|
||||
target_include_directories(discord-rpc INTERFACE discord-rpc/include)
|
||||
|
||||
# GCN Headers
|
||||
add_subdirectory(gcn)
|
||||
add_subdirectory(gcn)
|
||||
|
2
externals/date
vendored
2
externals/date
vendored
@ -1 +1 @@
|
||||
Subproject commit 51ce7e131079c061533d741be5fe7cca57f2faac
|
||||
Subproject commit dd8affc6de5755e07638bf0a14382d29549d6ee9
|
1
externals/discord-rpc
vendored
Submodule
1
externals/discord-rpc
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 4ec218155d73bcb8022f8f7ca72305d801f84beb
|
2
externals/ext-boost
vendored
2
externals/ext-boost
vendored
@ -1 +1 @@
|
||||
Subproject commit a04136add1e469f46d8ae8d3e8307779240a5c53
|
||||
Subproject commit f2474e1b584fb7a3ed6f85ba875e6eacd742ec8a
|
2
externals/glslang
vendored
2
externals/glslang
vendored
@ -1 +1 @@
|
||||
Subproject commit 46ef757e048e760b46601e6e77ae0cb72c97bd2f
|
||||
Subproject commit e61d7bb3006f451968714e2f653412081871e1ee
|
2
externals/sdl3
vendored
2
externals/sdl3
vendored
@ -1 +1 @@
|
||||
Subproject commit 0548050fc5a4edf1f47c3633c2cd06d8762b5532
|
||||
Subproject commit 54e622c2e6af456bfef382fae44c17682d5ac88a
|
2
externals/sirit
vendored
2
externals/sirit
vendored
@ -1 +1 @@
|
||||
Subproject commit 37090c74cc6e680f2bc334cac8fd182f7634a1f6
|
||||
Subproject commit 6cecb95d679c82c413d1f989e0b7ad9af130600d
|
2
externals/toml11
vendored
2
externals/toml11
vendored
@ -1 +1 @@
|
||||
Subproject commit d050c6b137199666cae75c2628a75d63b49b1c22
|
||||
Subproject commit f925e7f287c0008813c2294798cf9ca167fd9ffd
|
2
externals/vulkan-headers
vendored
2
externals/vulkan-headers
vendored
@ -1 +1 @@
|
||||
Subproject commit 29f979ee5aa58b7b005f805ea8df7a855c39ff37
|
||||
Subproject commit d91597a82f881d473887b560a03a7edf2720b72c
|
2
externals/xbyak
vendored
2
externals/xbyak
vendored
@ -1 +1 @@
|
||||
Subproject commit ccdf68421bc8eb85693f573080fc0a5faad862db
|
||||
Subproject commit d067f0d3f55696ae8bc9a25ad7012ee80f221d54
|
2
externals/xxhash
vendored
2
externals/xxhash
vendored
@ -1 +1 @@
|
||||
Subproject commit 3e321b4407318ac1348c0b80fb6fbae8c81ad5fa
|
||||
Subproject commit d4ad85e4afaad5c780f54db1dc967fff5a869ffd
|
2
externals/zydis
vendored
2
externals/zydis
vendored
@ -1 +1 @@
|
||||
Subproject commit bd73bc03b0aacaa89c9c203b9b43cd08f1b1843b
|
||||
Subproject commit 9d298eb8067ff62a237203d1e1470785033e185c
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,68 @@ 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);
|
||||
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) {
|
||||
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;
|
||||
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
|
||||
@ -42,10 +46,23 @@ 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 RegDump {
|
||||
static constexpr size_t MaxShaderStages = 5;
|
||||
Vulkan::Liverpool::Regs regs{};
|
||||
std::array<ShaderDump, MaxShaderStages> stages{};
|
||||
};
|
||||
|
||||
struct FrameDump {
|
||||
std::vector<QueueDump> queues;
|
||||
std::unordered_map<uintptr_t, RegDump> regs; // address -> reg dump
|
||||
};
|
||||
|
||||
class DebugStateImpl {
|
||||
@ -61,15 +78,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 +125,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 +140,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);
|
||||
};
|
||||
} // namespace DebugStateType
|
||||
|
||||
|
@ -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,16 @@ static std::vector<Widget::FrameDumpViewer> frame_viewers;
|
||||
|
||||
static float debug_popup_timing = 3.0f;
|
||||
|
||||
static bool just_opened_options = false;
|
||||
|
||||
void L::DrawMenuBar() {
|
||||
const auto& ctx = *GImGui;
|
||||
const auto& io = ctx.IO;
|
||||
|
||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||
|
||||
bool open_popup_options = false;
|
||||
|
||||
if (BeginMainMenuBar()) {
|
||||
if (BeginMenu("Options")) {
|
||||
if (MenuItemEx("Emulator Paused", nullptr, nullptr, isSystemPaused)) {
|
||||
@ -55,6 +59,7 @@ void L::DrawMenuBar() {
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
open_popup_options = MenuItem("Options");
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
EndMainMenuBar();
|
||||
@ -74,6 +79,11 @@ void L::DrawMenuBar() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (open_popup_options) {
|
||||
OpenPopup("GPU Tools Options");
|
||||
just_opened_options = true;
|
||||
}
|
||||
}
|
||||
|
||||
void L::DrawAdvanced() {
|
||||
@ -91,13 +101,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 +149,30 @@ 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];
|
||||
|
||||
if (just_opened_options) {
|
||||
just_opened_options = false;
|
||||
auto s = Options.disassembly_cli.copy(disassembly_cli, sizeof(disassembly_cli) - 1);
|
||||
disassembly_cli[s] = '\0';
|
||||
}
|
||||
|
||||
InputText("Shader disassembler: ", disassembly_cli, sizeof(disassembly_cli));
|
||||
if (IsItemHovered()) {
|
||||
SetTooltip(R"(Command to disassemble shaders. Example "dis.exe" --raw "{src}")");
|
||||
}
|
||||
|
||||
if (Button("Save")) {
|
||||
Options.disassembly_cli = disassembly_cli;
|
||||
SaveIniSettingsToDisk(io.IniFilename);
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void L::DrawSimple() {
|
||||
@ -140,26 +180,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 +237,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);
|
||||
}
|
||||
|
||||
|
24
src/core/devtools/options.cpp
Normal file
24
src/core/devtools/options.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
// 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];
|
||||
if (sscanf(line, "disassembly_cli=%511[^\n]", str) == 1) {
|
||||
Options.disassembly_cli = str;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeOptionsConfig(ImGuiTextBuffer* buf) {
|
||||
buf->appendf("disassembly_cli=%s\n", Options.disassembly_cli.c_str());
|
||||
}
|
||||
|
||||
} // namespace Core::Devtools
|
21
src/core/devtools/options.h
Normal file
21
src/core/devtools/options.h
Normal file
@ -0,0 +1,21 @@
|
||||
// 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;
|
||||
};
|
||||
|
||||
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,58 @@ 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() {
|
||||
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 (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 +1222,240 @@ 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);
|
||||
if (TreeNode("Batches")) {
|
||||
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);
|
||||
|
||||
char batch_hdr[128];
|
||||
if (batch.type == static_cast<AmdGpu::PM4ItOpcode>(0xFF)) {
|
||||
snprintf(batch_hdr, sizeof(batch_hdr), "State batch");
|
||||
} 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, batch_id);
|
||||
pop.open = true;
|
||||
} else {
|
||||
batch_view.SetData(data, batch_id);
|
||||
batch_view.open = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool show_batch_content = true;
|
||||
|
||||
if (group_batches) {
|
||||
show_batch_content =
|
||||
CollapsingHeader(batch_hdr, ImGuiTreeNodeFlags_AllowOverlap);
|
||||
SameLine(GetContentRegionAvail().x - 40.0f);
|
||||
if (Button("->", {40.0f, 0.0f})) {
|
||||
open_batch_view();
|
||||
}
|
||||
}
|
||||
|
||||
if (show_batch_content) {
|
||||
auto processed_size = 0ull;
|
||||
auto bb = ctx.LastItemData.Rect;
|
||||
if (group_batches) {
|
||||
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);
|
||||
if (Button("->", {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) {
|
||||
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();
|
||||
}
|
||||
highlight_batch = current_highlight_batch;
|
||||
|
||||
if (batch_id == batches.size() - 2) {
|
||||
Separator();
|
||||
}
|
||||
TreePop();
|
||||
}
|
||||
}
|
||||
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,43 +23,50 @@ 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;
|
||||
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);
|
||||
|
||||
explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector<u32>& cmd_list,
|
||||
uintptr_t base_addr = 0, std::string name = "");
|
||||
|
||||
void Draw();
|
||||
};
|
||||
|
100
src/core/devtools/widget/common.h
Normal file
100
src/core/devtools/widget/common.h
Normal file
@ -0,0 +1,100 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#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, typename V = u32>
|
||||
void DrawEnumRow(const char* text, T value) {
|
||||
DrawRow(text, "%X (%s)", V(value), magic_enum::enum_name(value).data());
|
||||
}
|
||||
|
||||
template <typename V, typename... Extra>
|
||||
void DrawMultipleRow(const char* text, const char* fmt, V arg, Extra&&... extra_args) {
|
||||
DrawRow(text, fmt, arg);
|
||||
if constexpr (sizeof...(extra_args) > 0) {
|
||||
DrawMultipleRow(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
|
@ -36,7 +36,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,20 +45,19 @@ 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;
|
||||
cmd_list_viewer.reserve(frame_dump->queues.size());
|
||||
for (const auto& cmd : frame_dump->queues) {
|
||||
const auto fname =
|
||||
fmt::format("{}_{}_{:02}_{:02}", id, magic_enum::enum_name(selected_queue_type),
|
||||
selected_submit_num, selected_queue_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) {
|
||||
@ -66,11 +66,11 @@ void FrameDumpViewer::Draw() {
|
||||
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "Frame #%d dump", id);
|
||||
static ImGuiID dock_id = ImHashStr("FrameDumpDock");
|
||||
SetNextWindowDockID(dock_id, ImGuiCond_Appearing);
|
||||
if (Begin(name, &is_open, ImGuiWindowFlags_NoSavedSettings)) {
|
||||
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();
|
||||
@ -89,12 +89,30 @@ void FrameDumpViewer::Draw() {
|
||||
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) {
|
||||
for (const auto& cmd : frame_dump->queues) {
|
||||
if (cmd.type == selected_queue_type) {
|
||||
available_submits[cmd.submit_num] = true;
|
||||
}
|
||||
@ -119,7 +137,7 @@ void FrameDumpViewer::Draw() {
|
||||
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) {
|
||||
for (const auto& cmd : frame_dump->queues) {
|
||||
if (cmd.type == selected_queue_type && cmd.submit_num == selected_submit_num) {
|
||||
available_queues[cmd.num2] = true;
|
||||
}
|
||||
@ -134,34 +152,16 @@ void FrameDumpViewer::Draw() {
|
||||
}
|
||||
}
|
||||
if (selected) {
|
||||
const auto it = std::ranges::find_if(frame_dump.queues, [&](const auto& cmd) {
|
||||
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);
|
||||
if (it != frame_dump->queues.end()) {
|
||||
selected_cmd = static_cast<s32>(std::distance(frame_dump->queues.begin(), it));
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@ -169,21 +169,6 @@ void FrameDumpViewer::Draw() {
|
||||
}
|
||||
}
|
||||
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,10 @@ 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;
|
||||
|
||||
DebugStateType::QueueType selected_queue_type;
|
||||
s32 selected_submit_num;
|
||||
@ -31,7 +29,7 @@ class FrameDumpViewer {
|
||||
public:
|
||||
bool is_open = true;
|
||||
|
||||
explicit FrameDumpViewer(DebugStateType::FrameDump frame_dump);
|
||||
explicit FrameDumpViewer(const DebugStateType::FrameDump& frame_dump);
|
||||
|
||||
~FrameDumpViewer();
|
||||
|
||||
|
182
src/core/devtools/widget/reg_popup.cpp
Normal file
182
src/core/devtools/widget/reg_popup.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
// 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"
|
||||
|
||||
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
|
||||
|
||||
DrawMultipleRow(
|
||||
"BASE_ADDR", "%X", buffer.base_address,
|
||||
"PITCH.TILE_MAX", "%X", buffer.pitch.tile_max,
|
||||
"PITCH.FMASK_TILE_MAX", "%X", buffer.pitch.fmask_tile_max,
|
||||
"SLICE.TILE_MAX", "%X", buffer.slice.tile_max,
|
||||
"VIEW.SLICE_START", "%X", buffer.view.slice_start,
|
||||
"VIEW.SLICE_MAX", "%X", 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();
|
||||
DrawMultipleRow(
|
||||
"CMASK_BASE_EXT", "%X", buffer.cmask_base_address,
|
||||
"FMASK_BASE_EXT", "%X", buffer.fmask_base_address,
|
||||
"FMASK_SLICE.TILE_MAX", "%X", buffer.fmask_slice.tile_max,
|
||||
"CLEAR_WORD0", "%X", buffer.clear_word0,
|
||||
"CLEAR_WORD1", "%X", buffer.clear_word1
|
||||
);
|
||||
|
||||
DrawMultipleRow(
|
||||
"Pitch()", "%X", buffer.Pitch(),
|
||||
"Height()", "%X", buffer.Height(),
|
||||
"Address()", "%X", buffer.Address(),
|
||||
"CmaskAddress", "%X", buffer.CmaskAddress(),
|
||||
"FmaskAddress", "%X", buffer.FmaskAddress(),
|
||||
"NumSamples()", "%X", buffer.NumSamples(),
|
||||
"NumSlices()", "%X", buffer.NumSlices(),
|
||||
"GetColorSliceSize()", "%X", buffer.GetColorSliceSize()
|
||||
);
|
||||
|
||||
auto tiling_mode = buffer.GetTilingMode();
|
||||
auto num_format = buffer.NumFormat();
|
||||
DrawEnumRow("GetTilingMode()", tiling_mode);
|
||||
DrawRow("IsTiled()", "%X", buffer.IsTiled());
|
||||
DrawEnumRow("NumFormat()", num_format);
|
||||
|
||||
// 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
|
||||
DrawEnumRow("Z_INFO.FORMAT", depth_buffer.z_info.format.Value());
|
||||
DrawMultipleRow(
|
||||
"Z_INFO.NUM_SAMPLES", "%X", depth_buffer.z_info.num_samples,
|
||||
"Z_INFO.TILE_SPLIT", "%X", depth_buffer.z_info.tile_split,
|
||||
"Z_INFO.TILE_MODE_INDEX", "%X", depth_buffer.z_info.tile_mode_index,
|
||||
"Z_INFO.DECOMPRESS_ON_N_ZPLANES", "%X", depth_buffer.z_info.decompress_on_n_zplanes,
|
||||
"Z_INFO.ALLOW_EXPCLEAR", "%X", depth_buffer.z_info.allow_expclear,
|
||||
"Z_INFO.READ_SIZE", "%X", depth_buffer.z_info.read_size,
|
||||
"Z_INFO.TILE_SURFACE_EN", "%X", depth_buffer.z_info.tile_surface_en,
|
||||
"Z_INFO.CLEAR_DISALLOWED", "%X", depth_buffer.z_info.clear_disallowed,
|
||||
"Z_INFO.ZRANGE_PRECISION", "%X", depth_buffer.z_info.zrange_precision
|
||||
);
|
||||
|
||||
DrawEnumRow("STENCIL_INFO.FORMAT", depth_buffer.stencil_info.format.Value());
|
||||
|
||||
DrawMultipleRow(
|
||||
"Z_READ_BASE", "%X", depth_buffer.z_read_base,
|
||||
"STENCIL_READ_BASE", "%X", depth_buffer.stencil_read_base,
|
||||
"Z_WRITE_BASE", "%X", depth_buffer.z_write_base,
|
||||
"STENCIL_WRITE_BASE", "%X", depth_buffer.stencil_write_base,
|
||||
"DEPTH_SIZE.PITCH_TILE_MAX", "%X", depth_buffer.depth_size.pitch_tile_max,
|
||||
"DEPTH_SIZE.HEIGHT_TILE_MAX", "%X", depth_buffer.depth_size.height_tile_max,
|
||||
"DEPTH_SLICE.TILE_MAX", "%X", depth_buffer.depth_slice.tile_max,
|
||||
"Pitch()", "%X", depth_buffer.Pitch(),
|
||||
"Height()", "%X", depth_buffer.Height(),
|
||||
"Address()", "%X", depth_buffer.Address(),
|
||||
"NumSamples()", "%X", depth_buffer.NumSamples(),
|
||||
"NumBits()", "%X", depth_buffer.NumBits(),
|
||||
"GetDepthSliceSize()", "%X", depth_buffer.GetDepthSliceSize()
|
||||
);
|
||||
// clang-format on
|
||||
EndTable();
|
||||
}
|
||||
SeparatorText("Depth control");
|
||||
if (BeginTable("DEPTH_CONTROL", 2, ImGuiTableFlags_Borders)) {
|
||||
TableNextRow();
|
||||
|
||||
// clang-format off
|
||||
DrawMultipleRow(
|
||||
"STENCIL_ENABLE", "%X", depth_control.stencil_enable,
|
||||
"DEPTH_ENABLE", "%X", depth_control.depth_enable,
|
||||
"DEPTH_WRITE_ENABLE", "%X", depth_control.depth_write_enable,
|
||||
"DEPTH_BOUNDS_ENABLE", "%X", depth_control.depth_bounds_enable
|
||||
);
|
||||
DrawEnumRow("DEPTH_FUNC", depth_control.depth_func.Value());
|
||||
DrawRow("BACKFACE_ENABLE", "%X", depth_control.backface_enable);
|
||||
DrawEnumRow("STENCIL_FUNC", depth_control.stencil_ref_func.Value());
|
||||
DrawEnumRow("STENCIL_FUNC_BF", depth_control.stencil_bf_func.Value());
|
||||
DrawMultipleRow(
|
||||
"ENABLE_COLOR_WRITES_ON_DEPTH_FAIL", "%X", depth_control.enable_color_writes_on_depth_fail,
|
||||
"DISABLE_COLOR_WRITES_ON_DEPTH_PASS", "%X", 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(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id) {
|
||||
this->data = color_buffer;
|
||||
this->title = fmt::format("Batch #{} CB #{}", batch_id, cb_id);
|
||||
}
|
||||
|
||||
void RegPopup::SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer,
|
||||
AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id) {
|
||||
this->data = std::make_tuple(depth_buffer, depth_control);
|
||||
this->title = fmt::format("Batch #{} Depth", batch_id);
|
||||
}
|
||||
|
||||
void RegPopup::Draw() {
|
||||
|
||||
char name[128];
|
||||
snprintf(name, sizeof(name), "%s###reg_popup_%d", title.c_str(), id);
|
||||
|
||||
SetNextWindowSize({250.0f, 300.0f}, ImGuiCond_FirstUseEver);
|
||||
if (Begin(name, &open, ImGuiWindowFlags_NoSavedSettings)) {
|
||||
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
|
38
src/core/devtools/widget/reg_popup.h
Normal file
38
src/core/devtools/widget/reg_popup.h
Normal file
@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
class RegPopup {
|
||||
int id;
|
||||
|
||||
using DepthBuffer = std::tuple<AmdGpu::Liverpool::DepthBuffer, AmdGpu::Liverpool::DepthControl>;
|
||||
|
||||
std::variant<AmdGpu::Liverpool::ColorBuffer, DepthBuffer> data;
|
||||
std::string title{};
|
||||
|
||||
void DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer);
|
||||
|
||||
void DrawDepthBuffer(const DepthBuffer& depth_data);
|
||||
|
||||
public:
|
||||
bool open = false;
|
||||
|
||||
RegPopup();
|
||||
|
||||
void SetData(AmdGpu::Liverpool::ColorBuffer color_buffer, u32 batch_id, u32 cb_id);
|
||||
|
||||
void SetData(AmdGpu::Liverpool::DepthBuffer depth_buffer,
|
||||
AmdGpu::Liverpool::DepthControl depth_control, u32 batch_id);
|
||||
|
||||
void Draw();
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
305
src/core/devtools/widget/reg_view.cpp
Normal file
305
src/core/devtools/widget/reg_view.cpp
Normal file
@ -0,0 +1,305 @@
|
||||
// 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_internal.h"
|
||||
#include "reg_view.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
#endif
|
||||
|
||||
using namespace ImGui;
|
||||
using magic_enum::enum_name;
|
||||
|
||||
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) {
|
||||
auto shader = data.stages[shader_id];
|
||||
|
||||
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 = 16;
|
||||
hex_view.OptShowAscii = 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 = shader.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::DrawRegs() {
|
||||
const auto& regs = data.regs;
|
||||
|
||||
if (BeginTable("REGS", 2, ImGuiTableFlags_Borders)) {
|
||||
|
||||
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);
|
||||
|
||||
auto cc_mode = regs.color_control.mode.Value();
|
||||
DrawRow("Color control", "%X (%s)", cc_mode, enum_name(cc_mode).data());
|
||||
|
||||
const auto open_new_popup = [&](int cb, auto... args) {
|
||||
if (GetIO().KeyShift) {
|
||||
auto& pop = extra_reg_popup.emplace_back();
|
||||
pop.SetData(args...);
|
||||
pop.open = 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(args...);
|
||||
if (!default_reg_popup.open) {
|
||||
default_reg_popup.open = true;
|
||||
auto popup_pos =
|
||||
GetCurrentContext()->LastItemData.Rect.Max + ImVec2(5.0f, 0.0f);
|
||||
SetNextWindowPos(popup_pos, ImGuiCond_Always);
|
||||
default_reg_popup.Draw();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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, batch_id, cb);
|
||||
}
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
TextUnformatted("Depth buffer");
|
||||
TableNextColumn();
|
||||
if (regs.depth_buffer.Address() == 0 || !regs.depth_control.depth_enable) {
|
||||
TextUnformatted("N/A");
|
||||
} else {
|
||||
constexpr auto depth_id = 0xF3;
|
||||
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, batch_id);
|
||||
}
|
||||
}
|
||||
|
||||
EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
RegView::RegView() {
|
||||
static int unique_id = 0;
|
||||
id = unique_id++;
|
||||
|
||||
char name[128];
|
||||
snprintf(name, sizeof(name), "BatchView###reg_dump_%d", id);
|
||||
SetNextWindowPos({400.0f, 200.0f});
|
||||
SetNextWindowSize({450.0f, 500.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.2f, &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, u32 batch_id) {
|
||||
this->data = std::move(data);
|
||||
this->batch_id = batch_id;
|
||||
// clear cache
|
||||
selected_shader = -1;
|
||||
shader_decomp.clear();
|
||||
default_reg_popup.open = false;
|
||||
extra_reg_popup.clear();
|
||||
}
|
||||
|
||||
void RegView::Draw() {
|
||||
|
||||
char name[128];
|
||||
snprintf(name, sizeof(name), "BatchView %u###reg_dump_%d", batch_id, id);
|
||||
if (Begin(name, &open, ImGuiWindowFlags_MenuBar)) {
|
||||
const char* names[] = {"vs", "ps", "gs", "es", "hs", "ls"};
|
||||
|
||||
if (BeginMenuBar()) {
|
||||
if (BeginMenu("Stage")) {
|
||||
for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; i++) {
|
||||
if (data.regs.stage_enable.IsStageEnabled(i)) {
|
||||
bool selected = selected_shader == i;
|
||||
if (Selectable(names[i], &selected)) {
|
||||
SelectShader(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (BeginMenu("Windows")) {
|
||||
Checkbox("Registers", &show_registers);
|
||||
Checkbox("User data", &show_user_data);
|
||||
Checkbox("Disassembly", &show_disassembly);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
EndMenuBar();
|
||||
}
|
||||
|
||||
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)) {
|
||||
auto shader = get_shader();
|
||||
if (!shader) {
|
||||
Text("Select a stage");
|
||||
} 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("Select a stage");
|
||||
} 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)) {
|
||||
DrawRegs();
|
||||
}
|
||||
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
|
50
src/core/devtools/widget/reg_view.h
Normal file
50
src/core/devtools/widget/reg_view.h
Normal file
@ -0,0 +1,50 @@
|
||||
// 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;
|
||||
|
||||
DebugStateType::RegDump data;
|
||||
u32 batch_id{~0u};
|
||||
|
||||
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 DrawRegs();
|
||||
|
||||
public:
|
||||
bool open = false;
|
||||
|
||||
RegView();
|
||||
|
||||
void SetData(DebugStateType::RegDump data, u32 batch_id);
|
||||
|
||||
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
|
@ -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"
|
||||
|
||||
@ -53,7 +54,14 @@ 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;
|
||||
std::filesystem::path host_path = mount->host_path / rel_path;
|
||||
|
||||
std::filesystem::path patch_path = mount->host_path;
|
||||
patch_path += "-UPDATE";
|
||||
if (corrected_path.starts_with("/app0/") && std::filesystem::exists(patch_path / rel_path)) {
|
||||
host_path = patch_path / rel_path;
|
||||
}
|
||||
|
||||
if (!NeedsCaseInsensitiveSearch) {
|
||||
return host_path;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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));
|
||||
}
|
||||
@ -367,8 +374,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,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;
|
||||
|
@ -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};
|
||||
}
|
||||
|
||||
CentralizeWindow();
|
||||
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
|
@ -498,4 +498,12 @@ constexpr int ORBIS_AVPLAYER_ERROR_INFO_OTHER_ENCRY = 0x806A00BF;
|
||||
// AppContent library
|
||||
constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002;
|
||||
constexpr int ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT = 0x80D90007;
|
||||
constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005;
|
||||
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() {
|
||||
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();
|
||||
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;
|
||||
|
@ -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,9 +986,9 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpGetOnlineId() {
|
||||
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId) {
|
||||
LOG_DEBUG(Lib_NpManager, "called returned sign out");
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpGetParentalControlInfo() {
|
||||
|
@ -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) {
|
||||
|
@ -12,9 +12,9 @@
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "save_instance.h"
|
||||
|
||||
constexpr u32 OrbisSaveDataBlocksMax = 32768; // 1 GiB
|
||||
constexpr auto OrbisSaveDataBlocksMin2 = 96; // 3MiB
|
||||
constexpr auto OrbisSaveDataBlocksMax = 32768; // 1 GiB
|
||||
constexpr std::string_view sce_sys = "sce_sys"; // system folder inside save
|
||||
constexpr std::string_view max_block_file_name = "max_block.txt";
|
||||
|
||||
static Core::FileSys::MntPoints* g_mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
|
||||
@ -58,18 +58,13 @@ std::filesystem::path SaveInstance::MakeDirSavePath(OrbisUserServiceUserId user_
|
||||
game_serial / dir_name;
|
||||
}
|
||||
|
||||
int SaveInstance::GetMaxBlocks(const std::filesystem::path& save_path) {
|
||||
Common::FS::IOFile max_blocks_file{save_path / sce_sys / max_block_file_name,
|
||||
Common::FS::FileAccessMode::Read};
|
||||
int max_blocks = 0;
|
||||
if (max_blocks_file.IsOpen()) {
|
||||
max_blocks = std::atoi(max_blocks_file.ReadString(16).c_str());
|
||||
uint64_t SaveInstance::GetMaxBlockFromSFO(const PSF& psf) {
|
||||
const auto vec = psf.GetBinary(std::string{SaveParams::SAVEDATA_BLOCKS});
|
||||
if (!vec.has_value()) {
|
||||
return OrbisSaveDataBlocksMax;
|
||||
}
|
||||
if (max_blocks <= 0) {
|
||||
max_blocks = OrbisSaveDataBlocksMax;
|
||||
}
|
||||
|
||||
return max_blocks;
|
||||
auto value = vec.value();
|
||||
return *(uint64_t*)value.data();
|
||||
}
|
||||
|
||||
std::filesystem::path SaveInstance::GetParamSFOPath(const std::filesystem::path& dir_path) {
|
||||
@ -92,13 +87,15 @@ void SaveInstance::SetupDefaultParamSFO(PSF& param_sfo, std::string dir_name,
|
||||
P(String, SaveParams::SAVEDATA_DIRECTORY, std::move(dir_name));
|
||||
P(Integer, SaveParams::SAVEDATA_LIST_PARAM, 0);
|
||||
P(String, SaveParams::TITLE_ID, std::move(game_serial));
|
||||
P(Binary, SaveParams::SAVEDATA_BLOCKS, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
#undef P
|
||||
}
|
||||
|
||||
SaveInstance::SaveInstance(int slot_num, OrbisUserServiceUserId user_id, std::string _game_serial,
|
||||
std::string_view _dir_name, int max_blocks)
|
||||
: slot_num(slot_num), user_id(user_id), game_serial(std::move(_game_serial)),
|
||||
dir_name(_dir_name), max_blocks(max_blocks) {
|
||||
dir_name(_dir_name),
|
||||
max_blocks(std::clamp(max_blocks, OrbisSaveDataBlocksMin2, OrbisSaveDataBlocksMax)) {
|
||||
ASSERT(slot_num >= 0 && slot_num < 16);
|
||||
|
||||
save_path = MakeDirSavePath(user_id, game_serial, dir_name);
|
||||
@ -187,7 +184,7 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor
|
||||
}
|
||||
}
|
||||
|
||||
max_blocks = GetMaxBlocks(save_path);
|
||||
max_blocks = static_cast<int>(GetMaxBlockFromSFO(param_sfo));
|
||||
|
||||
g_mnt->Mount(save_path, mount_point, read_only);
|
||||
mounted = true;
|
||||
@ -217,16 +214,13 @@ void SaveInstance::CreateFiles() {
|
||||
fs::create_directories(sce_sys_dir);
|
||||
|
||||
SetupDefaultParamSFO(param_sfo, dir_name, game_serial);
|
||||
param_sfo.AddBinary(std::string{SaveParams::SAVEDATA_BLOCKS}, max_blocks, true);
|
||||
|
||||
const bool ok = param_sfo.Encode(param_sfo_path);
|
||||
if (!ok) {
|
||||
throw std::filesystem::filesystem_error("Failed to write param.sfo", param_sfo_path,
|
||||
std::make_error_code(std::errc::permission_denied));
|
||||
}
|
||||
|
||||
Common::FS::IOFile max_block{sce_sys_dir / max_block_file_name,
|
||||
Common::FS::FileAccessMode::Write};
|
||||
max_block.WriteString(std::to_string(max_blocks == 0 ? OrbisSaveDataBlocksMax : max_blocks));
|
||||
}
|
||||
|
||||
} // namespace Libraries::SaveData
|
@ -62,7 +62,7 @@ public:
|
||||
std::string_view game_serial,
|
||||
std::string_view dir_name);
|
||||
|
||||
static int GetMaxBlocks(const std::filesystem::path& save_path);
|
||||
static uint64_t GetMaxBlockFromSFO(const PSF& psf);
|
||||
|
||||
// Get param.sfo path from a dir_path generated by MakeDirSavePath
|
||||
static std::filesystem::path GetParamSFOPath(const std::filesystem::path& dir_path);
|
||||
|
@ -445,7 +445,7 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
|
||||
fs::create_directories(root_save);
|
||||
const auto available = fs::space(root_save).available;
|
||||
|
||||
auto requested_size = mount_info->blocks * OrbisSaveDataBlockSize;
|
||||
auto requested_size = save_instance.GetMaxBlocks() * OrbisSaveDataBlockSize;
|
||||
if (requested_size > available) {
|
||||
mount_result->required_blocks = (requested_size - available) / OrbisSaveDataBlockSize;
|
||||
return Error::NO_SPACE_FS;
|
||||
@ -830,10 +830,11 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond
|
||||
LOG_ERROR(Lib_SaveData, "Failed to read SFO: {}", fmt::UTF(sfo_path.u8string()));
|
||||
ASSERT_MSG(false, "Failed to read SFO");
|
||||
}
|
||||
map_dir_sfo.emplace(dir_name, std::move(sfo));
|
||||
|
||||
size_t size = Common::FS::GetDirectorySize(dir_path);
|
||||
size_t total = SaveInstance::GetMaxBlocks(dir_path);
|
||||
size_t total = SaveInstance::GetMaxBlockFromSFO(sfo);
|
||||
|
||||
map_dir_sfo.emplace(dir_name, std::move(sfo));
|
||||
map_free_size.emplace(dir_name, total - size / OrbisSaveDataBlockSize);
|
||||
map_max_blocks.emplace(dir_name, total);
|
||||
}
|
||||
|
186
src/core/libraries/share_play/shareplay.cpp
Normal file
186
src/core/libraries/share_play/shareplay.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shareplay.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::SharePlay {
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayCrashDaemon() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfo(OrbisSharePlayConnectionInfo* pInfo) {
|
||||
memset(pInfo, 0, sizeof(*pInfo));
|
||||
pInfo->status = ORBIS_SHARE_PLAY_CONNECTION_STATUS_DORMANT;
|
||||
LOG_DEBUG(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfoA() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayGetCurrentInfo() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayGetEvent() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayInitialize() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayNotifyDialogOpen() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayNotifyForceCloseForCdlg() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayNotifyOpenQuickMenu() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayResumeScreenForCdlg() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayServerLock() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayServerUnLock() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlaySetMode() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlaySetProhibition() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlaySetProhibitionModeWithAppId() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayStartStandby() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayStartStreaming() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayStopStandby() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayStopStreaming() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayTerminate() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_2E93C0EA6A6B67C4() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_C1C236728D88E177() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_E9E80C474781F115() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_F3DD6199DA15ED44() {
|
||||
LOG_ERROR(Lib_SharePlay, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterlibSceSharePlay(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("ggnCfalLU-8", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayCrashDaemon);
|
||||
LIB_FUNCTION("OOrLKB0bSDs", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayGetCurrentConnectionInfo);
|
||||
LIB_FUNCTION("+MCXJlWdi+s", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayGetCurrentConnectionInfoA);
|
||||
LIB_FUNCTION("vUMkWXQff3w", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayGetCurrentInfo);
|
||||
LIB_FUNCTION("Md7Mdkr8LBc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayGetEvent);
|
||||
LIB_FUNCTION("isruqthpYcw", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayInitialize);
|
||||
LIB_FUNCTION("9zwJpai7jGc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayNotifyDialogOpen);
|
||||
LIB_FUNCTION("VUW2V9cUTP4", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayNotifyForceCloseForCdlg);
|
||||
LIB_FUNCTION("XL0WwUJoQPg", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayNotifyOpenQuickMenu);
|
||||
LIB_FUNCTION("6-1fKaa5HlY", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayResumeScreenForCdlg);
|
||||
LIB_FUNCTION("U28jAuLHj6c", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayServerLock);
|
||||
LIB_FUNCTION("3Oaux9ITEtY", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayServerUnLock);
|
||||
LIB_FUNCTION("QZy+KmyqKPU", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, sceSharePlaySetMode);
|
||||
LIB_FUNCTION("co2NCj--pnc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlaySetProhibition);
|
||||
LIB_FUNCTION("KADsbjNCgPo", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlaySetProhibitionModeWithAppId);
|
||||
LIB_FUNCTION("-F6NddfUsa4", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayStartStandby);
|
||||
LIB_FUNCTION("rWVNHNnEx6g", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayStartStreaming);
|
||||
LIB_FUNCTION("zEDkUWLVwFI", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayStopStandby);
|
||||
LIB_FUNCTION("aGlema+JxUU", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayStopStreaming);
|
||||
LIB_FUNCTION("UaLjloJinow", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayTerminate);
|
||||
LIB_FUNCTION("LpPA6mprZ8Q", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
Func_2E93C0EA6A6B67C4);
|
||||
LIB_FUNCTION("wcI2co2I4Xc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
Func_C1C236728D88E177);
|
||||
LIB_FUNCTION("6egMR0eB8RU", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
Func_E9E80C474781F115);
|
||||
LIB_FUNCTION("891hmdoV7UQ", "libSceSharePlay", 1, "libSceSharePlay", 0, 0,
|
||||
Func_F3DD6199DA15ED44);
|
||||
LIB_FUNCTION("OOrLKB0bSDs", "libSceSharePlayCompat", 1, "libSceSharePlay", 0, 0,
|
||||
sceSharePlayGetCurrentConnectionInfo);
|
||||
};
|
||||
|
||||
} // namespace Libraries::SharePlay
|
54
src/core/libraries/share_play/shareplay.h
Normal file
54
src/core/libraries/share_play/shareplay.h
Normal file
@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <core/libraries/np_manager/np_manager.h>
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::SharePlay {
|
||||
|
||||
constexpr int ORBIS_SHARE_PLAY_CONNECTION_STATUS_DORMANT = 0x00;
|
||||
constexpr int ORBIS_SHARE_PLAY_CONNECTION_STATUS_READY = 0x01;
|
||||
constexpr int ORBIS_SHARE_PLAY_CONNECTION_STATUS_CONNECTED = 0x02;
|
||||
|
||||
struct OrbisSharePlayConnectionInfo {
|
||||
int status;
|
||||
int mode;
|
||||
Libraries::NpManager::OrbisNpOnlineId hostOnlineId;
|
||||
Libraries::NpManager::OrbisNpOnlineId visitorOnlineId;
|
||||
s32 hostUserId;
|
||||
s32 visitorUserId;
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceSharePlayCrashDaemon();
|
||||
int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfo(OrbisSharePlayConnectionInfo* pInfo);
|
||||
int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfoA();
|
||||
int PS4_SYSV_ABI sceSharePlayGetCurrentInfo();
|
||||
int PS4_SYSV_ABI sceSharePlayGetEvent();
|
||||
int PS4_SYSV_ABI sceSharePlayInitialize();
|
||||
int PS4_SYSV_ABI sceSharePlayNotifyDialogOpen();
|
||||
int PS4_SYSV_ABI sceSharePlayNotifyForceCloseForCdlg();
|
||||
int PS4_SYSV_ABI sceSharePlayNotifyOpenQuickMenu();
|
||||
int PS4_SYSV_ABI sceSharePlayResumeScreenForCdlg();
|
||||
int PS4_SYSV_ABI sceSharePlayServerLock();
|
||||
int PS4_SYSV_ABI sceSharePlayServerUnLock();
|
||||
int PS4_SYSV_ABI sceSharePlaySetMode();
|
||||
int PS4_SYSV_ABI sceSharePlaySetProhibition();
|
||||
int PS4_SYSV_ABI sceSharePlaySetProhibitionModeWithAppId();
|
||||
int PS4_SYSV_ABI sceSharePlayStartStandby();
|
||||
int PS4_SYSV_ABI sceSharePlayStartStreaming();
|
||||
int PS4_SYSV_ABI sceSharePlayStopStandby();
|
||||
int PS4_SYSV_ABI sceSharePlayStopStreaming();
|
||||
int PS4_SYSV_ABI sceSharePlayTerminate();
|
||||
int PS4_SYSV_ABI Func_2E93C0EA6A6B67C4();
|
||||
int PS4_SYSV_ABI Func_C1C236728D88E177();
|
||||
int PS4_SYSV_ABI Func_E9E80C474781F115();
|
||||
int PS4_SYSV_ABI Func_F3DD6199DA15ED44();
|
||||
|
||||
void RegisterlibSceSharePlay(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::SharePlay
|
@ -256,7 +256,7 @@ void MsgDialogUi::Draw() {
|
||||
std::min(io.DisplaySize.y, 300.0f),
|
||||
};
|
||||
|
||||
CentralizeWindow();
|
||||
CentralizeNextWindow();
|
||||
SetNextWindowSize(window_size);
|
||||
SetNextWindowCollapsed(false);
|
||||
if (first_render || !io.NavActive) {
|
||||
|
@ -491,7 +491,7 @@ int PS4_SYSV_ABI sceUserServiceGetImeRunCount() {
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceUserServiceGetInitialUser(int* user_id) {
|
||||
LOG_INFO(Lib_UserService, "called");
|
||||
LOG_DEBUG(Lib_UserService, "called");
|
||||
if (user_id == nullptr) {
|
||||
LOG_ERROR(Lib_UserService, "user_id is null");
|
||||
return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT;
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/memory_management.h"
|
||||
@ -39,8 +40,10 @@ MemoryManager::MemoryManager() {
|
||||
MemoryManager::~MemoryManager() = default;
|
||||
|
||||
void MemoryManager::SetupMemoryRegions(u64 flexible_size) {
|
||||
const auto total_size =
|
||||
Config::isNeoMode() ? SCE_KERNEL_MAIN_DMEM_SIZE_PRO : SCE_KERNEL_MAIN_DMEM_SIZE;
|
||||
total_flexible_size = flexible_size;
|
||||
total_direct_size = SCE_KERNEL_MAIN_DMEM_SIZE - flexible_size;
|
||||
total_direct_size = total_size - flexible_size;
|
||||
|
||||
// Insert an area that covers direct memory physical block.
|
||||
// Note that this should never be called after direct memory allocations have been made.
|
||||
@ -242,6 +245,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot)
|
||||
new_vma.is_exec = false;
|
||||
new_vma.phys_base = 0;
|
||||
|
||||
rasterizer->MapMemory(mapped_addr, size);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ enum class MemoryProt : u32 {
|
||||
CpuReadWrite = 2,
|
||||
GpuRead = 16,
|
||||
GpuWrite = 32,
|
||||
GpuReadWrite = 38,
|
||||
GpuReadWrite = 48,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryProt)
|
||||
|
||||
|
113
src/emulator.cpp
113
src/emulator.cpp
@ -8,9 +8,11 @@
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#ifdef ENABLE_QT_GUI
|
||||
#include <QtCore>
|
||||
#include "common/memory_patcher.h"
|
||||
#endif
|
||||
#include "common/assert.h"
|
||||
#include "common/discord_rpc_handler.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/ntapi.h"
|
||||
#include "common/path_util.h"
|
||||
@ -24,6 +26,7 @@
|
||||
#include "core/file_format/trp.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/disc_map/disc_map.h"
|
||||
#include "core/libraries/fiber/fiber.h"
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
#include "core/libraries/libc_internal/libc_internal.h"
|
||||
#include "core/libraries/libs.h"
|
||||
@ -58,6 +61,7 @@ Emulator::Emulator() {
|
||||
LOG_INFO(Loader, "Branch {}", Common::g_scm_branch);
|
||||
LOG_INFO(Loader, "Description {}", Common::g_scm_desc);
|
||||
|
||||
LOG_INFO(Config, "General Logtype: {}", Config::getLogType());
|
||||
LOG_INFO(Config, "General isNeo: {}", Config::isNeoMode());
|
||||
LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu());
|
||||
LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders());
|
||||
@ -77,6 +81,17 @@ Emulator::Emulator() {
|
||||
|
||||
// Load renderdoc module.
|
||||
VideoCore::LoadRenderDoc();
|
||||
|
||||
// Start the timer (Play Time)
|
||||
#ifdef ENABLE_QT_GUI
|
||||
start_time = std::chrono::steady_clock::now();
|
||||
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
||||
QString filePath = QString::fromStdString((user_dir / "play_time.txt").string());
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
||||
LOG_INFO(Loader, "Error opening or creating play_time.txt");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Emulator::~Emulator() {
|
||||
@ -99,7 +114,10 @@ void Emulator::Run(const std::filesystem::path& file) {
|
||||
std::string app_version;
|
||||
u32 fw_version;
|
||||
|
||||
std::filesystem::path sce_sys_folder = file.parent_path() / "sce_sys";
|
||||
std::filesystem::path game_patch_folder = file.parent_path().concat("-UPDATE");
|
||||
bool use_game_patch = std::filesystem::exists(game_patch_folder / "sce_sys");
|
||||
std::filesystem::path sce_sys_folder =
|
||||
use_game_patch ? game_patch_folder / "sce_sys" : file.parent_path() / "sce_sys";
|
||||
if (std::filesystem::is_directory(sce_sys_folder)) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(sce_sys_folder)) {
|
||||
if (entry.path().filename() == "param.sfo") {
|
||||
@ -120,6 +138,14 @@ void Emulator::Run(const std::filesystem::path& file) {
|
||||
}
|
||||
#ifdef ENABLE_QT_GUI
|
||||
MemoryPatcher::g_game_serial = id;
|
||||
|
||||
// Timer for 'Play Time'
|
||||
QTimer* timer = new QTimer();
|
||||
QObject::connect(timer, &QTimer::timeout, [this, id]() {
|
||||
UpdatePlayTime(id);
|
||||
start_time = std::chrono::steady_clock::now();
|
||||
});
|
||||
timer->start(60000); // 60000 ms = 1 minute
|
||||
#endif
|
||||
title = param_sfo->GetString("TITLE").value_or("Unknown title");
|
||||
LOG_INFO(Loader, "Game id: {} Title: {}", id, title);
|
||||
@ -209,6 +235,15 @@ void Emulator::Run(const std::filesystem::path& file) {
|
||||
}
|
||||
}
|
||||
|
||||
// Discord RPC
|
||||
if (Config::getEnableDiscordRPC()) {
|
||||
auto* rpc = Common::Singleton<DiscordRPCHandler::RPC>::Instance();
|
||||
if (rpc->getRPCEnabled() == false) {
|
||||
rpc->init();
|
||||
}
|
||||
rpc->setStatusPlaying(game_info.title, id);
|
||||
}
|
||||
|
||||
// start execution
|
||||
std::jthread mainthread =
|
||||
std::jthread([this](std::stop_token stop_token) { linker->Execute(); });
|
||||
@ -217,13 +252,17 @@ void Emulator::Run(const std::filesystem::path& file) {
|
||||
window->waitEvent();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
UpdatePlayTime(id);
|
||||
#endif
|
||||
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
void Emulator::LoadSystemModules(const std::filesystem::path& file) {
|
||||
constexpr std::array<SysModules, 13> ModulesToLoad{
|
||||
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
|
||||
{"libSceFiber.sprx", nullptr},
|
||||
{"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber},
|
||||
{"libSceUlt.sprx", nullptr},
|
||||
{"libSceJson.sprx", nullptr},
|
||||
{"libSceJson2.sprx", nullptr},
|
||||
@ -258,4 +297,74 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
void Emulator::UpdatePlayTime(const std::string& serial) {
|
||||
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
||||
QString filePath = QString::fromStdString((user_dir / "play_time.txt").string());
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
||||
LOG_INFO(Loader, "Error opening play_time.txt");
|
||||
return;
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::seconds>(end_time - start_time);
|
||||
int totalSeconds = duration.count();
|
||||
|
||||
QTextStream in(&file);
|
||||
QStringList lines;
|
||||
QString content;
|
||||
while (!in.atEnd()) {
|
||||
content += in.readLine() + "\n";
|
||||
}
|
||||
file.close();
|
||||
|
||||
QStringList existingLines = content.split('\n', Qt::SkipEmptyParts);
|
||||
int accumulatedSeconds = 0;
|
||||
bool found = false;
|
||||
|
||||
for (const QString& line : existingLines) {
|
||||
QStringList parts = line.split(' ');
|
||||
if (parts.size() == 2 && parts[0] == QString::fromStdString(serial)) {
|
||||
QStringList timeParts = parts[1].split(':');
|
||||
if (timeParts.size() == 3) {
|
||||
int hours = timeParts[0].toInt();
|
||||
int minutes = timeParts[1].toInt();
|
||||
int seconds = timeParts[2].toInt();
|
||||
accumulatedSeconds = hours * 3600 + minutes * 60 + seconds;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
accumulatedSeconds += totalSeconds;
|
||||
int hours = accumulatedSeconds / 3600;
|
||||
int minutes = (accumulatedSeconds % 3600) / 60;
|
||||
int seconds = accumulatedSeconds % 60;
|
||||
QString playTimeSaved = QString::number(hours) + ":" +
|
||||
QString::number(minutes).rightJustified(2, '0') + ":" +
|
||||
QString::number(seconds).rightJustified(2, '0');
|
||||
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QTextStream out(&file);
|
||||
bool lineUpdated = false;
|
||||
|
||||
for (const QString& line : existingLines) {
|
||||
if (line.startsWith(QString::fromStdString(serial))) {
|
||||
out << QString::fromStdString(serial) + " " + playTimeSaved + "\n";
|
||||
lineUpdated = true;
|
||||
} else {
|
||||
out << line << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!lineUpdated) {
|
||||
out << QString::fromStdString(serial) + " " + playTimeSaved + "\n";
|
||||
}
|
||||
}
|
||||
LOG_INFO(Loader, "Playing time for {}: {}", serial, playTimeSaved.toStdString());
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Core
|
||||
|
@ -26,6 +26,7 @@ public:
|
||||
~Emulator();
|
||||
|
||||
void Run(const std::filesystem::path& file);
|
||||
void UpdatePlayTime(const std::string& serial);
|
||||
|
||||
private:
|
||||
void LoadSystemModules(const std::filesystem::path& file);
|
||||
@ -34,6 +35,7 @@ private:
|
||||
Input::GameController* controller;
|
||||
Core::Linker* linker;
|
||||
std::unique_ptr<Frontend::WindowSDL> window;
|
||||
std::chrono::steady_clock::time_point start_time;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 965 B |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user