Merge branch 'shadps4-emu:main' into disable-heap-malloc

This commit is contained in:
Stephen Miller 2024-10-15 14:28:06 -05:00 committed by GitHub
commit 0fa3ef165a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
213 changed files with 14230 additions and 1926 deletions

View File

@ -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

View File

@ -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
View File

@ -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
View 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()

View File

@ -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

View File

@ -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).
![image](https://github.com/user-attachments/assets/af520c77-797c-41a0-8f67-d87f5de3e3df)
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).

View File

@ -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

View File

@ -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

@ -1 +1 @@
Subproject commit 51ce7e131079c061533d741be5fe7cca57f2faac
Subproject commit dd8affc6de5755e07638bf0a14382d29549d6ee9

1
externals/discord-rpc vendored Submodule

@ -0,0 +1 @@
Subproject commit 4ec218155d73bcb8022f8f7ca72305d801f84beb

2
externals/ext-boost vendored

@ -1 +1 @@
Subproject commit a04136add1e469f46d8ae8d3e8307779240a5c53
Subproject commit f2474e1b584fb7a3ed6f85ba875e6eacd742ec8a

2
externals/glslang vendored

@ -1 +1 @@
Subproject commit 46ef757e048e760b46601e6e77ae0cb72c97bd2f
Subproject commit e61d7bb3006f451968714e2f653412081871e1ee

2
externals/sdl3 vendored

@ -1 +1 @@
Subproject commit 0548050fc5a4edf1f47c3633c2cd06d8762b5532
Subproject commit 54e622c2e6af456bfef382fae44c17682d5ac88a

2
externals/sirit vendored

@ -1 +1 @@
Subproject commit 37090c74cc6e680f2bc334cac8fd182f7634a1f6
Subproject commit 6cecb95d679c82c413d1f989e0b7ad9af130600d

2
externals/toml11 vendored

@ -1 +1 @@
Subproject commit d050c6b137199666cae75c2628a75d63b49b1c22
Subproject commit f925e7f287c0008813c2294798cf9ca167fd9ffd

@ -1 +1 @@
Subproject commit 29f979ee5aa58b7b005f805ea8df7a855c39ff37
Subproject commit d91597a82f881d473887b560a03a7edf2720b72c

2
externals/xbyak vendored

@ -1 +1 @@
Subproject commit ccdf68421bc8eb85693f573080fc0a5faad862db
Subproject commit d067f0d3f55696ae8bc9a25ad7012ee80f221d54

2
externals/xxhash vendored

@ -1 +1 @@
Subproject commit 3e321b4407318ac1348c0b80fb6fbae8c81ad5fa
Subproject commit d4ad85e4afaad5c780f54db1dc967fff5a869ffd

2
externals/zydis vendored

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

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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

View 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

View File

@ -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

View File

@ -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
}

View File

@ -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) \

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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()},
};
}
}
}
}

View File

@ -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

View File

@ -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);
}

View 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

View 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

View File

@ -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

View File

@ -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();
};

View 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

View File

@ -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

View File

@ -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();

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View File

@ -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();

View File

@ -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);

View File

@ -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;
}

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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;

View File

@ -75,7 +75,7 @@ public:
std::min(io.DisplaySize.y, 300.0f),
};
CentralizeWindow();
CentralizeNextWindow();
SetNextWindowSize(window_size);
SetNextWindowCollapsed(false);
if (first_render || !io.NavActive) {

View File

@ -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) {

View File

@ -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

View 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

View 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

View File

@ -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;

View 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

View 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

View 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

View 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

View File

@ -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);
}

View File

@ -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();

View 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

View 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

View File

@ -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={}",

View File

@ -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 = {}",

View File

@ -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 {

View File

@ -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{};

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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() {

View File

@ -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();

View File

@ -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;

View File

View File

View 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;
}

View File

@ -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();

View File

@ -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() {

View File

@ -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);

View File

@ -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;

View File

@ -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

View 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

View 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

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View 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

View 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

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}

View File

@ -28,7 +28,7 @@ enum class MemoryProt : u32 {
CpuReadWrite = 2,
GpuRead = 16,
GpuWrite = 32,
GpuReadWrite = 38,
GpuReadWrite = 48,
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryProt)

View File

@ -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

View File

@ -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