Merge branch 'main' into add_keyboard_keymapping

This commit is contained in:
georgemoralis 2024-09-11 08:20:48 +03:00 committed by GitHub
commit 977eb9bb3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
181 changed files with 10773 additions and 2142 deletions

View file

@ -31,8 +31,27 @@ jobs:
arch: linux_gcc_64
version: 6.7.1
- name: Cache CMake dependency source code
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-dependency-sources
with:
path: |
${{github.workspace}}/build
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
restore-keys: |
${{ env.cache-name }}-
- name: Cache CMake dependency build objects
uses: hendrikmuhs/ccache-action@v1.2.14
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-dependency-builds
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_QT_GUI=ON
run: cmake -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
@ -50,4 +69,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: shadps4-linux-qt-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.shorthash }}
path: Shadps4-qt.AppImage
path: Shadps4-qt.AppImage

View file

@ -25,8 +25,27 @@ jobs:
run: >
sudo apt-get update && sudo apt install libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential
- name: Cache CMake dependency source code
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-dependency-sources
with:
path: |
${{github.workspace}}/build
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
restore-keys: |
${{ env.cache-name }}-
- name: Cache CMake dependency build objects
uses: hendrikmuhs/ccache-action@v1.2.14
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-dependency-builds
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel

View file

@ -40,8 +40,29 @@ jobs:
arch: clang_64
archives: qtbase qttools
- name: Cache CMake dependency source code
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-dependency-sources
with:
path: |
${{github.workspace}}/build
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
restore-keys: |
${{ env.cache-name }}-
- name: Cache CMake dependency build objects
uses: hendrikmuhs/ccache-action@v1.2.14
env:
cache-name: ${{runner.os}}-qt-cache-cmake-dependency-builds
with:
append-timestamp: false
create-symlink: true
key: ${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
variant: sccache
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)

View file

@ -31,8 +31,29 @@ jobs:
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
arch -x86_64 /usr/local/bin/brew install molten-vk
- name: Cache CMake dependency source code
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-dependency-sources
with:
path: |
${{github.workspace}}/build
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
restore-keys: |
${{ env.cache-name }}-
- name: Cache CMake dependency build objects
uses: hendrikmuhs/ccache-action@v1.2.14
env:
cache-name: ${{runner.os}}-sdl-cache-cmake-dependency-builds
with:
append-timestamp: false
create-symlink: true
key: ${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
variant: sccache
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)

View file

@ -30,6 +30,17 @@ jobs:
arch: win64_msvc2019_64
archives: qtbase qttools
- name: Cache CMake dependency source code
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-dependency-sources
with:
path: |
${{github.workspace}}/build
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
restore-keys: |
${{ env.cache-name }}-
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -T ClangCL -DENABLE_QT_GUI=ON

View file

@ -20,6 +20,17 @@ jobs:
with:
submodules: recursive
- name: Cache CMake dependency source code
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-dependency-sources
with:
path: |
${{github.workspace}}/build
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
restore-keys: |
${{ env.cache-name }}-
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -T ClangCL

8
.gitmodules vendored
View file

@ -85,3 +85,11 @@
[submodule "externals/half"]
path = externals/half
url = https://github.com/ROCm/half.git
[submodule "externals/dear_imgui"]
path = externals/dear_imgui
url = https://github.com/shadps4-emu/ext-imgui.git
shallow = true
branch = docking
[submodule "externals/pugixml"]
path = externals/pugixml
url = https://github.com/zeux/pugixml.git

View file

@ -31,6 +31,22 @@ endif()
option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF)
# First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR.
if (APPLE AND CMAKE_OSX_ARCHITECTURES)
set(BASE_ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
else()
set(BASE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}")
endif()
# Next, match common architecture strings down to a known common value.
if (BASE_ARCHITECTURE MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
set(ARCHITECTURE "x86_64")
elseif (BASE_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
set(ARCHITECTURE "arm64")
else()
message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}")
endif()
# This function should be passed a list of all files in a target. It will automatically generate file groups
# following the directory hierarchy, so that the layout of the files in IDEs matches the one in the filesystem.
function(create_target_directory_groups target_name)
@ -92,6 +108,7 @@ find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE)
find_package(zlib-ng 2.1.7 MODULE)
find_package(Zydis 5.0.0 CONFIG)
find_package(pugixml 1.14 CONFIG)
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR NOT MSVC)
find_package(cryptopp 8.9.0 MODULE)
@ -204,6 +221,7 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/system/commondialog.h
src/core/libraries/system/msgdialog.cpp
src/core/libraries/system/msgdialog.h
src/core/libraries/system/msgdialog_ui.cpp
src/core/libraries/system/posix.cpp
src/core/libraries/system/posix.h
src/core/libraries/save_data/error_codes.h
@ -243,6 +261,11 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/ngs2/ngs2_impl.cpp
src/core/libraries/ngs2/ngs2_impl.h
src/core/libraries/ajm/ajm_error.h
src/core/libraries/audio3d/audio3d.cpp
src/core/libraries/audio3d/audio3d.h
src/core/libraries/audio3d/audio3d_error.h
src/core/libraries/audio3d/audio3d_impl.cpp
src/core/libraries/audio3d/audio3d_impl.h
)
set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
@ -291,6 +314,8 @@ set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
src/core/libraries/np_score/np_score.h
src/core/libraries/np_trophy/np_trophy.cpp
src/core/libraries/np_trophy/np_trophy.h
src/core/libraries/np_trophy/trophy_ui.cpp
src/core/libraries/np_trophy/trophy_ui.h
)
set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
@ -308,6 +333,7 @@ set(COMMON src/common/logging/backend.cpp
src/common/logging/text_formatter.h
src/common/logging/types.h
src/common/alignment.h
src/common/arch.h
src/common/assert.cpp
src/common/assert.h
src/common/bit_field.h
@ -325,6 +351,7 @@ set(COMMON src/common/logging/backend.cpp
src/common/error.cpp
src/common/error.h
src/common/scope_exit.h
src/common/fixed_value.h
src/common/func_traits.h
src/common/native_clock.cpp
src/common/native_clock.h
@ -356,8 +383,6 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/aerolib/aerolib.h
src/core/address_space.cpp
src/core/address_space.h
src/core/cpu_patches.cpp
src/core/cpu_patches.h
src/core/crypto/crypto.cpp
src/core/crypto/crypto.h
src/core/crypto/keys.h
@ -415,6 +440,12 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/virtual_memory.h
)
if (ARCHITECTURE STREQUAL "x86_64")
set(CORE ${CORE}
src/core/cpu_patches.cpp
src/core/cpu_patches.h)
endif()
set(SHADER_RECOMPILER src/shader_recompiler/exception.h
src/shader_recompiler/profile.h
src/shader_recompiler/recompiler.cpp
@ -553,6 +584,7 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/texture_cache/tile_manager.cpp
src/video_core/texture_cache/tile_manager.h
src/video_core/texture_cache/types.h
src/video_core/texture_cache/host_compatibility.h
src/video_core/page_manager.cpp
src/video_core/page_manager.h
src/video_core/multi_level_page_table.h
@ -560,6 +592,19 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/renderdoc.h
)
set(IMGUI src/imgui/imgui_config.h
src/imgui/imgui_layer.h
src/imgui/imgui_std.h
src/imgui/layer/video_info.cpp
src/imgui/layer/video_info.h
src/imgui/renderer/imgui_core.cpp
src/imgui/renderer/imgui_core.h
src/imgui/renderer/imgui_impl_sdl3.cpp
src/imgui/renderer/imgui_impl_sdl3.h
src/imgui/renderer/imgui_impl_vulkan.cpp
src/imgui/renderer/imgui_impl_vulkan.h
)
set(INPUT src/input/controller.cpp
src/input/controller.h
src/input/keys_constants.h
@ -620,6 +665,7 @@ endif()
if (ENABLE_QT_GUI)
qt_add_executable(shadps4
${AUDIO_CORE}
${IMGUI}
${INPUT}
${QT_GUI}
${COMMON}
@ -632,6 +678,7 @@ if (ENABLE_QT_GUI)
else()
add_executable(shadps4
${AUDIO_CORE}
${IMGUI}
${INPUT}
${COMMON}
${CORE}
@ -648,8 +695,11 @@ endif()
create_target_directory_groups(shadps4)
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3)
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3 pugixml::pugixml)
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
if (APPLE)
option(USE_SYSTEM_VULKAN_LOADER "Enables using the system Vulkan loader instead of directly linking with MoltenVK. Useful for loading validation layers." OFF)
@ -661,8 +711,10 @@ if (APPLE)
target_link_libraries(shadps4 PRIVATE ${MOLTENVK})
endif()
# Reserve system-managed memory space.
target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,GUEST_SYSTEM,0x400000,-image_base,0x20000000000)
if (ARCHITECTURE STREQUAL "x86_64")
# Reserve system-managed memory space.
target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,GUEST_SYSTEM,0x400000,-image_base,0x20000000000)
endif()
# Replacement for std::chrono::time_zone
target_link_libraries(shadps4 PRIVATE date::date-tz)

View file

@ -40,7 +40,7 @@ If you encounter problems or have doubts, do not hesitate to look at the [**Quic
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/MyZRaBngxA).
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/).
@ -159,6 +159,20 @@ Open a PR and we'll check it :)
<img src="https://contrib.rocks/image?repo=shadps4-emu/shadPS4&max=15">
</a>
# Special Thanks
A few noteworthy teams/projects who've helped us along the way are:
- [**Panda3DS**](https://github.com/wheremyfoodat/Panda3DS): A multiplatform 3DS emulator from our co-author wheremyfoodat. They have been incredibly helpful in understanding and solving problems that came up from natively executing the x64 code of PS4 binaries
- [**fpPS4**](https://github.com/red-prig/fpPS4): The fpPS4 team has assisted massively with understanding some of the more complex parts of the PS4 operating system and libraries, by helping with reverse engineering work and research.
- **yuzu**: Our shader compiler has been designed with yuzu's Hades compiler as a blueprint. This allowed us to focus on the challenges of emulating a modern AMD GPU while having a high-quality optimizing shader compiler implementation as a base.
- [**hydra**](https://github.com/hydra-emu/hydra): A multisystem, multiplatform emulator (chip-8, GB, NES, N64) from Paris.
# Sister Projects
- [**Panda3DS**](https://github.com/wheremyfoodat/Panda3DS): A multiplatform 3DS emulator from our co-author wheremyfoodat.

View file

@ -9,7 +9,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
#### Debian & Ubuntu
```
sudo apt-get install build-essential libasound2-dev libpulse-dev libopenal-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git libevdev-dev libsdl2-2.0 libsdl2-dev libjack-dev libsndio-dev
sudo apt-get install build-essential libasound2-dev libpulse-dev libopenal-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git libevdev-dev libsdl2-2.0 libsdl2-dev libjack-dev libsndio-dev qt6-base-dev qt6-tools-dev
```
#### Fedora
@ -34,9 +34,9 @@ git clone --recursive https://github.com/shadps4-emu/shadPS4.git
cd shadPS4
```
Generate the build directory in the shadPS4 directory:
Generate the build directory in the shadPS4 directory. To enable the QT GUI, pass the ```-DENABLE_QT_GUI=ON``` flag:
```
cmake -S . -B build/
cmake -S . -B build/ -DENABLE_QT_GUI=ON
```
Enter the directory:
@ -49,8 +49,11 @@ Use make to build the project:
cmake --build . --parallel$(nproc)
```
Now run the emulator:
Now run the emulator. If QT is enabled:
```
./shadps4
```
Otherwise, specify the path to your PKG's boot file:
```
./shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin
```

View file

@ -43,7 +43,6 @@ else()
endif()
if (NOT TARGET FFmpeg::ffmpeg)
set(ARCHITECTURE "x86_64")
add_subdirectory(ffmpeg-core)
add_library(FFmpeg::ffmpeg ALIAS ffmpeg)
endif()
@ -155,6 +154,17 @@ if (APPLE)
endif()
endif()
# Dear ImGui
add_library(Dear_ImGui
dear_imgui/imgui.cpp
dear_imgui/imgui_demo.cpp
dear_imgui/imgui_draw.cpp
dear_imgui/imgui_internal.h
dear_imgui/imgui_tables.cpp
dear_imgui/imgui_widgets.cpp
)
target_include_directories(Dear_ImGui INTERFACE dear_imgui/)
# Tracy
option(TRACY_ENABLE "" ON)
option(TRACY_NO_CRASH_HANDLER "" ON) # Otherwise texture cache exceptions will be treaten as a crash
@ -168,3 +178,8 @@ option(TRACY_NO_SAMPLING "" ON)
option(TRACY_ONLY_LOCALHOST "" ON)
option(TRACY_NO_CONTEXT_SWITCH "" ON)
add_subdirectory(tracy)
# pugixml
if (NOT TARGET pugixml::pugixml)
add_subdirectory(pugixml)
endif()

1
externals/dear_imgui vendored Submodule

@ -0,0 +1 @@
Subproject commit 636cd4a7d623a2bc9bf59bb3acbb4ca075befba3

1
externals/pugixml vendored Submodule

@ -0,0 +1 @@
Subproject commit 30cc354fe37114ec7a0a4ed2192951690357c2ed

10
src/common/arch.h Normal file
View file

@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#if defined(__x86_64__) || defined(_M_X64)
#define ARCH_X86_64 1
#elif defined(__aarch64__) || defined(_M_ARM64)
#define ARCH_ARM64 1
#endif

View file

@ -1,10 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/arch.h"
#include "common/assert.h"
#include "common/logging/backend.h"
#if defined(ARCH_X86_64)
#define Crash() __asm__ __volatile__("int $3")
#elif defined(ARCH_ARM64)
#define Crash() __asm__ __volatile__("brk 0")
#else
#error "Missing Crash() implementation for target CPU architecture."
#endif
void assert_fail_impl() {
Common::Log::Stop();
@ -18,3 +25,8 @@ void assert_fail_impl() {
Crash();
throw std::runtime_error("Unreachable code");
}
void assert_fail_debug_msg(const char* msg) {
LOG_CRITICAL(Debug, "Assertion Failed!\n{}", msg);
assert_fail_impl();
}

35
src/common/fixed_value.h Normal file
View file

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
/**
* @brief A template class that encapsulates a fixed, compile-time constant value.
*
* @tparam T The type of the value.
* @tparam Value The fixed value of type T.
*
* This class provides a way to encapsulate a value that is constant and known at compile-time.
* The value is stored as a private member and cannot be changed. Any attempt to assign a new
* value to an object of this class will reset it to the fixed value.
*/
template <typename T, T Value>
class FixedValue {
T m_value{Value};
public:
constexpr FixedValue() = default;
constexpr explicit(false) operator T() const {
return m_value;
}
FixedValue& operator=(const T&) {
m_value = Value;
return *this;
}
FixedValue& operator=(T&&) noexcept {
m_value = {Value};
return *this;
}
};

View file

@ -113,10 +113,12 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, ImeDialog) \
SUB(Lib, AvPlayer) \
SUB(Lib, Ngs2) \
SUB(Lib, Audio3d) \
CLS(Frontend) \
CLS(Render) \
SUB(Render, Vulkan) \
SUB(Render, Recompiler) \
CLS(ImGui) \
CLS(Input) \
CLS(Tty) \
CLS(Loader)

View file

@ -80,10 +80,12 @@ enum class Class : u8 {
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

View file

@ -116,6 +116,7 @@ static auto UserPaths = [] {
create_path(PathType::CheatsDir, user_dir / CHEATS_DIR);
create_path(PathType::PatchesDir, user_dir / PATCHES_DIR);
create_path(PathType::AddonsDir, user_dir / ADDONS_DIR);
create_path(PathType::MetaDataDir, user_dir / METADATA_DIR);
return paths;
}();

View file

@ -23,6 +23,7 @@ enum class PathType {
CheatsDir, // Where cheats are stored.
PatchesDir, // Where patches are stored.
AddonsDir, // Where additional content is stored.
MetaDataDir, // Where game metadata (e.g. trophies and menu backgrounds) is stored.
};
constexpr auto PORTABLE_DIR = "user";
@ -41,6 +42,7 @@ constexpr auto CAPTURES_DIR = "captures";
constexpr auto CHEATS_DIR = "cheats";
constexpr auto PATCHES_DIR = "patches";
constexpr auto ADDONS_DIR = "addcont";
constexpr auto METADATA_DIR = "game_data";
// Filenames
constexpr auto LOG_FILE = "shad_log.txt";

View file

@ -3,6 +3,8 @@
#pragma once
#include "common/arch.h"
#ifdef _MSC_VER
#include <intrin.h>
#endif
@ -13,15 +15,20 @@ namespace Common {
#ifdef _MSC_VER
__forceinline static u64 FencedRDTSC() {
#ifdef ARCH_X86_64
_mm_lfence();
_ReadWriteBarrier();
const u64 result = __rdtsc();
_mm_lfence();
_ReadWriteBarrier();
return result;
#else
#error "Missing FencedRDTSC() implementation for target CPU architecture."
#endif
}
#else
static inline u64 FencedRDTSC() {
#ifdef ARCH_X86_64
u64 eax;
u64 edx;
asm volatile("lfence\n\t"
@ -29,6 +36,16 @@ static inline u64 FencedRDTSC() {
"lfence\n\t"
: "=a"(eax), "=d"(edx));
return (edx << 32) | eax;
#elif defined(ARCH_ARM64)
u64 ret;
asm volatile("isb\n\t"
"mrs %0, cntvct_el0\n\t"
"isb\n\t"
: "=r"(ret)::"memory");
return ret;
#else
#error "Missing FencedRDTSC() implementation for target CPU architecture."
#endif
}
#endif

View file

@ -3,11 +3,13 @@
#include <boost/icl/separate_interval_set.hpp>
#include "common/alignment.h"
#include "common/arch.h"
#include "common/assert.h"
#include "common/error.h"
#include "core/address_space.h"
#include "core/libraries/kernel/memory_management.h"
#include "core/memory.h"
#include "libraries/error_codes.h"
#ifdef _WIN32
#include <windows.h>
@ -16,7 +18,7 @@
#include <sys/mman.h>
#endif
#ifdef __APPLE__
#if defined(__APPLE__) && defined(ARCH_X86_64)
// Reserve space for the system address space using a zerofill section.
asm(".zerofill GUEST_SYSTEM,GUEST_SYSTEM,__guest_system,0xFBFC00000");
#endif
@ -231,27 +233,36 @@ struct AddressSpace::Impl {
void Protect(VAddr virtual_addr, size_t size, bool read, bool write, bool execute) {
DWORD new_flags{};
if (read && write) {
if (read && write && execute) {
new_flags = PAGE_EXECUTE_READWRITE;
} else if (read && write) {
new_flags = PAGE_READWRITE;
} else if (read && !write) {
new_flags = PAGE_READONLY;
} else if (!read && !write) {
} else if (execute && !read && not write) {
new_flags = PAGE_EXECUTE;
} else if (!read && !write && !execute) {
new_flags = PAGE_NOACCESS;
} else {
UNIMPLEMENTED_MSG("Protection flag combination read={} write={}", read, write);
LOG_CRITICAL(Common_Memory,
"Unsupported protection flag combination for address {:#x}, size {}",
virtual_addr, size);
return;
}
const VAddr virtual_end = virtual_addr + size;
auto [it, end] = placeholders.equal_range({virtual_addr, virtual_end});
while (it != end) {
const size_t offset = std::max(it->lower(), virtual_addr);
const size_t protect_length = std::min(it->upper(), virtual_end) - offset;
DWORD old_flags{};
if (!VirtualProtect(virtual_base + offset, protect_length, new_flags, &old_flags)) {
LOG_CRITICAL(Common_Memory, "Failed to change virtual memory protect rules");
}
++it;
DWORD old_flags{};
bool success =
VirtualProtect(reinterpret_cast<void*>(virtual_addr), size, new_flags, &old_flags);
if (!success) {
LOG_ERROR(Common_Memory,
"Failed to change virtual memory protection for address {:#x}, size {}",
virtual_addr, size);
}
// Use assert to ensure success in debug builds
DEBUG_ASSERT(success && "Failed to change virtual memory protection");
}
HANDLE process{};
@ -298,12 +309,12 @@ struct AddressSpace::Impl {
constexpr int protection_flags = PROT_READ | PROT_WRITE;
constexpr int base_map_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
#ifdef __APPLE__
// On ARM64 Macs, we run into limitations due to the commpage from 0xFC0000000 - 0xFFFFFFFFF
// and the GPU carveout region from 0x1000000000 - 0x6FFFFFFFFF. We can allocate the system
// managed region, as well as system reserved if reduced in size slightly, but we cannot map
// the user region where we want, so we must let the OS put it wherever possible and hope
// the game won't rely on its location.
#if defined(__APPLE__) && defined(ARCH_X86_64)
// On ARM64 Macs under Rosetta 2, we run into limitations due to the commpage from
// 0xFC0000000 - 0xFFFFFFFFF and the GPU carveout region from 0x1000000000 - 0x6FFFFFFFFF.
// We can allocate the system managed region, as well as system reserved if reduced in size
// slightly, but we cannot map the user region where we want, so we must let the OS put it
// wherever possible and hope the game won't rely on its location.
system_managed_base = reinterpret_cast<u8*>(
mmap(reinterpret_cast<void*>(SYSTEM_MANAGED_MIN), system_managed_size, protection_flags,
base_map_flags | MAP_FIXED, -1, 0));
@ -315,12 +326,22 @@ struct AddressSpace::Impl {
protection_flags, base_map_flags, -1, 0));
#else
const auto virtual_size = system_managed_size + system_reserved_size + user_size;
#if defined(ARCH_X86_64)
const auto virtual_base =
reinterpret_cast<u8*>(mmap(reinterpret_cast<void*>(SYSTEM_MANAGED_MIN), virtual_size,
protection_flags, base_map_flags | MAP_FIXED, -1, 0));
system_managed_base = virtual_base;
system_reserved_base = reinterpret_cast<u8*>(SYSTEM_RESERVED_MIN);
user_base = reinterpret_cast<u8*>(USER_MIN);
#else
// Map memory wherever possible and instruction translation can handle offsetting to the
// base.
const auto virtual_base = reinterpret_cast<u8*>(
mmap(nullptr, virtual_size, protection_flags, base_map_flags, -1, 0));
system_managed_base = virtual_base;
system_reserved_base = virtual_base + SYSTEM_RESERVED_MIN - SYSTEM_MANAGED_MIN;
user_base = virtual_base + USER_MIN - SYSTEM_MANAGED_MIN;
#endif
#endif
if (system_managed_base == MAP_FAILED || system_reserved_base == MAP_FAILED ||
user_base == MAP_FAILED) {
@ -420,9 +441,11 @@ struct AddressSpace::Impl {
if (write) {
flags |= PROT_WRITE;
}
#ifdef ARCH_X86_64
if (execute) {
flags |= PROT_EXEC;
}
#endif
int ret = mprotect(reinterpret_cast<void*>(virtual_addr), size, flags);
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
}
@ -453,8 +476,14 @@ AddressSpace::~AddressSpace() = default;
void* AddressSpace::Map(VAddr virtual_addr, size_t size, u64 alignment, PAddr phys_addr,
bool is_exec) {
return impl->Map(virtual_addr, phys_addr, size,
is_exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
#if ARCH_X86_64
const auto prot = is_exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
#else
// On non-native architectures, we can simplify things by ignoring the execute flag for the
// canonical copy of the memory and rely on the JIT to map translated code as executable.
constexpr auto prot = PAGE_READWRITE;
#endif
return impl->Map(virtual_addr, phys_addr, size, prot);
}
void* AddressSpace::MapFile(VAddr virtual_addr, size_t size, size_t offset, u32 prot,
@ -493,7 +522,10 @@ void AddressSpace::Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VA
}
void AddressSpace::Protect(VAddr virtual_addr, size_t size, MemoryPermission perms) {
return impl->Protect(virtual_addr, size, true, true, true);
const bool read = True(perms & MemoryPermission::Read);
const bool write = True(perms & MemoryPermission::Write);
const bool execute = True(perms & MemoryPermission::Execute);
return impl->Protect(virtual_addr, size, read, write, execute);
}
} // namespace Core

View file

@ -4,6 +4,7 @@
#pragma once
#include <memory>
#include "common/arch.h"
#include "common/enum.h"
#include "common/types.h"
@ -23,7 +24,7 @@ constexpr VAddr CODE_BASE_OFFSET = 0x100000000ULL;
constexpr VAddr SYSTEM_MANAGED_MIN = 0x00000400000ULL;
constexpr VAddr SYSTEM_MANAGED_MAX = 0x07FFFFBFFFULL;
constexpr VAddr SYSTEM_RESERVED_MIN = 0x07FFFFC000ULL;
#ifdef __APPLE__
#if defined(__APPLE__) && defined(ARCH_X86_64)
// Can only comfortably reserve the first 0x7C0000000 of system reserved space.
constexpr VAddr SYSTEM_RESERVED_MAX = 0xFBFFFFFFFULL;
#else

View file

@ -315,14 +315,12 @@ static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerat
SaveRegisters(c, {scratch});
// BLSI sets CF to zero if source is zero, otherwise it sets CF to one.
Xbyak::Label set_carry, clear_carry, end;
Xbyak::Label clear_carry, end;
c.mov(scratch, *src);
c.neg(scratch); // NEG, like BLSI, clears CF if the source is zero and sets it otherwise
c.jc(set_carry);
c.jmp(clear_carry);
c.jnc(clear_carry);
c.L(set_carry);
c.and_(scratch, *src);
c.stc(); // setting/clearing carry needs to happen after the AND because that clears CF
c.jmp(end);
@ -345,15 +343,13 @@ static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGener
SaveRegisters(c, {scratch});
Xbyak::Label set_carry, clear_carry, end;
Xbyak::Label clear_carry, end;
// BLSMSK sets CF to zero if source is NOT zero, otherwise it sets CF to one.
c.mov(scratch, *src);
c.test(scratch, scratch);
c.jz(set_carry);
c.jmp(clear_carry);
c.jnz(clear_carry);
c.L(set_carry);
c.dec(scratch);
c.xor_(scratch, *src);
c.stc();
@ -378,15 +374,13 @@ static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerat
SaveRegisters(c, {scratch});
Xbyak::Label set_carry, clear_carry, end;
Xbyak::Label clear_carry, end;
// BLSR sets CF to zero if source is NOT zero, otherwise it sets CF to one.
c.mov(scratch, *src);
c.test(scratch, scratch);
c.jz(set_carry);
c.jmp(clear_carry);
c.jnz(clear_carry);
c.L(set_carry);
c.dec(scratch);
c.and_(scratch, *src);
c.stc();

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/path_util.h"
#include "trp.h"
TRP::TRP() = default;
@ -48,8 +49,9 @@ bool TRP::Extract(const std::filesystem::path& trophyPath) {
return false;
s64 seekPos = sizeof(TrpHeader);
std::filesystem::path trpFilesPath(std::filesystem::current_path() / "user/game_data" /
title / "TrophyFiles" / it.path().stem());
std::filesystem::path trpFilesPath(
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / title / "TrophyFiles" /
it.path().stem());
std::filesystem::create_directories(trpFilesPath / "Icons");
std::filesystem::create_directory(trpFilesPath / "Xml");

View file

@ -0,0 +1,344 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio3d.h"
#include "audio3d_error.h"
#include "audio3d_impl.h"
#include "common/logging/log.h"
#include "core/libraries/audio/audioout.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
namespace Libraries::Audio3d {
// Audio3d
int PS4_SYSV_ABI sceAudio3dInitialize(s64 iReserved) {
LOG_INFO(Lib_Audio3d, "iReserved = {}", iReserved);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dTerminate() {
// TODO: When not initialized or some ports still open, return ORBIS_AUDIO3D_ERROR_NOT_READY
LOG_INFO(Lib_Audio3d, "called");
return ORBIS_OK;
}
void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* sParameters) {
if (sParameters != NULL) {
sParameters->szSizeThis = sizeof(OrbisAudio3dOpenParameters);
sParameters->uiGranularity = 256;
sParameters->eRate = ORBIS_AUDIO3D_RATE_48000;
sParameters->uiMaxObjects = 512;
sParameters->uiQueueDepth = 2;
sParameters->eBufferMode = ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH;
sParameters->uiNumBeds = 2;
} else {
LOG_ERROR(Lib_Audio3d, "Invalid OpenParameters ptr");
}
}
int PS4_SYSV_ABI sceAudio3dPortOpen(OrbisUserServiceUserId iUserId,
const OrbisAudio3dOpenParameters* pParameters,
OrbisAudio3dPortId* pId) {
LOG_INFO(Lib_Audio3d, "iUserId = {}", iUserId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortClose(OrbisAudio3dPortId uiPortId) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortSetAttribute(OrbisAudio3dPortId uiPortId,
OrbisAudio3dAttributeId uiAttributeId,
const void* pAttribute, size_t szAttribute) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiAttributeId = {}, szAttribute = {}", uiPortId,
uiAttributeId, szAttribute);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortFlush(OrbisAudio3dPortId uiPortId) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId uiPortId) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId uiPortId, OrbisAudio3dBlocking eBlocking) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported(OrbisAudio3dPortId uiPortId,
OrbisAudio3dAttributeId* pCapabilities,
unsigned int* pNumCapabilities) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId uiPortId, unsigned int* pQueueLevel,
unsigned int* pQueueAvailable) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dObjectReserve(OrbisAudio3dPortId uiPortId, OrbisAudio3dObjectId* pId) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dObjectUnreserve(OrbisAudio3dPortId uiPortId,
OrbisAudio3dObjectId uiObjectId) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiObjectId = {}", uiPortId, uiObjectId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dObjectSetAttributes(OrbisAudio3dPortId uiPortId,
OrbisAudio3dObjectId uiObjectId,
size_t szNumAttributes,
const OrbisAudio3dAttribute* pAttributeArray) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiObjectId = {}, szNumAttributes = {}", uiPortId,
uiObjectId, szNumAttributes);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId uiPortId, unsigned int uiNumChannels,
OrbisAudio3dFormat eFormat, const void* pBuffer,
unsigned int uiNumSamples) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId,
uiNumChannels, uiNumSamples);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dBedWrite2(OrbisAudio3dPortId uiPortId, unsigned int uiNumChannels,
OrbisAudio3dFormat eFormat, const void* pBuffer,
unsigned int uiNumSamples,
OrbisAudio3dOutputRoute eOutputRoute, bool bRestricted) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}, bRestricted = {}",
uiPortId, uiNumChannels, uiNumSamples, bRestricted);
return ORBIS_OK;
}
size_t PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize(unsigned int uiNumSpeakers, bool bIs3d) {
LOG_INFO(Lib_Audio3d, "uiNumSpeakers = {}, bIs3d = {}", uiNumSpeakers, bIs3d);
return ORBIS_OK;
}
int PS4_SYSV_ABI
sceAudio3dCreateSpeakerArray(OrbisAudio3dSpeakerArrayHandle* pHandle,
const OrbisAudio3dSpeakerArrayParameters* pParameters) {
if (pHandle == nullptr || pParameters == nullptr) {
LOG_ERROR(Lib_Audio3d, "invalid SpeakerArray parameters");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
LOG_INFO(Lib_Audio3d, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dDeleteSpeakerArray(OrbisAudio3dSpeakerArrayHandle handle) {
if (handle == nullptr) {
LOG_ERROR(Lib_Audio3d, "invalid SpeakerArrayHandle");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
LOG_INFO(Lib_Audio3d, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients(OrbisAudio3dSpeakerArrayHandle handle,
OrbisAudio3dPosition pos, float fSpread,
float* pCoefficients,
unsigned int uiNumCoefficients) {
LOG_INFO(Lib_Audio3d, "fSpread = {}, uiNumCoefficients = {}", fSpread, uiNumCoefficients);
if (handle == nullptr) {
LOG_ERROR(Lib_Audio3d, "invalid SpeakerArrayHandle");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2(OrbisAudio3dSpeakerArrayHandle handle,
OrbisAudio3dPosition pos, float fSpread,
float* pCoefficients,
unsigned int uiNumCoefficients,
bool bHeightAware,
float fDownmixSpreadRadius) {
LOG_INFO(Lib_Audio3d,
"fSpread = {}, uiNumCoefficients = {}, bHeightAware = {}, fDownmixSpreadRadius = {}",
fSpread, uiNumCoefficients, bHeightAware, fDownmixSpreadRadius);
if (handle == nullptr) {
LOG_ERROR(Lib_Audio3d, "invalid SpeakerArrayHandle");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(OrbisAudio3dPortId uiPortId, OrbisUserServiceUserId userId,
s32 type, s32 index, u32 len, u32 freq, u32 param) {
LOG_INFO(Lib_Audio3d,
"uiPortId = {}, userId = {}, type = {}, index = {}, len = {}, freq = {}, param = {}",
uiPortId, userId, type, index, len, freq, param);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle) {
LOG_INFO(Lib_Audio3d, "handle = {}", handle);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, const void* ptr) {
LOG_INFO(Lib_Audio3d, "handle = {}", handle);
if (ptr == nullptr) {
LOG_ERROR(Lib_Audio3d, "invalid Output ptr");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceAudio3dAudioOutOutputs(::Libraries::AudioOut::OrbisAudioOutOutputParam* param,
s32 num) {
LOG_INFO(Lib_Audio3d, "num = {}", num);
if (param == nullptr) {
LOG_ERROR(Lib_Audio3d, "invalid OutputParam ptr");
return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER;
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortCreate(unsigned int uiGranularity, OrbisAudio3dRate eRate,
s64 iReserved, OrbisAudio3dPortId* pId) {
LOG_INFO(Lib_Audio3d, "uiGranularity = {}, iReserved = {}", uiGranularity, iReserved);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortDestroy(OrbisAudio3dPortId uiPortId) {
LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId);
return ORBIS_OK;
}
// Audio3dPrivate
const char* PS4_SYSV_ABI sceAudio3dStrError(int iErrorCode) {
LOG_ERROR(Lib_Audio3d, "(PRIVATE) called, iErrorCode = {}", iErrorCode);
return "NOT_IMPLEMENTED";
}
// Audio3dDebug
int PS4_SYSV_ABI sceAudio3dPortQueryDebug() {
LOG_WARNING(Lib_Audio3d, "(DEBUG) sceAudio3dPortQueryDebug called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortGetList() {
LOG_WARNING(Lib_Audio3d, "(DEBUG) sceAudio3dPortGetList called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortGetParameters() {
LOG_WARNING(Lib_Audio3d, "(DEBUG) sceAudio3dPortGetParameters called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortGetState() {
LOG_WARNING(Lib_Audio3d, "(DEBUG) sceAudio3dPortGetState called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortFreeState() {
LOG_WARNING(Lib_Audio3d, "(DEBUG) sceAudio3dPortFreeState called");
return ORBIS_OK;
}
// Unknown
int PS4_SYSV_ABI sceAudio3dPortGetBufferLevel() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dPortGetStatus() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dReportRegisterHandler() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dReportUnregisterHandler() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceAudio3dSetGpuRenderer() {
LOG_ERROR(Lib_Audio3d, "(STUBBED) called");
return ORBIS_OK;
}
void RegisterlibSceAudio3d(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("-R1DukFq7Dk", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dGetSpeakerArrayMixCoefficients);
LIB_FUNCTION("-Re+pCWvwjQ", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dGetSpeakerArrayMixCoefficients2);
LIB_FUNCTION("-pzYDZozm+M", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dPortQueryDebug);
LIB_FUNCTION("1HXxo-+1qCw", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dObjectUnreserve);
LIB_FUNCTION("4uyHN9q4ZeU", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dObjectSetAttributes);
LIB_FUNCTION("7NYEzJ9SJbM", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dAudioOutOutput);
LIB_FUNCTION("8hm6YdoQgwg", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dDeleteSpeakerArray);
LIB_FUNCTION("9ZA23Ia46Po", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dPortGetAttributesSupported);
LIB_FUNCTION("9tEwE0GV0qo", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dBedWrite);
LIB_FUNCTION("Aacl5qkRU6U", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dStrError);
LIB_FUNCTION("CKHlRW2E9dA", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortGetState);
LIB_FUNCTION("HbxYY27lK6E", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dAudioOutOutputs);
LIB_FUNCTION("Im+jOoa5WAI", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dGetDefaultOpenParameters);
LIB_FUNCTION("Mw9mRQtWepY", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortDestroy);
LIB_FUNCTION("OyVqOeVNtSk", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortClose);
LIB_FUNCTION("QfNXBrKZeI0", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dReportRegisterHandler);
LIB_FUNCTION("QqgTQQdzEMY", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dPortGetBufferLevel);
LIB_FUNCTION("SEggctIeTcI", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortGetList);
LIB_FUNCTION("UHFOgVNz0kk", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortCreate);
LIB_FUNCTION("UmCvjSmuZIw", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dInitialize);
LIB_FUNCTION("VEVhZ9qd4ZY", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortPush);
LIB_FUNCTION("WW1TS2iz5yc", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dTerminate);
LIB_FUNCTION("XeDDK0xJWQA", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortOpen);
LIB_FUNCTION("YaaDbDwKpFM", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dPortGetQueueLevel);
LIB_FUNCTION("Yq9bfUQ0uJg", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dPortSetAttribute);
LIB_FUNCTION("ZOGrxWLgQzE", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortFlush);
LIB_FUNCTION("flPcUaXVXcw", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dPortGetParameters);
LIB_FUNCTION("iRX6GJs9tvE", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortGetStatus);
LIB_FUNCTION("jO2tec4dJ2M", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dObjectReserve);
LIB_FUNCTION("kEqqyDkmgdI", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dGetSpeakerArrayMemorySize);
LIB_FUNCTION("lvWMW6vEqFU", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dCreateSpeakerArray);
LIB_FUNCTION("lw0qrdSjZt8", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortAdvance);
LIB_FUNCTION("pZlOm1aF3aA", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dAudioOutClose);
LIB_FUNCTION("psv2gbihC1A", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dReportUnregisterHandler);
LIB_FUNCTION("uJ0VhGcxCTQ", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortFreeState);
LIB_FUNCTION("ucEsi62soTo", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dAudioOutOpen);
LIB_FUNCTION("xH4Q9UILL3o", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dBedWrite2);
LIB_FUNCTION("yEYXcbAGK14", "libSceAudio3d", 1, "libSceAudio3d", 1, 1,
sceAudio3dSetGpuRenderer);
};
} // namespace Libraries::Audio3d

View file

@ -0,0 +1,134 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include <stddef.h>
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Audio3d {
class Audio3d;
typedef int OrbisUserServiceUserId;
typedef unsigned int OrbisAudio3dPortId;
typedef unsigned int OrbisAudio3dObjectId;
typedef unsigned int OrbisAudio3dAttributeId;
enum OrbisAudio3dFormat {
ORBIS_AUDIO3D_FORMAT_S16 = 0, // s16
ORBIS_AUDIO3D_FORMAT_FLOAT = 1 // f32
};
enum OrbisAudio3dRate { ORBIS_AUDIO3D_RATE_48000 = 0 };
enum OrbisAudio3dBufferMode {
ORBIS_AUDIO3D_BUFFER_NO_ADVANCE = 0,
ORBIS_AUDIO3D_BUFFER_ADVANCE_NO_PUSH = 1,
ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH = 2
};
enum OrbisAudio3dBlocking { ORBIS_AUDIO3D_BLOCKING_ASYNC = 0, ORBIS_AUDIO3D_BLOCKING_SYNC = 1 };
enum OrbisAudio3dPassthrough {
ORBIS_AUDIO3D_PASSTHROUGH_NONE = 0,
ORBIS_AUDIO3D_PASSTHROUGH_LEFT = 1,
ORBIS_AUDIO3D_PASSTHROUGH_RIGHT = 2
};
enum OrbisAudio3dOutputRoute {
ORBIS_AUDIO3D_OUTPUT_BOTH = 0,
ORBIS_AUDIO3D_OUTPUT_HMU_ONLY = 1,
ORBIS_AUDIO3D_OUTPUT_TV_ONLY = 2
};
enum OrbisAudio3dAmbisonics {
ORBIS_AUDIO3D_AMBISONICS_NONE = ~0,
ORBIS_AUDIO3D_AMBISONICS_W = 0,
ORBIS_AUDIO3D_AMBISONICS_X = 1,
ORBIS_AUDIO3D_AMBISONICS_Y = 2,
ORBIS_AUDIO3D_AMBISONICS_Z = 3,
ORBIS_AUDIO3D_AMBISONICS_R = 4,
ORBIS_AUDIO3D_AMBISONICS_S = 5,
ORBIS_AUDIO3D_AMBISONICS_T = 6,
ORBIS_AUDIO3D_AMBISONICS_U = 7,
ORBIS_AUDIO3D_AMBISONICS_V = 8,
ORBIS_AUDIO3D_AMBISONICS_K = 9,
ORBIS_AUDIO3D_AMBISONICS_L = 10,
ORBIS_AUDIO3D_AMBISONICS_M = 11,
ORBIS_AUDIO3D_AMBISONICS_N = 12,
ORBIS_AUDIO3D_AMBISONICS_O = 13,
ORBIS_AUDIO3D_AMBISONICS_P = 14,
ORBIS_AUDIO3D_AMBISONICS_Q = 15
};
static const OrbisAudio3dAttributeId s_sceAudio3dAttributePcm = 0x00000001;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributePriority = 0x00000002;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributePosition = 0x00000003;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributeSpread = 0x00000004;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributeGain = 0x00000005;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributePassthrough = 0x00000006;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributeResetState = 0x00000007;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributeApplicationSpecific = 0x00000008;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributeAmbisonics = 0x00000009;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributeRestricted = 0x0000000A;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributeOutputRoute = 0x0000000B;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributeLateReverbLevel = 0x00010001;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributeDownmixSpreadRadius = 0x00010002;
static const OrbisAudio3dAttributeId s_sceAudio3dAttributeDownmixSpreadHeightAware = 0x00010003;
struct OrbisAudio3dSpeakerArray;
using OrbisAudio3dSpeakerArrayHandle = OrbisAudio3dSpeakerArray*; // head
struct OrbisAudio3dOpenParameters {
size_t szSizeThis;
unsigned int uiGranularity;
OrbisAudio3dRate eRate;
unsigned int uiMaxObjects;
unsigned int uiQueueDepth;
OrbisAudio3dBufferMode eBufferMode;
char padding[32];
unsigned int uiNumBeds;
};
struct OrbisAudio3dAttribute {
OrbisAudio3dAttributeId uiAttributeId;
char padding[32];
const void* pValue;
size_t szValue;
};
struct OrbisAudio3dPosition {
float fX;
float fY;
float fZ;
};
struct OrbisAudio3dPcm {
OrbisAudio3dFormat eFormat;
const void* pSampleBuffer;
unsigned int uiNumSamples;
};
struct OrbisAudio3dSpeakerArrayParameters {
OrbisAudio3dPosition* pSpeakerPosition;
unsigned int uiNumSpeakers;
bool bIs3d;
void* pBuffer;
size_t szSize;
};
struct OrbisAudio3dApplicationSpecific {
size_t szSizeThis;
u8 cApplicationSpecific[32];
};
void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* sParameters);
void RegisterlibSceAudio3d(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Audio3d

View file

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
constexpr int ORBIS_AUDIO3D_ERROR_UNKNOWN = 0x80EA0001;
constexpr int ORBIS_AUDIO3D_ERROR_INVALID_PORT = 0x80EA0002;
constexpr int ORBIS_AUDIO3D_ERROR_INVALID_OBJECT = 0x80EA0003;
constexpr int ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER = 0x80EA0004;
constexpr int ORBIS_AUDIO3D_ERROR_OUT_OF_MEMORY = 0x80EA0005;
constexpr int ORBIS_AUDIO3D_ERROR_OUT_OF_RESOURCES = 0x80EA0006;
constexpr int ORBIS_AUDIO3D_ERROR_NOT_READY = 0x80EA0007;
constexpr int ORBIS_AUDIO3D_ERROR_NOT_SUPPORTED = 0x80EA0008;

View file

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio3d_error.h"
#include "audio3d_impl.h"
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/libkernel.h"
using namespace Libraries::Kernel;
namespace Libraries::Audio3d {} // namespace Libraries::Audio3d

View file

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "audio3d.h"
namespace Libraries::Audio3d {
class Audio3d {
public:
private:
typedef unsigned int OrbisAudio3dPluginId;
};
} // namespace Libraries::Audio3d

View file

@ -20,6 +20,17 @@ extern "C" {
#include <libswscale/swscale.h>
}
// The av_err2str macro in libavutil/error.h does not play nice with C++
#ifdef av_err2str
#undef av_err2str
#include <string>
av_always_inline std::string av_err2string(int errnum) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum);
}
#define av_err2str(err) av_err2string(err).c_str()
#endif // av_err2str
namespace Libraries::AvPlayer {
using namespace Kernel;

View file

@ -440,11 +440,47 @@ constexpr int ORBIS_USER_SERVICE_ERROR_BUFFER_TOO_SHORT = 0x8096000A;
constexpr int ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER = 0x80A10003;
// NpTrophy library
constexpr int ORBIS_NP_TROPHY_ERROR_UNKNOWN = 0x80551600;
constexpr int ORBIS_NP_TROPHY_ERROR_NOT_INITIALIZED = 0x80551601;
constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_INITIALIZED = 0x80551602;
constexpr int ORBIS_NP_TROPHY_ERROR_OUT_OF_MEMORY = 0x80551603;
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT = 0x80551604;
constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_BUFFER = 0x80551605;
constexpr int ORBIS_NP_TROPHY_ERROR_EXCEEDS_MAX = 0x80551606;
constexpr int ORBIS_NP_TROPHY_ERROR_ABORT = 0x80551607;
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE = 0x80551608;
constexpr int ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX = 0x80551624;
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT = 0x80551609;
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID = 0x8055160A;
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_GROUP_ID = 0x8055160B;
constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED = 0x8055160C;
constexpr int ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK = 0x8055160D;
constexpr int ORBIS_NP_TROPHY_ERROR_ACCOUNTID_NOT_MATCH = 0x8055160E;
constexpr int ORBIS_NP_TROPHY_ERROR_NOT_REGISTERED = 0x8055160F;
constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_REGISTERED = 0x80551610;
constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_DATA = 0x80551611;
constexpr int ORBIS_NP_TROPHY_ERROR_INSUFFICIENT_SPACE = 0x80551612;
constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_ALREADY_EXISTS = 0x80551613;
constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_EXCEEDS_MAX = 0x80551622;
constexpr int ORBIS_NP_TROPHY_ERROR_ICON_FILE_NOT_FOUND = 0x80551614;
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TRP_FILE_FORMAT = 0x80551616;
constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TRP_FILE = 0x80551617;
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_CONF_FORMAT = 0x80551618;
constexpr int ORBIS_NP_TROPHY_ERROR_UNSUPPORTED_TROPHY_CONF = 0x80551619;
constexpr int ORBIS_NP_TROPHY_ERROR_TROPHY_NOT_UNLOCKED = 0x8055161A;
constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_FOUND = 0x8055161C;
constexpr int ORBIS_NP_TROPHY_ERROR_USER_NOT_LOGGED_IN = 0x8055161D;
constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_USER_LOGOUT = 0x8055161E;
constexpr int ORBIS_NP_TROPHY_ERROR_USE_TRP_FOR_DEVELOPMENT = 0x8055161F;
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_NP_SERVICE_LABEL = 0x80551621;
constexpr int ORBIS_NP_TROPHY_ERROR_NOT_SUPPORTED = 0x80551622;
constexpr int ORBIS_NP_TROPHY_ERROR_CONTEXT_EXCEEDS_MAX = 0x80551623;
constexpr int ORBIS_NP_TROPHY_ERROR_HANDLE_EXCEEDS_MAX = 0x80551624;
constexpr int ORBIS_NP_TROPHY_ERROR_INVALID_USER_ID = 0x80551625;
constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_CONF_NOT_INSTALLED = 0x80551626;
constexpr int ORBIS_NP_TROPHY_ERROR_BROKEN_TITLE_CONF = 0x80551627;
constexpr int ORBIS_NP_TROPHY_ERROR_INCONSISTENT_TITLE_CONF = 0x80551628;
constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_BACKGROUND = 0x80551629;
constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISABLED = 0x8055162B;
constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_NOT_IN_USE = 0x8055162D;
// AvPlayer library
constexpr int ORBIS_AVPLAYER_ERROR_INVALID_PARAMS = 0x806A0001;

View file

@ -2155,6 +2155,7 @@ int PS4_SYSV_ABI sceGnmSubmitCommandBuffersForWorkload() {
int PS4_SYSV_ABI sceGnmSubmitDone() {
LOG_DEBUG(Lib_GnmDriver, "called");
WaitGpuIdle();
if (!liverpool->IsGpuIdle()) {
submission_lock = true;
}

View file

@ -8,7 +8,7 @@
namespace Libraries::Kernel {
int PS4_SYSV_ABI sceKernelIsNeoMode() {
LOG_INFO(Kernel_Sce, "called");
LOG_DEBUG(Kernel_Sce, "called");
return Config::isNeoMode();
}

View file

@ -78,7 +78,7 @@ int PS4_SYSV_ABI sceKernelCloseEventFlag() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) {
LOG_INFO(Kernel_Event, "called");
LOG_DEBUG(Kernel_Event, "called");
ef->Clear(bitPattern);
return ORBIS_OK;
}
@ -97,7 +97,7 @@ int PS4_SYSV_ABI sceKernelSetEventFlag(OrbisKernelEventFlag ef, u64 bitPattern)
}
int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode,
u64* pResultPat) {
LOG_INFO(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode);
LOG_DEBUG(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode);
if (ef == nullptr) {
return ORBIS_KERNEL_ERROR_ESRCH;
@ -145,7 +145,7 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
}
int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode,
u64* pResultPat, OrbisKernelUseconds* pTimeout) {
LOG_INFO(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode);
LOG_DEBUG(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode);
if (ef == nullptr) {
return ORBIS_KERNEL_ERROR_ESRCH;
}

View file

@ -454,6 +454,8 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent);
LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent);
LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId);
LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect);
LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect);
LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter);
// misc

View file

@ -33,6 +33,8 @@ typedef struct {
} OrbisKernelUuid;
int* PS4_SYSV_ABI __Error();
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
struct OrbisTimesec* st, unsigned long* dst_sec);
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver);
void LibKernel_Register(Core::Loader::SymbolsResolver* sym);

View file

@ -7,6 +7,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/singleton.h"
#include "core/address_space.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/memory_management.h"
#include "core/linker.h"
@ -218,6 +219,19 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void**
return memory->QueryProtection(std::bit_cast<VAddr>(addr), start, end, prot);
}
int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot) {
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags);
}
int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot) {
Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
return memory_manager->MTypeProtect(std::bit_cast<VAddr>(addr), size,
static_cast<Core::VMAType>(mtype), protection_flags);
}
int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info,
size_t infoSize) {
LOG_WARNING(Kernel_Vmm, "called offset = {:#x}, flags = {:#x}", offset, flags);
@ -282,6 +296,12 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
entries[i].operation, entries[i].length, result);
break;
}
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_PROTECT: {
result = sceKernelMProtect(entries[i].start, entries[i].length, entries[i].protection);
LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
entries[i].operation, entries[i].length, result);
break;
}
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE: {
result = sceKernelMapNamedFlexibleMemory(&entries[i].start, entries[i].length,
entries[i].protection, flags, "");
@ -292,11 +312,10 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
break;
}
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT: {
// By now, ignore protection and log it instead
LOG_WARNING(Kernel_Vmm,
"entry = {}, operation = {}, len = {:#x}, type = {} "
"is UNSUPPORTED and skipped",
i, entries[i].operation, entries[i].length, (u8)entries[i].type);
result = sceKernelMTypeProtect(entries[i].start, entries[i].length, entries[i].type,
entries[i].protection);
LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
entries[i].operation, entries[i].length, result);
break;
}
default: {

View file

@ -95,6 +95,10 @@ s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len,
int flags);
int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot);
int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot);
int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot);
int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info,
size_t infoSize);
s32 PS4_SYSV_ABI sceKernelAvailableFlexibleMemorySize(size_t* sizeOut);

View file

@ -6,6 +6,7 @@
#include <thread>
#include "common/alignment.h"
#include "common/arch.h"
#include "common/assert.h"
#include "common/error.h"
#include "common/logging/log.h"
@ -53,11 +54,12 @@ void init_pthreads() {
}
void pthreadInitSelfMainThread() {
const char* name = "Main_Thread";
auto* pthread_pool = g_pthread_cxt->GetPthreadPool();
g_pthread_self = pthread_pool->Create();
g_pthread_self = pthread_pool->Create(name);
scePthreadAttrInit(&g_pthread_self->attr);
g_pthread_self->pth = pthread_self();
g_pthread_self->name = "Main_Thread";
g_pthread_self->name = name;
}
int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr) {
@ -295,7 +297,7 @@ ScePthread PS4_SYSV_ABI scePthreadSelf() {
int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr,
const /*SceKernelCpumask*/ u64 mask) {
LOG_INFO(Kernel_Pthread, "called");
LOG_DEBUG(Kernel_Pthread, "called");
if (pattr == nullptr || *pattr == nullptr) {
return SCE_KERNEL_ERROR_EINVAL;
@ -387,7 +389,7 @@ int PS4_SYSV_ABI posix_pthread_attr_setstacksize(ScePthreadAttr* attr, size_t st
}
int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask) {
LOG_INFO(Kernel_Pthread, "called");
LOG_DEBUG(Kernel_Pthread, "called");
if (thread == nullptr) {
return SCE_KERNEL_ERROR_ESRCH;
@ -414,11 +416,6 @@ ScePthreadMutex* createMutex(ScePthreadMutex* addr) {
if (addr == nullptr || *addr != nullptr) {
return addr;
}
static std::mutex mutex;
std::scoped_lock lk{mutex};
if (*addr != nullptr) {
return addr;
}
const VAddr vaddr = reinterpret_cast<VAddr>(addr);
std::string name = fmt::format("mutex{:#x}", vaddr);
scePthreadMutexInit(addr, nullptr, name.c_str());
@ -584,8 +581,7 @@ int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex) {
}
int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex) {
mutex = createMutex(mutex);
if (mutex == nullptr) {
if (mutex == nullptr || *mutex == nullptr) {
return SCE_KERNEL_ERROR_EINVAL;
}
@ -995,7 +991,9 @@ static void cleanup_thread(void* arg) {
static void* run_thread(void* arg) {
auto* thread = static_cast<ScePthread>(arg);
Common::SetCurrentThreadName(thread->name.c_str());
#ifdef ARCH_X86_64
Core::InitializeThreadPatchStack();
#endif
auto* linker = Common::Singleton<Core::Linker>::Instance();
linker->InitTlsForThread(false);
void* ret = nullptr;
@ -1019,7 +1017,7 @@ int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr
attr = g_pthread_cxt->GetDefaultAttr();
}
*thread = pthread_pool->Create();
*thread = pthread_pool->Create(name);
if ((*thread)->attr != nullptr) {
scePthreadAttrDestroy(&(*thread)->attr);
@ -1061,11 +1059,11 @@ int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr
}
}
ScePthread PThreadPool::Create() {
ScePthread PThreadPool::Create(const char* name) {
std::scoped_lock lock{m_mutex};
for (auto* p : m_threads) {
if (p->is_free) {
if (p->is_free && p->name == name) {
p->is_free = false;
return p;
}
@ -1188,6 +1186,7 @@ int PS4_SYSV_ABI scePthreadCondattrDestroy(ScePthreadCondattr* attr) {
int result = pthread_condattr_destroy(&(*attr)->cond_attr);
LOG_DEBUG(Kernel_Pthread, "scePthreadCondattrDestroy: result = {} ", result);
delete *attr;
switch (result) {
case 0:
@ -1493,6 +1492,8 @@ int PS4_SYSV_ABI scePthreadOnce(int* once_control, void (*init_routine)(void)) {
}
[[noreturn]] void PS4_SYSV_ABI scePthreadExit(void* value_ptr) {
g_pthread_self->is_free = true;
pthread_exit(value_ptr);
UNREACHABLE();
}

View file

@ -119,7 +119,7 @@ struct PthreadSemInternal {
class PThreadPool {
public:
ScePthread Create();
ScePthread Create(const char* name);
private:
std::vector<ScePthread> m_threads;

View file

@ -3,6 +3,8 @@
#pragma once
#include <sys/types.h>
#include "common/types.h"
namespace Core::Loader {
@ -50,7 +52,10 @@ u64 PS4_SYSV_ABI sceKernelGetProcessTime();
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency();
u64 PS4_SYSV_ABI sceKernelReadTsc();
int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp);
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz);
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
OrbisKernelTimezone* timezone, int* dst_seconds);
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Kernel

View file

@ -6,6 +6,7 @@
#include "core/libraries/app_content/app_content.h"
#include "core/libraries/audio/audioin.h"
#include "core/libraries/audio/audioout.h"
#include "core/libraries/audio3d/audio3d.h"
#include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/dialogs/error_dialog.h"
#include "core/libraries/dialogs/ime_dialog.h"
@ -75,6 +76,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::ErrorDialog::RegisterlibSceErrorDialog(sym);
Libraries::ImeDialog::RegisterlibSceImeDialog(sym);
Libraries::AvPlayer::RegisterlibSceAvPlayer(sym);
Libraries::Audio3d::RegisterlibSceAudio3d(sym);
}
} // namespace Libraries

View file

@ -10,8 +10,12 @@ class SymbolsResolver;
}
// Define our own htonll and ntohll because its not available in some systems/platforms
#ifndef HTONLL
#define HTONLL(x) (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32))
#endif
#ifndef NTOHLL
#define NTOHLL(x) (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32))
#endif
namespace Libraries::Net {

View file

@ -4,13 +4,20 @@
#include <unordered_map>
#include "common/logging/log.h"
#include "common/path_util.h"
#include "common/slot_vector.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "externals/pugixml/src/pugixml.hpp"
#include "np_trophy.h"
#include "trophy_ui.h"
namespace Libraries::NpTrophy {
static TrophyUI g_trophy_ui;
std::string game_serial;
static constexpr auto MaxTrophyHandles = 4u;
static constexpr auto MaxTrophyContexts = 8u;
@ -24,11 +31,50 @@ struct ContextKeyHash {
struct TrophyContext {
u32 context_id;
};
static Common::SlotVector<u32> trophy_handles{};
static Common::SlotVector<OrbisNpTrophyHandle> trophy_handles{};
static Common::SlotVector<ContextKey> trophy_contexts{};
static std::unordered_map<ContextKey, TrophyContext, ContextKeyHash> contexts_internal{};
int PS4_SYSV_ABI sceNpTrophyAbortHandle() {
void ORBIS_NP_TROPHY_FLAG_ZERO(OrbisNpTrophyFlagArray* p) {
for (int i = 0; i < ORBIS_NP_TROPHY_NUM_MAX; i++) {
uint32_t array_index = i / 32;
uint32_t bit_position = i % 32;
p->flag_bits[array_index] &= ~(1U << bit_position);
}
}
void ORBIS_NP_TROPHY_FLAG_SET(int32_t trophyId, OrbisNpTrophyFlagArray* p) {
uint32_t array_index = trophyId / 32;
uint32_t bit_position = trophyId % 32;
p->flag_bits[array_index] |= (1U << bit_position);
}
void ORBIS_NP_TROPHY_FLAG_SET_ALL(OrbisNpTrophyFlagArray* p) {
for (int i = 0; i < ORBIS_NP_TROPHY_NUM_MAX; i++) {
uint32_t array_index = i / 32;
uint32_t bit_position = i % 32;
p->flag_bits[array_index] |= (1U << bit_position);
}
}
void ORBIS_NP_TROPHY_FLAG_CLR(int32_t trophyId, OrbisNpTrophyFlagArray* p) {
uint32_t array_index = trophyId / 32;
uint32_t bit_position = trophyId % 32;
p->flag_bits[array_index] &= ~(1U << bit_position);
}
bool ORBIS_NP_TROPHY_FLAG_ISSET(int32_t trophyId, OrbisNpTrophyFlagArray* p) {
uint32_t array_index = trophyId / 32;
uint32_t bit_position = trophyId % 32;
return (p->flag_bits[array_index] & (1U << bit_position)) ? 1 : 0;
}
int PS4_SYSV_ABI sceNpTrophyAbortHandle(OrbisNpTrophyHandle handle) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
@ -83,8 +129,8 @@ int PS4_SYSV_ABI sceNpTrophyConfigHasGroupFeature() {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpTrophyCreateContext(u32* context, u32 user_id, u32 service_label,
u64 options) {
s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, int32_t user_id,
uint32_t service_label, uint64_t options) {
ASSERT(options == 0ull);
if (!context) {
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
@ -107,7 +153,7 @@ s32 PS4_SYSV_ABI sceNpTrophyCreateContext(u32* context, u32 user_id, u32 service
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(u32* handle) {
s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(OrbisNpTrophyHandle* handle) {
if (!handle) {
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
}
@ -122,55 +168,120 @@ s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(u32* handle) {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyDestroyContext() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
int PS4_SYSV_ABI sceNpTrophyDestroyContext(OrbisNpTrophyContext context) {
LOG_INFO(Lib_NpTrophy, "Destroyed Context {}", context);
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
Common::SlotId contextId;
contextId.index = context;
ContextKey contextkey = trophy_contexts[contextId];
trophy_contexts.erase(contextId);
contexts_internal.erase(contextkey);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(u32 handle) {
if (!trophy_handles.is_allocated({handle})) {
s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(OrbisNpTrophyHandle handle) {
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
if (!trophy_handles.is_allocated({static_cast<u32>(handle)})) {
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
}
trophy_handles.erase({handle});
trophy_handles.erase({static_cast<u32>(handle)});
LOG_INFO(Lib_NpTrophy, "Handle {} destroyed", handle);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyGetGameIcon() {
int PS4_SYSV_ABI sceNpTrophyGetGameIcon(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
void* buffer, size_t* size) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyGetGameInfo() {
int PS4_SYSV_ABI sceNpTrophyGetGameInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGameDetails* details,
OrbisNpTrophyGameData* data) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyGetGroupIcon() {
int PS4_SYSV_ABI sceNpTrophyGetGroupIcon(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGroupId groupId, void* buffer, size_t* size) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyGetGroupInfo() {
int PS4_SYSV_ABI sceNpTrophyGetGroupInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGroupId groupId,
OrbisNpTrophyGroupDetails* details,
OrbisNpTrophyGroupData* data) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyGetTrophyIcon() {
int PS4_SYSV_ABI sceNpTrophyGetTrophyIcon(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyId trophyId, void* buffer, size_t* size) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyGetTrophyInfo() {
int PS4_SYSV_ABI sceNpTrophyGetTrophyInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyId trophyId, OrbisNpTrophyDetails* details,
OrbisNpTrophyData* data) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(u32 context, u32 handle, u32* flags, u32* count) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
*flags = 0u;
*count = 0;
s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context,
OrbisNpTrophyHandle handle,
OrbisNpTrophyFlagArray* flags, u32* count) {
LOG_INFO(Lib_NpTrophy, "GetTrophyUnlockState called");
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
if (flags == nullptr || count == nullptr)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
ORBIS_NP_TROPHY_FLAG_ZERO(flags);
const auto trophyDir =
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
pugi::xml_document doc;
pugi::xml_parse_result result =
doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
int numTrophies = 0;
if (result) {
auto trophyconf = doc.child("trophyconf");
for (pugi::xml_node_iterator it = trophyconf.children().begin();
it != trophyconf.children().end(); ++it) {
std::string currentTrophyId = it->attribute("id").value();
std::string currentTrophyUnlockState = it->attribute("unlockstate").value();
if (std::string(it->name()) == "trophy") {
numTrophies++;
}
if (currentTrophyUnlockState == "unlocked") {
ORBIS_NP_TROPHY_FLAG_SET(std::stoi(currentTrophyId), flags);
}
}
} else
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description());
*count = numTrophies;
return ORBIS_OK;
}
@ -239,8 +350,16 @@ int PS4_SYSV_ABI sceNpTrophyNumInfoGetTotal() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyRegisterContext() {
int PS4_SYSV_ABI sceNpTrophyRegisterContext(OrbisNpTrophyContext context,
OrbisNpTrophyHandle handle, uint64_t options) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
return ORBIS_OK;
}
@ -254,7 +373,8 @@ int PS4_SYSV_ABI sceNpTrophySetInfoGetTrophyNum() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyShowTrophyList() {
int PS4_SYSV_ABI sceNpTrophyShowTrophyList(OrbisNpTrophyContext context,
OrbisNpTrophyHandle handle) {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
return ORBIS_OK;
}
@ -474,8 +594,132 @@ int PS4_SYSV_ABI sceNpTrophySystemSetDbgParamInt() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpTrophyUnlockTrophy() {
LOG_ERROR(Lib_NpTrophy, "(STUBBED) called");
int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyId trophyId, OrbisNpTrophyId* platinumId) {
LOG_INFO(Lib_NpTrophy, "Unlocking trophy id {}", trophyId);
if (context == ORBIS_NP_TROPHY_INVALID_CONTEXT)
return ORBIS_NP_TROPHY_ERROR_INVALID_CONTEXT;
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
if (trophyId >= 127)
return ORBIS_NP_TROPHY_ERROR_INVALID_TROPHY_ID;
if (platinumId == nullptr)
return ORBIS_NP_TROPHY_ERROR_INVALID_ARGUMENT;
const auto trophyDir =
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles";
pugi::xml_document doc;
pugi::xml_parse_result result =
doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
*platinumId = ORBIS_NP_TROPHY_INVALID_TROPHY_ID;
int numTrophies = 0;
int numTrophiesUnlocked = 0;
pugi::xml_node_iterator platinumIt;
int platinumTrophyGroup = -1;
if (result) {
auto trophyconf = doc.child("trophyconf");
for (pugi::xml_node_iterator it = trophyconf.children().begin();
it != trophyconf.children().end(); ++it) {
std::string currentTrophyId = it->attribute("id").value();
std::string currentTrophyName = it->child("name").text().as_string();
std::string currentTrophyDescription = it->child("detail").text().as_string();
std::string currentTrophyType = it->attribute("ttype").value();
std::string currentTrophyUnlockState = it->attribute("unlockstate").value();
if (currentTrophyType == "P") {
platinumIt = it;
if (std::string(platinumIt->attribute("gid").value()).empty()) {
platinumTrophyGroup = -1;
} else {
platinumTrophyGroup =
std::stoi(std::string(platinumIt->attribute("gid").value()));
}
if (trophyId == std::stoi(currentTrophyId)) {
return ORBIS_NP_TROPHY_ERROR_PLATINUM_CANNOT_UNLOCK;
}
}
if (std::string(it->name()) == "trophy") {
if (platinumTrophyGroup == -1) {
if (std::string(it->attribute("gid").value()).empty()) {
numTrophies++;
if (currentTrophyUnlockState == "unlocked") {
numTrophiesUnlocked++;
}
}
} else {
if (!std::string(it->attribute("gid").value()).empty()) {
if (std::stoi(std::string(it->attribute("gid").value())) ==
platinumTrophyGroup) {
numTrophies++;
if (currentTrophyUnlockState == "unlocked") {
numTrophiesUnlocked++;
}
}
}
}
if (std::stoi(currentTrophyId) == trophyId) {
LOG_INFO(Lib_NpTrophy, "Found trophy to unlock {} : {}",
it->child("name").text().as_string(),
it->child("detail").text().as_string());
if (currentTrophyUnlockState == "unlocked") {
LOG_INFO(Lib_NpTrophy, "Trophy already unlocked");
return ORBIS_NP_TROPHY_ERROR_TROPHY_ALREADY_UNLOCKED;
} else {
if (std::string(it->attribute("unlockstate").value()).empty()) {
it->append_attribute("unlockstate") = "unlocked";
} else {
it->attribute("unlockstate").set_value("unlocked");
}
g_trophy_ui.AddTrophyToQueue(trophyId, currentTrophyName);
}
}
}
}
if (std::string(platinumIt->attribute("unlockstate").value()).empty()) {
if ((numTrophies - 2) == numTrophiesUnlocked) {
platinumIt->append_attribute("unlockstate") = "unlocked";
std::string platinumTrophyId = platinumIt->attribute("id").value();
std::string platinumTrophyName = platinumIt->child("name").text().as_string();
*platinumId = std::stoi(platinumTrophyId);
g_trophy_ui.AddTrophyToQueue(*platinumId, platinumTrophyName);
}
} else if (std::string(platinumIt->attribute("unlockstate").value()) == "locked") {
if ((numTrophies - 2) == numTrophiesUnlocked) {
platinumIt->attribute("unlockstate").set_value("unlocked");
std::string platinumTrophyId = platinumIt->attribute("id").value();
std::string platinumTrophyName = platinumIt->child("name").text().as_string();
*platinumId = std::stoi(platinumTrophyId);
g_trophy_ui.AddTrophyToQueue(*platinumId, platinumTrophyName);
}
}
doc.save_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str());
} else
LOG_INFO(Lib_NpTrophy, "couldnt parse xml : {}", result.description());
return ORBIS_OK;
}

View file

@ -4,6 +4,7 @@
#pragma once
#include "common/types.h"
#include "core/libraries/rtc/rtc.h"
namespace Core::Loader {
class SymbolsResolver;
@ -11,7 +12,116 @@ class SymbolsResolver;
namespace Libraries::NpTrophy {
int PS4_SYSV_ABI sceNpTrophyAbortHandle();
extern std::string game_serial;
constexpr int ORBIS_NP_TROPHY_FLAG_SETSIZE = 128;
constexpr int ORBIS_NP_TROPHY_FLAG_BITS_SHIFT = 5;
constexpr int ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE = 128;
constexpr int ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE = 1024;
constexpr int ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE = 128;
constexpr int ORBIS_NP_TROPHY_GROUP_DESCR_MAX_SIZE = 1024;
constexpr int ORBIS_NP_TROPHY_NAME_MAX_SIZE = 128;
constexpr int ORBIS_NP_TROPHY_DESCR_MAX_SIZE = 1024;
constexpr int ORBIS_NP_TROPHY_NUM_MAX = 128;
constexpr int ORBIS_NP_TROPHY_INVALID_HANDLE = -1;
constexpr int ORBIS_NP_TROPHY_INVALID_CONTEXT = -1;
constexpr int ORBIS_NP_TROPHY_INVALID_TROPHY_ID = -1;
typedef int32_t OrbisNpTrophyHandle;
typedef int32_t OrbisNpTrophyContext;
typedef int32_t OrbisNpTrophyId;
typedef uint32_t OrbisNpTrophyFlagMask;
struct OrbisNpTrophyFlagArray {
OrbisNpTrophyFlagMask
flag_bits[ORBIS_NP_TROPHY_FLAG_SETSIZE >> ORBIS_NP_TROPHY_FLAG_BITS_SHIFT];
};
void ORBIS_NP_TROPHY_FLAG_ZERO(OrbisNpTrophyFlagArray* p);
void ORBIS_NP_TROPHY_FLAG_SET(int32_t trophyId, OrbisNpTrophyFlagArray* p);
void ORBIS_NP_TROPHY_FLAG_SET_ALL(OrbisNpTrophyFlagArray* p);
void ORBIS_NP_TROPHY_FLAG_CLR(int32_t trophyId, OrbisNpTrophyFlagArray* p);
bool ORBIS_NP_TROPHY_FLAG_ISSET(int32_t trophyId, OrbisNpTrophyFlagArray* p);
struct OrbisNpTrophyData {
size_t size;
OrbisNpTrophyId trophyId;
bool unlocked;
uint8_t reserved[3];
Rtc::OrbisRtcTick timestamp;
};
typedef int32_t OrbisNpTrophyGrade;
constexpr int ORBIS_NP_TROPHY_GRADE_UNKNOWN = 0;
constexpr int ORBIS_NP_TROPHY_GRADE_PLATINUM = 1;
constexpr int ORBIS_NP_TROPHY_GRADE_GOLD = 2;
constexpr int ORBIS_NP_TROPHY_GRADE_SILVER = 3;
constexpr int ORBIS_NP_TROPHY_GRADE_BRONZE = 4;
typedef int32_t OrbisNpTrophyGroupId;
constexpr int ORBIS_NP_TROPHY_BASE_GAME_GROUP_ID = -1;
constexpr int ORBIS_NP_TROPHY_INVALID_GROUP_ID = -2;
struct OrbisNpTrophyDetails {
size_t size;
OrbisNpTrophyId trophyId;
OrbisNpTrophyGrade trophyGrade;
OrbisNpTrophyGroupId groupId;
bool hidden;
uint8_t reserved[3];
char name[ORBIS_NP_TROPHY_NAME_MAX_SIZE];
char description[ORBIS_NP_TROPHY_DESCR_MAX_SIZE];
};
struct OrbisNpTrophyGameData {
size_t size;
uint32_t unlockedTrophies;
uint32_t unlockedPlatinum;
uint32_t unlockedGold;
uint32_t unlockedSilver;
uint32_t unlockedBronze;
uint32_t progressPercentage;
};
struct OrbisNpTrophyGameDetails {
size_t size;
uint32_t numGroups;
uint32_t numTrophies;
uint32_t numPlatinum;
uint32_t numGold;
uint32_t numSilver;
uint32_t numBronze;
char title[ORBIS_NP_TROPHY_GAME_TITLE_MAX_SIZE];
char description[ORBIS_NP_TROPHY_GAME_DESCR_MAX_SIZE];
};
struct OrbisNpTrophyGroupData {
size_t size;
OrbisNpTrophyGroupId groupId;
uint32_t unlockedTrophies;
uint32_t unlockedPlatinum;
uint32_t unlockedGold;
uint32_t unlockedSilver;
uint32_t unlockedBronze;
uint32_t progressPercentage;
uint8_t reserved[4];
};
struct OrbisNpTrophyGroupDetails {
size_t size;
OrbisNpTrophyGroupId groupId;
uint32_t numTrophies;
uint32_t numPlatinum;
uint32_t numGold;
uint32_t numSilver;
uint32_t numBronze;
char title[ORBIS_NP_TROPHY_GROUP_TITLE_MAX_SIZE];
char description[ORBIS_NP_TROPHY_GROUP_DESCR_MAX_SIZE];
};
int PS4_SYSV_ABI sceNpTrophyAbortHandle(OrbisNpTrophyHandle handle);
int PS4_SYSV_ABI sceNpTrophyCaptureScreenshot();
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyDetails();
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyFlagArray();
@ -22,18 +132,30 @@ int PS4_SYSV_ABI sceNpTrophyConfigGetTrophySetInfoInGroup();
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophySetVersion();
int PS4_SYSV_ABI sceNpTrophyConfigGetTrophyTitleDetails();
int PS4_SYSV_ABI sceNpTrophyConfigHasGroupFeature();
s32 PS4_SYSV_ABI sceNpTrophyCreateContext(u32* context, u32 user_id, u32 service_label,
u64 options);
s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(u32* handle);
int PS4_SYSV_ABI sceNpTrophyDestroyContext();
s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(u32 handle);
int PS4_SYSV_ABI sceNpTrophyGetGameIcon();
int PS4_SYSV_ABI sceNpTrophyGetGameInfo();
int PS4_SYSV_ABI sceNpTrophyGetGroupIcon();
int PS4_SYSV_ABI sceNpTrophyGetGroupInfo();
int PS4_SYSV_ABI sceNpTrophyGetTrophyIcon();
int PS4_SYSV_ABI sceNpTrophyGetTrophyInfo();
s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(u32 context, u32 handle, u32* flags, u32* count);
s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context, int32_t user_id,
uint32_t service_label, uint64_t options);
s32 PS4_SYSV_ABI sceNpTrophyCreateHandle(OrbisNpTrophyHandle* handle);
int PS4_SYSV_ABI sceNpTrophyDestroyContext(OrbisNpTrophyContext context);
s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(OrbisNpTrophyHandle handle);
int PS4_SYSV_ABI sceNpTrophyGetGameIcon(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
void* buffer, size_t* size);
int PS4_SYSV_ABI sceNpTrophyGetGameInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGameDetails* details,
OrbisNpTrophyGameData* data);
int PS4_SYSV_ABI sceNpTrophyGetGroupIcon(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGroupId groupId, void* buffer, size_t* size);
int PS4_SYSV_ABI sceNpTrophyGetGroupInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyGroupId groupId,
OrbisNpTrophyGroupDetails* details,
OrbisNpTrophyGroupData* data);
int PS4_SYSV_ABI sceNpTrophyGetTrophyIcon(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyId trophyId, void* buffer, size_t* size);
int PS4_SYSV_ABI sceNpTrophyGetTrophyInfo(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyId trophyId, OrbisNpTrophyDetails* details,
OrbisNpTrophyData* data);
s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context,
OrbisNpTrophyHandle handle,
OrbisNpTrophyFlagArray* flags, u32* count);
int PS4_SYSV_ABI sceNpTrophyGroupArrayGetNum();
int PS4_SYSV_ABI sceNpTrophyIntAbortHandle();
int PS4_SYSV_ABI sceNpTrophyIntCheckNetSyncTitles();
@ -47,10 +169,12 @@ int PS4_SYSV_ABI sceNpTrophyIntGetTrpIconByUri();
int PS4_SYSV_ABI sceNpTrophyIntNetSyncTitle();
int PS4_SYSV_ABI sceNpTrophyIntNetSyncTitles();
int PS4_SYSV_ABI sceNpTrophyNumInfoGetTotal();
int PS4_SYSV_ABI sceNpTrophyRegisterContext();
int PS4_SYSV_ABI sceNpTrophyRegisterContext(OrbisNpTrophyContext context,
OrbisNpTrophyHandle handle, uint64_t options);
int PS4_SYSV_ABI sceNpTrophySetInfoGetTrophyFlagArray();
int PS4_SYSV_ABI sceNpTrophySetInfoGetTrophyNum();
int PS4_SYSV_ABI sceNpTrophyShowTrophyList();
int PS4_SYSV_ABI sceNpTrophyShowTrophyList(OrbisNpTrophyContext context,
OrbisNpTrophyHandle handle);
int PS4_SYSV_ABI sceNpTrophySystemAbortHandle();
int PS4_SYSV_ABI sceNpTrophySystemBuildGroupIconUri();
int PS4_SYSV_ABI sceNpTrophySystemBuildNetTrophyIconUri();
@ -94,7 +218,8 @@ int PS4_SYSV_ABI sceNpTrophySystemRemoveTitleData();
int PS4_SYSV_ABI sceNpTrophySystemRemoveUserData();
int PS4_SYSV_ABI sceNpTrophySystemSetDbgParam();
int PS4_SYSV_ABI sceNpTrophySystemSetDbgParamInt();
int PS4_SYSV_ABI sceNpTrophyUnlockTrophy();
int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTrophyHandle handle,
OrbisNpTrophyId trophyId, OrbisNpTrophyId* platinumId);
int PS4_SYSV_ABI Func_149656DA81D41C59();
int PS4_SYSV_ABI Func_9F80071876FFA5F6();
int PS4_SYSV_ABI Func_F8EF6F5350A91990();

View file

@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <imgui.h>
#include "common/assert.h"
#include "imgui/imgui_std.h"
#include "trophy_ui.h"
using namespace ImGui;
using namespace Libraries::NpTrophy;
TrophyUI::TrophyUI() {
AddLayer(this);
}
TrophyUI::~TrophyUI() {
Finish();
}
void Libraries::NpTrophy::TrophyUI::AddTrophyToQueue(int trophyId, std::string trophyName) {
TrophyInfo newInfo;
newInfo.trophyId = trophyId;
newInfo.trophyName = trophyName;
trophyQueue.push_back(newInfo);
}
void TrophyUI::Finish() {
RemoveLayer(this);
}
bool displayingTrophy;
std::chrono::steady_clock::time_point trophyStartedTime;
void TrophyUI::Draw() {
const auto& io = GetIO();
const ImVec2 window_size{
std::min(io.DisplaySize.x, 200.f),
std::min(io.DisplaySize.y, 75.f),
};
if (trophyQueue.size() != 0) {
if (!displayingTrophy) {
displayingTrophy = true;
trophyStartedTime = std::chrono::steady_clock::now();
}
std::chrono::steady_clock::time_point timeNow = std::chrono::steady_clock::now();
std::chrono::seconds duration =
std::chrono::duration_cast<std::chrono::seconds>(timeNow - trophyStartedTime);
if (duration.count() >= 5) {
trophyQueue.erase(trophyQueue.begin());
displayingTrophy = false;
}
if (trophyQueue.size() != 0) {
SetNextWindowSize(window_size);
SetNextWindowCollapsed(false);
SetNextWindowPos(ImVec2(io.DisplaySize.x - 200, 50));
KeepNavHighlight();
TrophyInfo currentTrophyInfo = trophyQueue[0];
if (Begin("Trophy Window", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoInputs)) {
Text("Trophy earned!");
TextWrapped(currentTrophyInfo.trophyName.c_str());
}
End();
}
}
}

View file

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <variant>
#include <vector>
#include "common/fixed_value.h"
#include "common/types.h"
#include "core/libraries/np_trophy/np_trophy.h"
#include "imgui/imgui_layer.h"
namespace Libraries::NpTrophy {
struct TrophyInfo {
int trophyId = -1;
std::string trophyName;
};
class TrophyUI final : public ImGui::Layer {
std::vector<TrophyInfo> trophyQueue;
public:
TrophyUI();
~TrophyUI() override;
void AddTrophyToQueue(int trophyId, std::string trophyName);
void Finish();
void Draw() override;
bool ShouldGrabGamepad() override {
return false;
}
};
}; // namespace Libraries::NpTrophy

View file

@ -250,7 +250,7 @@ int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenP
if (type != ORBIS_PAD_PORT_TYPE_SPECIAL)
return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED;
} else {
if (type != ORBIS_PAD_PORT_TYPE_STANDARD)
if (type != ORBIS_PAD_PORT_TYPE_STANDARD && type != ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL)
return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED;
}
return 1; // dummy
@ -263,7 +263,7 @@ int PS4_SYSV_ABI scePadOpenExt(s32 userId, s32 type, s32 index,
if (type != ORBIS_PAD_PORT_TYPE_SPECIAL)
return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED;
} else {
if (type != ORBIS_PAD_PORT_TYPE_STANDARD)
if (type != ORBIS_PAD_PORT_TYPE_STANDARD && type != ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL)
return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED;
}
return 1; // dummy

View file

@ -16,6 +16,7 @@ constexpr int ORBIS_PAD_MAX_DEVICE_UNIQUE_DATA_SIZE = 12;
constexpr int ORBIS_PAD_PORT_TYPE_STANDARD = 0;
constexpr int ORBIS_PAD_PORT_TYPE_SPECIAL = 2;
constexpr int ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL = 16;
enum OrbisPadDeviceClass {
ORBIS_PAD_DEVICE_CLASS_INVALID = -1,

File diff suppressed because it is too large Load diff

View file

@ -11,57 +11,81 @@ class SymbolsResolver;
namespace Libraries::Rtc {
constexpr int ORBIS_RTC_DAYOFWEEK_SUNDAY = 0;
constexpr int ORBIS_RTC_DAYOFWEEK_MONDAY = 1;
constexpr int ORBIS_RTC_DAYOFWEEK_TUESDAY = 2;
constexpr int ORBIS_RTC_DAYOFWEEK_WEDNESDAY = 3;
constexpr int ORBIS_RTC_DAYOFWEEK_THURSDAY = 4;
constexpr int ORBIS_RTC_DAYOFWEEK_FRIDAY = 5;
constexpr int ORBIS_RTC_DAYOFWEEK_SATURDAY = 6;
constexpr int64_t UNIX_EPOCH_TICKS = 0xdcbffeff2bc000;
constexpr int64_t WIN32_FILETIME_EPOCH_TICKS = 0xb36168b6a58000;
struct OrbisRtcTick {
u64 tick;
uint64_t tick;
};
int PS4_SYSV_ABI sceRtcCheckValid();
int PS4_SYSV_ABI sceRtcCompareTick();
int PS4_SYSV_ABI sceRtcConvertLocalTimeToUtc();
int PS4_SYSV_ABI sceRtcConvertUtcToLocalTime();
struct OrbisRtcDateTime {
uint16_t year;
uint16_t month;
uint16_t day;
uint16_t hour;
uint16_t minute;
uint16_t second;
uint32_t microsecond;
};
int PS4_SYSV_ABI sceRtcCheckValid(OrbisRtcDateTime* pTime);
int PS4_SYSV_ABI sceRtcCompareTick(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2);
int PS4_SYSV_ABI sceRtcConvertLocalTimeToUtc(OrbisRtcTick* pTickLocal, OrbisRtcTick* pTickUtc);
int PS4_SYSV_ABI sceRtcConvertUtcToLocalTime(OrbisRtcTick* pTickUtc, OrbisRtcTick* pTickLocal);
int PS4_SYSV_ABI sceRtcEnd();
int PS4_SYSV_ABI sceRtcFormatRFC2822();
int PS4_SYSV_ABI sceRtcFormatRFC2822LocalTime();
int PS4_SYSV_ABI sceRtcFormatRFC3339();
int PS4_SYSV_ABI sceRtcFormatRFC3339LocalTime();
int PS4_SYSV_ABI sceRtcFormatRFC3339Precise();
int PS4_SYSV_ABI sceRtcFormatRFC3339PreciseLocalTime();
int PS4_SYSV_ABI sceRtcGetCurrentAdNetworkTick();
int PS4_SYSV_ABI sceRtcGetCurrentClock();
int PS4_SYSV_ABI sceRtcGetCurrentClockLocalTime();
int PS4_SYSV_ABI sceRtcGetCurrentDebugNetworkTick();
int PS4_SYSV_ABI sceRtcGetCurrentNetworkTick();
int PS4_SYSV_ABI sceRtcGetCurrentRawNetworkTick();
int PS4_SYSV_ABI sceRtcFormatRFC2822(char* pszDateTime, const OrbisRtcTick* pTickUtc, int minutes);
int PS4_SYSV_ABI sceRtcFormatRFC2822LocalTime(char* pszDateTime, const OrbisRtcTick* pTickUtc);
int PS4_SYSV_ABI sceRtcFormatRFC3339(char* pszDateTime, const OrbisRtcTick* pTickUtc, int minutes);
int PS4_SYSV_ABI sceRtcFormatRFC3339LocalTime(char* pszDateTime, const OrbisRtcTick* pTickUtc);
int PS4_SYSV_ABI sceRtcFormatRFC3339Precise(char* pszDateTime, const OrbisRtcTick* pTickUtc,
int minutes);
int PS4_SYSV_ABI sceRtcFormatRFC3339PreciseLocalTime(char* pszDateTime,
const OrbisRtcTick* pTickUtc);
int PS4_SYSV_ABI sceRtcGetCurrentAdNetworkTick(OrbisRtcTick* pTick);
int PS4_SYSV_ABI sceRtcGetCurrentClock(OrbisRtcDateTime* pTime, int timeZone);
int PS4_SYSV_ABI sceRtcGetCurrentClockLocalTime(OrbisRtcDateTime* pTime);
int PS4_SYSV_ABI sceRtcGetCurrentDebugNetworkTick(OrbisRtcTick* pTick);
int PS4_SYSV_ABI sceRtcGetCurrentNetworkTick(OrbisRtcTick* pTick);
int PS4_SYSV_ABI sceRtcGetCurrentRawNetworkTick(OrbisRtcTick* pTick);
int PS4_SYSV_ABI sceRtcGetCurrentTick(OrbisRtcTick* pTick);
int PS4_SYSV_ABI sceRtcGetDayOfWeek();
int PS4_SYSV_ABI sceRtcGetDaysInMonth();
int PS4_SYSV_ABI sceRtcGetDosTime();
int PS4_SYSV_ABI sceRtcGetTick();
int PS4_SYSV_ABI sceRtcGetTickResolution();
int PS4_SYSV_ABI sceRtcGetTime_t();
int PS4_SYSV_ABI sceRtcGetWin32FileTime();
int PS4_SYSV_ABI sceRtcGetDayOfWeek(int year, int month, int day);
int PS4_SYSV_ABI sceRtcGetDaysInMonth(int year, int month);
int PS4_SYSV_ABI sceRtcGetDosTime(OrbisRtcDateTime* pTime, unsigned int* dosTime);
int PS4_SYSV_ABI sceRtcGetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick);
unsigned int PS4_SYSV_ABI sceRtcGetTickResolution();
int PS4_SYSV_ABI sceRtcGetTime_t(OrbisRtcDateTime* pTime, time_t* llTime);
int PS4_SYSV_ABI sceRtcGetWin32FileTime(OrbisRtcDateTime* pTime, uint64_t* ulWin32Time);
int PS4_SYSV_ABI sceRtcInit();
int PS4_SYSV_ABI sceRtcIsLeapYear();
int PS4_SYSV_ABI sceRtcParseDateTime();
int PS4_SYSV_ABI sceRtcParseRFC3339();
int PS4_SYSV_ABI sceRtcIsLeapYear(int yearInt);
int PS4_SYSV_ABI sceRtcParseDateTime(OrbisRtcTick* pTickUtc, const char* pszDateTime);
int PS4_SYSV_ABI sceRtcParseRFC3339(OrbisRtcTick* pTickUtc, const char* pszDateTime);
int PS4_SYSV_ABI sceRtcSetConf();
int PS4_SYSV_ABI sceRtcSetCurrentAdNetworkTick();
int PS4_SYSV_ABI sceRtcSetCurrentDebugNetworkTick();
int PS4_SYSV_ABI sceRtcSetCurrentNetworkTick();
int PS4_SYSV_ABI sceRtcSetCurrentTick();
int PS4_SYSV_ABI sceRtcSetDosTime();
int PS4_SYSV_ABI sceRtcSetTick();
int PS4_SYSV_ABI sceRtcSetTime_t();
int PS4_SYSV_ABI sceRtcSetWin32FileTime();
int PS4_SYSV_ABI sceRtcTickAddDays();
int PS4_SYSV_ABI sceRtcTickAddHours();
int PS4_SYSV_ABI sceRtcTickAddMicroseconds();
int PS4_SYSV_ABI sceRtcTickAddMinutes();
int PS4_SYSV_ABI sceRtcTickAddMonths();
int PS4_SYSV_ABI sceRtcTickAddSeconds();
int PS4_SYSV_ABI sceRtcTickAddTicks();
int PS4_SYSV_ABI sceRtcTickAddWeeks();
int PS4_SYSV_ABI sceRtcTickAddYears();
int PS4_SYSV_ABI sceRtcSetCurrentAdNetworkTick(OrbisRtcTick* pTick);
int PS4_SYSV_ABI sceRtcSetCurrentDebugNetworkTick(OrbisRtcTick* pTick);
int PS4_SYSV_ABI sceRtcSetCurrentNetworkTick(OrbisRtcTick* pTick);
int PS4_SYSV_ABI sceRtcSetCurrentTick(OrbisRtcTick* pTick);
int PS4_SYSV_ABI sceRtcSetDosTime(OrbisRtcDateTime* pTime, u32 dosTime);
int PS4_SYSV_ABI sceRtcSetTick(OrbisRtcDateTime* pTime, OrbisRtcTick* pTick);
int PS4_SYSV_ABI sceRtcSetTime_t(OrbisRtcDateTime* pTime, time_t llTime);
int PS4_SYSV_ABI sceRtcSetWin32FileTime(OrbisRtcDateTime* pTime, int64_t ulWin32Time);
int PS4_SYSV_ABI sceRtcTickAddDays(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd);
int PS4_SYSV_ABI sceRtcTickAddHours(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd);
int PS4_SYSV_ABI sceRtcTickAddMicroseconds(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2,
int64_t lAdd);
int PS4_SYSV_ABI sceRtcTickAddMinutes(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int64_t lAdd);
int PS4_SYSV_ABI sceRtcTickAddMonths(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd);
int PS4_SYSV_ABI sceRtcTickAddSeconds(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int64_t lAdd);
int PS4_SYSV_ABI sceRtcTickAddTicks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int64_t lAdd);
int PS4_SYSV_ABI sceRtcTickAddWeeks(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd);
int PS4_SYSV_ABI sceRtcTickAddYears(OrbisRtcTick* pTick1, OrbisRtcTick* pTick2, int32_t lAdd);
void RegisterlibSceRtc(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Rtc

View file

@ -3,6 +3,7 @@
#pragma once
constexpr int ORBIS_RTC_ERROR_DATETIME_UNINITIALIZED = 0x7FFEF9FE;
constexpr int ORBIS_RTC_ERROR_INVALID_PARAMETER = 0x80010602;
constexpr int ORBIS_RTC_ERROR_INVALID_TICK_PARAMETER = 0x80010603;
constexpr int ORBIS_RTC_ERROR_INVALID_DATE_PARAMETER = 0x80010604;
@ -14,4 +15,18 @@ constexpr int ORBIS_RTC_ERROR_INVALID_DAYS_PARAMETER = 0x80010623;
constexpr int ORBIS_RTC_ERROR_INVALID_HOURS_PARAMETER = 0x80010624;
constexpr int ORBIS_RTC_ERROR_INVALID_MINUTES_PARAMETER = 0x80010625;
constexpr int ORBIS_RTC_ERROR_INVALID_SECONDS_PARAMETER = 0x80010626;
constexpr int ORBIS_RTC_ERROR_INVALID_MILLISECONDS_PARAMETER = 0x80010627;
constexpr int ORBIS_RTC_ERROR_INVALID_MILLISECONDS_PARAMETER = 0x80010627;
constexpr int ORBIS_RTC_ERROR_NOT_INITIALIZED = 0x80B50001;
constexpr int ORBIS_RTC_ERROR_INVALID_POINTER = 0x80B50002;
constexpr int ORBIS_RTC_ERROR_INVALID_VALUE = 0x80B50003;
constexpr int ORBIS_RTC_ERROR_INVALID_ARG = 0x80B50004;
constexpr int ORBIS_RTC_ERROR_NOT_SUPPORTED = 0x80B50005;
constexpr int ORBIS_RTC_ERROR_NO_CLOCK = 0x80B50006;
constexpr int ORBIS_RTC_ERROR_BAD_PARSE = 0x80B50007;
constexpr int ORBIS_RTC_ERROR_INVALID_YEAR = 0x80B50008;
constexpr int ORBIS_RTC_ERROR_INVALID_MONTH = 0x80B50009;
constexpr int ORBIS_RTC_ERROR_INVALID_DAY = 0x80B5000A;
constexpr int ORBIS_RTC_ERROR_INVALID_HOUR = 0x80B5000B;
constexpr int ORBIS_RTC_ERROR_INVALID_MINUTE = 0x80B5000C;
constexpr int ORBIS_RTC_ERROR_INVALID_SECOND = 0x80B5000D;
constexpr int ORBIS_RTC_ERROR_INVALID_MICROSECOND = 0x80B5000E;

View file

@ -179,15 +179,21 @@ int PS4_SYSV_ABI sceSaveDataDeleteUser() {
int PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond* cond,
OrbisSaveDataDirNameSearchResult* result) {
if (cond == nullptr)
if (cond == nullptr || result == nullptr)
return ORBIS_SAVE_DATA_ERROR_PARAMETER;
LOG_INFO(Lib_SaveData, "called");
LOG_INFO(Lib_SaveData, "Number of directories = {}", result->dirNamesNum);
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
std::to_string(cond->userId) / game_serial;
if (!mount_dir.empty() && std::filesystem::exists(mount_dir)) {
if (cond->dirName == nullptr || std::string_view(cond->dirName->data)
.empty()) { // look for all dirs if no dir is provided.
for (int i = 0; const auto& entry : std::filesystem::directory_iterator(mount_dir)) {
int maxDirNum = result->dirNamesNum; // Games set a maximum of directories to search for
int i = 0;
if (cond->dirName == nullptr || std::string_view(cond->dirName->data).empty()) {
// Look for all dirs if no dir is provided.
for (const auto& entry : std::filesystem::directory_iterator(mount_dir)) {
if (i >= maxDirNum)
break;
if (std::filesystem::is_directory(entry.path()) &&
entry.path().filename().string() != "sdmemory") {
// sceSaveDataDirNameSearch does not search for dataMemory1/2 dirs.
@ -199,13 +205,50 @@ int PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond*
result->setNum = i;
}
}
} else { // Need a game to test.
LOG_ERROR(Lib_SaveData, "Check Me. sceSaveDataDirNameSearch: dirName = {}",
cond->dirName->data);
strncpy(result->dirNames[0].data, cond->dirName->data, 32);
result->hitNum = 1;
result->dirNamesNum = 1;
result->setNum = 1;
} else {
// Game checks for a specific directory.
LOG_INFO(Lib_SaveData, "dirName = {}", cond->dirName->data);
// Games can pass '%' as a wildcard
// e.g. `SAVELIST%` searches for all folders with names starting with `SAVELIST`
std::string baseName(cond->dirName->data);
u64 wildcardPos = baseName.find('%');
if (wildcardPos != std::string::npos) {
baseName = baseName.substr(0, wildcardPos);
}
for (const auto& entry : std::filesystem::directory_iterator(mount_dir)) {
if (i >= maxDirNum)
break;
if (std::filesystem::is_directory(entry.path())) {
std::string dirName = entry.path().filename().string();
if (wildcardPos != std::string::npos) {
if (dirName.compare(0, baseName.size(), baseName) != 0) {
continue;
}
} else if (wildcardPos == std::string::npos && dirName != cond->dirName->data) {
continue;
}
strncpy(result->dirNames[i].data, cond->dirName->data, 32);
i++;
result->hitNum = i;
result->dirNamesNum = i;
result->setNum = i;
}
}
}
if (result->params != nullptr) {
Common::FS::IOFile file(mount_dir / cond->dirName->data / "param.txt",
Common::FS::FileAccessMode::Read);
if (file.IsOpen()) {
file.ReadRaw<u8>((void*)result->params, sizeof(OrbisSaveDataParam));
file.Close();
}
}
} else {
result->hitNum = 0;

View file

@ -8,6 +8,9 @@
namespace Libraries::CommonDialog {
bool g_isInitialized = false;
bool g_isUsed = false;
int PS4_SYSV_ABI _ZN3sce16CommonDialogUtil12getSelfAppIdEv() {
LOG_ERROR(Lib_CommonDlg, "(STUBBED) called");
return ORBIS_OK;
@ -83,14 +86,19 @@ int PS4_SYSV_ABI _ZTVN3sce16CommonDialogUtil6ClientE() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceCommonDialogInitialize() {
LOG_ERROR(Lib_CommonDlg, "(DUMMY) called");
return ORBIS_OK;
Error PS4_SYSV_ABI sceCommonDialogInitialize() {
if (g_isInitialized) {
LOG_INFO(Lib_CommonDlg, "already initialized");
return Error::ALREADY_SYSTEM_INITIALIZED;
}
LOG_DEBUG(Lib_CommonDlg, "initialized");
g_isInitialized = true;
return Error::OK;
}
int PS4_SYSV_ABI sceCommonDialogIsUsed() {
LOG_ERROR(Lib_CommonDlg, "(STUBBED) called");
return ORBIS_OK;
bool PS4_SYSV_ABI sceCommonDialogIsUsed() {
LOG_TRACE(Lib_CommonDlg, "called");
return g_isUsed;
}
int PS4_SYSV_ABI Func_0FF577E4E8457883() {

View file

@ -11,9 +11,44 @@ class SymbolsResolver;
namespace Libraries::CommonDialog {
struct OrbisCommonDialogBaseParam {
enum class Status : u32 {
NONE = 0,
INITIALIZED = 1,
RUNNING = 2,
FINISHED = 3,
};
enum class Result : u32 {
OK = 0,
USER_CANCELED = 1,
};
enum class Error : u32 {
OK = 0,
NOT_SYSTEM_INITIALIZED = 0x80B80001,
ALREADY_SYSTEM_INITIALIZED = 0x80B80002,
NOT_INITIALIZED = 0x80B80003,
ALREADY_INITIALIZED = 0x80B80004,
NOT_FINISHED = 0x80B80005,
INVALID_STATE = 0x80B80006,
RESULT_NONE = 0x80B80007,
BUSY = 0x80B80008,
OUT_OF_MEMORY = 0x80B80009,
PARAM_INVALID = 0x80B8000A,
NOT_RUNNING = 0x80B8000B,
ALREADY_CLOSE = 0x80B8000C,
ARG_NULL = 0x80B8000D,
UNEXPECTED_FATAL = 0x80B8000E,
NOT_SUPPORTED = 0x80B8000F,
INHIBIT_SHAREPLAY_CLIENT = 0x80B80010,
};
extern bool g_isInitialized;
extern bool g_isUsed;
struct BaseParam {
std::size_t size;
u8 reserved[36];
std::array<u8, 36> reserved;
u32 magic;
};
@ -32,8 +67,8 @@ int PS4_SYSV_ABI _ZNK3sce16CommonDialogUtil6Client8getAppIdEv();
int PS4_SYSV_ABI _ZNK3sce16CommonDialogUtil6Client8isFinishEv();
int PS4_SYSV_ABI _ZNK3sce16CommonDialogUtil6Client9getResultEv();
int PS4_SYSV_ABI _ZTVN3sce16CommonDialogUtil6ClientE();
int PS4_SYSV_ABI sceCommonDialogInitialize();
int PS4_SYSV_ABI sceCommonDialogIsUsed();
Error PS4_SYSV_ABI sceCommonDialogInitialize();
bool PS4_SYSV_ABI sceCommonDialogIsUsed();
int PS4_SYSV_ABI Func_0FF577E4E8457883();
int PS4_SYSV_ABI Func_41716C2CE379416C();
int PS4_SYSV_ABI Func_483A427D8F6E0748();

View file

@ -1,79 +1,157 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <imgui.h>
#include <magic_enum.hpp>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/system/msgdialog.h"
#include <magic_enum.hpp>
#include "imgui_internal.h"
#include "msgdialog_ui.h"
namespace Libraries::MsgDialog {
int PS4_SYSV_ABI sceMsgDialogClose() {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
return ORBIS_OK;
}
using CommonDialog::Error;
using CommonDialog::Result;
using CommonDialog::Status;
int PS4_SYSV_ABI sceMsgDialogGetResult() {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
return ORBIS_OK;
}
static auto g_status = Status::NONE;
static MsgDialogState g_state{};
static DialogResult g_result{};
static MsgDialogUi g_msg_dialog_ui;
int PS4_SYSV_ABI sceMsgDialogGetStatus() {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceMsgDialogInitialize() {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceMsgDialogOpen(const OrbisMsgDialogParam* param) {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
switch (param->mode) {
case ORBIS_MSG_DIALOG_MODE_USER_MSG:
LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen userMsg type = %s msg = %s",
magic_enum::enum_name(param->userMsgParam->buttonType), param->userMsgParam->msg);
break;
case ORBIS_MSG_DIALOG_MODE_PROGRESS_BAR:
LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen progressBar type = %s msg = %s",
magic_enum::enum_name(param->progBarParam->barType), param->progBarParam->msg);
break;
case ORBIS_MSG_DIALOG_MODE_SYSTEM_MSG:
LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen systemMsg type: %s",
magic_enum::enum_name(param->sysMsgParam->sysMsgType));
break;
default:
break;
Error PS4_SYSV_ABI sceMsgDialogClose() {
LOG_DEBUG(Lib_MsgDlg, "called");
if (g_status != Status::RUNNING) {
return Error::NOT_RUNNING;
}
return ORBIS_OK;
g_msg_dialog_ui.Finish(ButtonId::INVALID);
return Error::OK;
}
int PS4_SYSV_ABI sceMsgDialogProgressBarInc() {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
return ORBIS_OK;
Error PS4_SYSV_ABI sceMsgDialogGetResult(DialogResult* result) {
LOG_DEBUG(Lib_MsgDlg, "called");
if (g_status != Status::FINISHED) {
return Error::NOT_FINISHED;
}
if (result == nullptr) {
return Error::ARG_NULL;
}
for (const auto v : result->reserved) {
if (v != 0) {
return Error::PARAM_INVALID;
}
}
*result = g_result;
return Error::OK;
}
int PS4_SYSV_ABI sceMsgDialogProgressBarSetMsg() {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
return ORBIS_OK;
Status PS4_SYSV_ABI sceMsgDialogGetStatus() {
LOG_TRACE(Lib_MsgDlg, "called status={}", magic_enum::enum_name(g_status));
return g_status;
}
int PS4_SYSV_ABI sceMsgDialogProgressBarSetValue() {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
return ORBIS_OK;
Error PS4_SYSV_ABI sceMsgDialogInitialize() {
LOG_DEBUG(Lib_MsgDlg, "called");
if (!CommonDialog::g_isInitialized) {
return Error::NOT_SYSTEM_INITIALIZED;
}
if (g_status != Status::NONE) {
return Error::ALREADY_INITIALIZED;
}
if (CommonDialog::g_isUsed) {
return Error::BUSY;
}
g_status = Status::INITIALIZED;
CommonDialog::g_isUsed = true;
return Error::OK;
}
int PS4_SYSV_ABI sceMsgDialogTerminate() {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
return ORBIS_OK;
Error PS4_SYSV_ABI sceMsgDialogOpen(const OrbisParam* param) {
if (g_status != Status::INITIALIZED && g_status != Status::FINISHED) {
LOG_INFO(Lib_MsgDlg, "called without initialize");
return Error::INVALID_STATE;
}
if (param == nullptr) {
LOG_DEBUG(Lib_MsgDlg, "called param:(NULL)");
return Error::ARG_NULL;
}
LOG_DEBUG(Lib_MsgDlg, "called param->mode: {}", magic_enum::enum_name(param->mode));
ASSERT(param->size == sizeof(OrbisParam));
ASSERT(param->baseParam.size == sizeof(CommonDialog::BaseParam));
g_result = {};
g_state = MsgDialogState{*param};
g_status = Status::RUNNING;
g_msg_dialog_ui = MsgDialogUi(&g_state, &g_status, &g_result);
return Error::OK;
}
int PS4_SYSV_ABI sceMsgDialogUpdateStatus() {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
return ORBIS_OK;
Error PS4_SYSV_ABI sceMsgDialogProgressBarInc(OrbisMsgDialogProgressBarTarget target, u32 delta) {
LOG_DEBUG(Lib_MsgDlg, "called");
if (g_status != Status::RUNNING) {
return Error::NOT_RUNNING;
}
if (g_state.GetMode() != MsgDialogMode::PROGRESS_BAR) {
return Error::NOT_SUPPORTED;
}
if (target != OrbisMsgDialogProgressBarTarget::DEFAULT) {
return Error::PARAM_INVALID;
}
g_state.GetState<MsgDialogState::ProgressState>().progress += delta;
return Error::OK;
}
Error PS4_SYSV_ABI sceMsgDialogProgressBarSetMsg(OrbisMsgDialogProgressBarTarget target,
const char* msg) {
LOG_DEBUG(Lib_MsgDlg, "called");
if (g_status != Status::RUNNING) {
return Error::NOT_RUNNING;
}
if (g_state.GetMode() != MsgDialogMode::PROGRESS_BAR) {
return Error::NOT_SUPPORTED;
}
if (target != OrbisMsgDialogProgressBarTarget::DEFAULT) {
return Error::PARAM_INVALID;
}
g_state.GetState<MsgDialogState::ProgressState>().msg = msg;
return Error::OK;
}
Error PS4_SYSV_ABI sceMsgDialogProgressBarSetValue(OrbisMsgDialogProgressBarTarget target,
u32 value) {
LOG_DEBUG(Lib_MsgDlg, "called");
if (g_status != Status::RUNNING) {
return Error::NOT_RUNNING;
}
if (g_state.GetMode() != MsgDialogMode::PROGRESS_BAR) {
return Error::NOT_SUPPORTED;
}
if (target != OrbisMsgDialogProgressBarTarget::DEFAULT) {
return Error::PARAM_INVALID;
}
g_state.GetState<MsgDialogState::ProgressState>().progress = value;
return Error::OK;
}
Error PS4_SYSV_ABI sceMsgDialogTerminate() {
LOG_DEBUG(Lib_MsgDlg, "called");
if (g_status == Status::RUNNING) {
sceMsgDialogClose();
}
if (g_status == Status::NONE) {
return Error::NOT_INITIALIZED;
}
g_status = Status::NONE;
CommonDialog::g_isUsed = false;
return Error::OK;
}
Status PS4_SYSV_ABI sceMsgDialogUpdateStatus() {
LOG_TRACE(Lib_MsgDlg, "called status={}", magic_enum::enum_name(g_status));
return g_status;
}
void RegisterlibSceMsgDialog(Core::Loader::SymbolsResolver* sym) {

View file

@ -3,7 +3,6 @@
#pragma once
#include "common/types.h"
#include "core/libraries/system/commondialog.h"
namespace Core::Loader {
@ -12,95 +11,23 @@ class SymbolsResolver;
namespace Libraries::MsgDialog {
using OrbisUserServiceUserId = s32;
struct DialogResult;
struct OrbisParam;
enum class OrbisMsgDialogProgressBarTarget : u32;
enum OrbisCommonDialogStatus {
ORBIS_COMMON_DIALOG_STATUS_NONE = 0,
ORBIS_COMMON_DIALOG_STATUS_INITIALIZED = 1,
ORBIS_COMMON_DIALOG_STATUS_RUNNING = 2,
ORBIS_COMMON_DIALOG_STATUS_FINISHED = 3
};
enum OrbisMsgDialogMode {
ORBIS_MSG_DIALOG_MODE_USER_MSG = 1,
ORBIS_MSG_DIALOG_MODE_PROGRESS_BAR = 2,
ORBIS_MSG_DIALOG_MODE_SYSTEM_MSG = 3,
};
enum OrbisMsgDialogButtonType {
ORBIS_MSG_DIALOG_BUTTON_TYPE_OK = 0,
ORBIS_MSG_DIALOG_BUTTON_TYPE_YESNO = 1,
ORBIS_MSG_DIALOG_BUTTON_TYPE_NONE = 2,
ORBIS_MSG_DIALOG_BUTTON_TYPE_OK_CANCEL = 3,
ORBIS_MSG_DIALOG_BUTTON_TYPE_WAIT = 5,
ORBIS_MSG_DIALOG_BUTTON_TYPE_WAIT_CANCEL = 6,
ORBIS_MSG_DIALOG_BUTTON_TYPE_YESNO_FOCUS_NO = 7,
ORBIS_MSG_DIALOG_BUTTON_TYPE_OK_CANCEL_FOCUS_CANCEL = 8,
ORBIS_MSG_DIALOG_BUTTON_TYPE_2BUTTONS = 9,
};
enum OrbisMsgDialogProgressBarType {
ORBIS_MSG_DIALOG_PROGRESSBAR_TYPE_PERCENTAGE = 0,
ORBIS_MSG_DIALOG_PROGRESSBAR_TYPE_PERCENTAGE_CANCEL = 1,
};
enum OrbisMsgDialogSystemMessageType {
ORBIS_MSG_DIALOG_SYSMSG_TYPE_TRC_EMPTY_STORE = 0,
ORBIS_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION = 1,
ORBIS_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_UGC_RESTRICTION = 2,
ORBIS_MSG_DIALOG_SYSMSG_TYPE_CAMERA_NOT_CONNECTED = 4,
ORBIS_MSG_DIALOG_SYSMSG_TYPE_WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED = 5,
};
struct OrbisMsgDialogButtonsParam {
const char* msg1;
const char* msg2;
char reserved[32];
};
struct OrbisMsgDialogUserMessageParam {
OrbisMsgDialogButtonType buttonType;
s32 : 32;
const char* msg;
OrbisMsgDialogButtonsParam* buttonsParam;
char reserved[24];
};
struct OrbisMsgDialogProgressBarParam {
OrbisMsgDialogProgressBarType barType;
int32_t : 32;
const char* msg;
char reserved[64];
};
struct OrbisMsgDialogSystemMessageParam {
OrbisMsgDialogSystemMessageType sysMsgType;
char reserved[32];
};
struct OrbisMsgDialogParam {
CommonDialog::OrbisCommonDialogBaseParam baseParam;
std::size_t size;
OrbisMsgDialogMode mode;
s32 : 32;
OrbisMsgDialogUserMessageParam* userMsgParam;
OrbisMsgDialogProgressBarParam* progBarParam;
OrbisMsgDialogSystemMessageParam* sysMsgParam;
OrbisUserServiceUserId userId;
char reserved[40];
s32 : 32;
};
int PS4_SYSV_ABI sceMsgDialogClose();
int PS4_SYSV_ABI sceMsgDialogGetResult();
int PS4_SYSV_ABI sceMsgDialogGetStatus();
int PS4_SYSV_ABI sceMsgDialogInitialize();
s32 PS4_SYSV_ABI sceMsgDialogOpen(const OrbisMsgDialogParam* param);
int PS4_SYSV_ABI sceMsgDialogProgressBarInc();
int PS4_SYSV_ABI sceMsgDialogProgressBarSetMsg();
int PS4_SYSV_ABI sceMsgDialogProgressBarSetValue();
int PS4_SYSV_ABI sceMsgDialogTerminate();
int PS4_SYSV_ABI sceMsgDialogUpdateStatus();
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogClose();
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogGetResult(DialogResult* result);
CommonDialog::Status PS4_SYSV_ABI sceMsgDialogGetStatus();
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogInitialize();
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogOpen(const OrbisParam* param);
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogProgressBarInc(OrbisMsgDialogProgressBarTarget,
u32 delta);
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogProgressBarSetMsg(OrbisMsgDialogProgressBarTarget,
const char* msg);
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogProgressBarSetValue(OrbisMsgDialogProgressBarTarget,
u32 value);
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogTerminate();
CommonDialog::Status PS4_SYSV_ABI sceMsgDialogUpdateStatus();
void RegisterlibSceMsgDialog(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::MsgDialog

View file

@ -0,0 +1,272 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <imgui.h>
#include "common/assert.h"
#include "imgui/imgui_std.h"
#include "msgdialog_ui.h"
using namespace ImGui;
using namespace Libraries::CommonDialog;
using namespace Libraries::MsgDialog;
static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
static constexpr float PROGRESS_BAR_WIDTH{0.8f};
struct {
int count = 0;
const char* text1;
const char* text2;
} static constexpr user_button_texts[] = {
{1, "OK"}, // 0 OK
{2, "Yes", "No"}, // 1 YESNO
{0}, // 2 NONE
{2, "OK", "Cancel"}, // 3 OK_CANCEL
{}, // 4 !!NOP
{1, "Wait"}, // 5 WAIT
{2, "Wait", "Cancel"}, // 6 WAIT_CANCEL
{2, "Yes", "No"}, // 7 YESNO_FOCUS_NO
{2, "OK", "Cancel"}, // 8 OK_CANCEL_FOCUS_CANCEL
{0xFF}, // 9 TWO_BUTTONS
};
static_assert(std::size(user_button_texts) == static_cast<int>(ButtonType::TWO_BUTTONS) + 1);
static void DrawCenteredText(const char* text) {
const auto ws = GetWindowSize();
const auto text_size = CalcTextSize(text, nullptr, false, ws.x - 40.0f);
PushTextWrapPos(ws.x - 30.0f);
SetCursorPos({
(ws.x - text_size.x) / 2.0f,
(ws.y - text_size.y) / 2.0f - 50.0f,
});
Text("%s", text);
PopTextWrapPos();
}
MsgDialogState::MsgDialogState(const OrbisParam& param) {
this->mode = param.mode;
switch (mode) {
case MsgDialogMode::USER_MSG: {
ASSERT(param.userMsgParam);
const auto& v = *param.userMsgParam;
auto state = UserState{
.type = v.buttonType,
.msg = std::string(v.msg),
};
if (v.buttonType == ButtonType::TWO_BUTTONS) {
ASSERT(v.buttonsParam);
state.btn_param1 = std::string(v.buttonsParam->msg1);
state.btn_param2 = std::string(v.buttonsParam->msg2);
}
this->state = state;
} break;
case MsgDialogMode::PROGRESS_BAR: {
ASSERT(param.progBarParam);
const auto& v = *param.progBarParam;
this->state = ProgressState{
.type = v.barType,
.msg = std::string(v.msg),
.progress = 0,
};
} break;
case MsgDialogMode::SYSTEM_MSG: {
ASSERT(param.sysMsgParam);
const auto& v = *param.sysMsgParam;
this->state = SystemState{
.type = v.sysMsgType,
};
} break;
default:
UNREACHABLE_MSG("Unknown dialog mode");
}
}
void MsgDialogUi::DrawUser() {
const auto& [button_type, msg, btn_param1, btn_param2] =
state->GetState<MsgDialogState::UserState>();
const auto ws = GetWindowSize();
DrawCenteredText(msg.c_str());
ASSERT(button_type <= ButtonType::TWO_BUTTONS);
auto [count, text1, text2] = user_button_texts[static_cast<u32>(button_type)];
if (count == 0xFF) { // TWO_BUTTONS -> User defined message
count = 2;
text1 = btn_param1.c_str();
text2 = btn_param2.c_str();
}
const bool focus_first = button_type < ButtonType::YESNO_FOCUS_NO;
SetCursorPos({
ws.x / 2.0f - BUTTON_SIZE.x / 2.0f * static_cast<float>(count),
ws.y - 10.0f - BUTTON_SIZE.y,
});
BeginGroup();
if (count > 0) {
// First button at the right, so we render the second button first
if (count == 2) {
PushID(2);
if (Button(text2, BUTTON_SIZE)) {
switch (button_type) {
case ButtonType::OK_CANCEL:
case ButtonType::WAIT_CANCEL:
case ButtonType::OK_CANCEL_FOCUS_CANCEL:
Finish(ButtonId::INVALID, Result::USER_CANCELED);
break;
default:
Finish(ButtonId::BUTTON2);
break;
}
}
if (first_render && !focus_first) {
SetItemCurrentNavFocus();
}
PopID();
SameLine();
}
PushID(1);
if (Button(text1, BUTTON_SIZE)) {
Finish(ButtonId::BUTTON1);
}
if (first_render && focus_first) {
SetItemCurrentNavFocus();
}
PopID();
SameLine();
}
EndGroup();
}
void MsgDialogUi::DrawProgressBar() {
const auto& [bar_type, msg, progress_bar_value] =
state->GetState<MsgDialogState::ProgressState>();
DrawCenteredText(msg.c_str());
const auto ws = GetWindowSize();
SetCursorPos({
ws.x * ((1 - PROGRESS_BAR_WIDTH) / 2.0f),
ws.y - 10.0f - BUTTON_SIZE.y,
});
const bool has_cancel = bar_type == ProgressBarType::PERCENTAGE_CANCEL;
float bar_width = PROGRESS_BAR_WIDTH * ws.x;
if (has_cancel) {
bar_width -= BUTTON_SIZE.x - 10.0f;
}
BeginGroup();
ProgressBar(static_cast<float>(progress_bar_value) / 100.0f, {bar_width, BUTTON_SIZE.y});
if (has_cancel) {
SameLine();
if (Button("Cancel", BUTTON_SIZE)) {
Finish(ButtonId::INVALID, Result::USER_CANCELED);
}
if (first_render) {
SetItemCurrentNavFocus();
}
}
EndGroup();
}
struct {
const char* text;
} static constexpr system_message_texts[] = {
"No product available in the store.", // TRC_EMPTY_STORE
"PSN chat restriction.", // TRC_PSN_CHAT_RESTRICTION
"User-generated Media restriction", // TRC_PSN_UGC_RESTRICTION
nullptr, // !!NOP
"Camera not connected.", // CAMERA_NOT_CONNECTED
"Warning: profile picture and name are not set", // WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED
};
static_assert(std::size(system_message_texts) ==
static_cast<int>(SystemMessageType::WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED) + 1);
void MsgDialogUi::DrawSystemMessage() {
// TODO: Implement go to settings & user profile
const auto& [msg_type] = state->GetState<MsgDialogState::SystemState>();
ASSERT(msg_type <= SystemMessageType::WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED);
auto [msg] = system_message_texts[static_cast<u32>(msg_type)];
DrawCenteredText(msg);
const auto ws = GetWindowSize();
SetCursorPos({
ws.x / 2.0f - BUTTON_SIZE.x / 2.0f,
ws.y - 10.0f - BUTTON_SIZE.y,
});
if (Button("OK", BUTTON_SIZE)) {
Finish(ButtonId::OK);
}
if (first_render) {
SetItemCurrentNavFocus();
}
}
MsgDialogUi::MsgDialogUi(MsgDialogState* state, Status* status, DialogResult* result)
: state(state), status(status), result(result) {
if (status && *status == Status::RUNNING) {
first_render = true;
AddLayer(this);
}
}
MsgDialogUi::~MsgDialogUi() {
Finish(ButtonId::INVALID);
}
MsgDialogUi::MsgDialogUi(MsgDialogUi&& other) noexcept
: Layer(other), state(other.state), status(other.status), result(other.result) {
other.state = nullptr;
other.status = nullptr;
other.result = nullptr;
}
MsgDialogUi& MsgDialogUi::operator=(MsgDialogUi other) {
using std::swap;
swap(state, other.state);
swap(status, other.status);
swap(result, other.result);
if (status && *status == Status::RUNNING) {
first_render = true;
AddLayer(this);
}
return *this;
}
void MsgDialogUi::Finish(ButtonId buttonId, Result r) {
if (result) {
result->result = r;
result->buttonId = buttonId;
}
if (status) {
*status = Status::FINISHED;
}
state = nullptr;
status = nullptr;
result = nullptr;
RemoveLayer(this);
}
void MsgDialogUi::Draw() {
if (status == nullptr || *status != Status::RUNNING) {
return;
}
const auto& io = GetIO();
const ImVec2 window_size{
std::min(io.DisplaySize.x, 500.0f),
std::min(io.DisplaySize.y, 300.0f),
};
CentralizeWindow();
SetNextWindowSize(window_size);
SetNextWindowFocus();
SetNextWindowCollapsed(false);
KeepNavHighlight();
// Hack to allow every dialog to have a unique window
if (Begin("Message Dialog##MessageDialog", nullptr, ImGuiWindowFlags_NoSavedSettings)) {
switch (state->GetMode()) {
case MsgDialogMode::USER_MSG:
DrawUser();
break;
case MsgDialogMode::PROGRESS_BAR:
DrawProgressBar();
break;
case MsgDialogMode::SYSTEM_MSG:
DrawSystemMessage();
break;
}
}
End();
first_render = false;
}

View file

@ -0,0 +1,177 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <variant>
#include "common/fixed_value.h"
#include "common/types.h"
#include "core/libraries/system/commondialog.h"
#include "imgui/imgui_layer.h"
namespace Libraries::MsgDialog {
using OrbisUserServiceUserId = s32;
enum class MsgDialogMode : u32 {
USER_MSG = 1,
PROGRESS_BAR = 2,
SYSTEM_MSG = 3,
};
enum class ButtonId : u32 {
INVALID = 0,
OK = 1,
YES = 1,
NO = 2,
BUTTON1 = 1,
BUTTON2 = 2,
};
enum class ButtonType : u32 {
OK = 0,
YESNO = 1,
NONE = 2,
OK_CANCEL = 3,
WAIT = 5,
WAIT_CANCEL = 6,
YESNO_FOCUS_NO = 7,
OK_CANCEL_FOCUS_CANCEL = 8,
TWO_BUTTONS = 9,
};
enum class ProgressBarType : u32 {
PERCENTAGE = 0,
PERCENTAGE_CANCEL = 1,
};
enum class SystemMessageType : u32 {
TRC_EMPTY_STORE = 0,
TRC_PSN_CHAT_RESTRICTION = 1,
TRC_PSN_UGC_RESTRICTION = 2,
CAMERA_NOT_CONNECTED = 4,
WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED = 5,
};
enum class OrbisMsgDialogProgressBarTarget : u32 {
DEFAULT = 0,
};
struct ButtonsParam {
const char* msg1{};
const char* msg2{};
std::array<char, 32> reserved{};
};
struct UserMessageParam {
ButtonType buttonType{};
s32 : 32;
const char* msg{};
ButtonsParam* buttonsParam{};
std::array<char, 24> reserved{};
};
struct ProgressBarParam {
ProgressBarType barType{};
s32 : 32;
const char* msg{};
std::array<char, 64> reserved{};
};
struct SystemMessageParam {
SystemMessageType sysMsgType{};
std::array<char, 32> reserved{};
};
struct OrbisParam {
CommonDialog::BaseParam baseParam;
std::size_t size;
MsgDialogMode mode;
s32 : 32;
UserMessageParam* userMsgParam;
ProgressBarParam* progBarParam;
SystemMessageParam* sysMsgParam;
OrbisUserServiceUserId userId;
std::array<char, 40> reserved;
s32 : 32;
};
struct DialogResult {
FixedValue<u32, 0> mode{};
CommonDialog::Result result{CommonDialog::Result::OK};
ButtonId buttonId{ButtonId::INVALID};
std::array<char, 32> reserved{};
};
// State is used to copy all the data from the param struct
class MsgDialogState {
public:
struct UserState {
ButtonType type{};
std::string msg{};
std::string btn_param1{};
std::string btn_param2{};
};
struct ProgressState {
ProgressBarType type{};
std::string msg{};
u32 progress{};
};
struct SystemState {
SystemMessageType type{};
};
private:
OrbisUserServiceUserId user_id{};
MsgDialogMode mode{};
std::variant<UserState, ProgressState, SystemState, std::monostate> state{std::monostate{}};
public:
explicit MsgDialogState(const OrbisParam& param);
MsgDialogState() = default;
[[nodiscard]] OrbisUserServiceUserId GetUserId() const {
return user_id;
}
[[nodiscard]] MsgDialogMode GetMode() const {
return mode;
}
template <typename T>
[[nodiscard]] T& GetState() {
return std::get<T>(state);
}
};
class MsgDialogUi final : public ImGui::Layer {
bool first_render{false};
MsgDialogState* state{};
CommonDialog::Status* status{};
DialogResult* result{};
void DrawUser();
void DrawProgressBar();
void DrawSystemMessage();
public:
explicit MsgDialogUi(MsgDialogState* state = nullptr, CommonDialog::Status* status = nullptr,
DialogResult* result = nullptr);
~MsgDialogUi() override;
MsgDialogUi(const MsgDialogUi& other) = delete;
MsgDialogUi(MsgDialogUi&& other) noexcept;
MsgDialogUi& operator=(MsgDialogUi other);
void Finish(ButtonId buttonId, CommonDialog::Result r = CommonDialog::Result::OK);
void SetProgressBarValue(u32 value, bool increment);
void Draw() override;
bool ShouldGrabGamepad() override {
return true;
}
};
}; // namespace Libraries::MsgDialog

View file

@ -161,10 +161,6 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) {
}
std::chrono::microseconds VideoOutDriver::Flip(const Request& req) {
if (!req) {
return std::chrono::microseconds{0};
}
const auto start = std::chrono::high_resolution_clock::now();
// Whatever the game is rendering show splash if it is active
@ -207,6 +203,11 @@ std::chrono::microseconds VideoOutDriver::Flip(const Request& req) {
return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
}
void VideoOutDriver::DrawBlankFrame() {
const auto empty_frame = renderer->PrepareBlankFrame(false);
renderer->Present(empty_frame);
}
bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
bool is_eop /*= false*/) {
{
@ -283,7 +284,14 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
auto& vblank_status = main_port.vblank_status;
if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
const auto request = receive_request();
delay = Flip(request);
if (!request) {
delay = std::chrono::microseconds{0};
if (!main_port.is_open) {
DrawBlankFrame();
}
} else {
delay = Flip(request);
}
FRAME_END;
}

View file

@ -102,6 +102,7 @@ private:
};
std::chrono::microseconds Flip(const Request& req);
void DrawBlankFrame(); // Used when there is no flip request to keep ImGui up to date
void SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false);
void PresentThread(std::stop_token token);

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/arch.h"
#include "common/assert.h"
#include "common/config.h"
#include "common/logging/log.h"
@ -27,6 +28,7 @@ static PS4_SYSV_ABI void ProgramExitFunc() {
}
static void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc exit_func) {
#ifdef ARCH_X86_64
// reinterpret_cast<entry_func_t>(addr)(params, exit_func); // can't be used, stack has to have
// a specific layout
asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes
@ -46,6 +48,9 @@ static void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc exit_func) {
:
: "r"(addr), "r"(params), "r"(exit_func)
: "rax", "rsi", "rdi");
#else
UNIMPLEMENTED_MSG("Missing RunMainEntry() implementation for target CPU architecture.");
#endif
}
Linker::Linker() : memory{Memory::Instance()} {}
@ -85,7 +90,9 @@ void Linker::Execute() {
// Init primary thread.
Common::SetCurrentThreadName("GAME_MainThread");
#ifdef ARCH_X86_64
InitializeThreadPatchStack();
#endif
Libraries::Kernel::pthreadInitSelfMainThread();
InitTlsForThread(true);

View file

@ -7,6 +7,7 @@
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/memory_management.h"
#include "core/memory.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
namespace Core {
@ -292,6 +293,118 @@ int MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* pr
return ORBIS_OK;
}
int MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex};
// Find the virtual memory area that contains the specified address range.
auto it = FindVMA(addr);
if (it == vma_map.end() || !it->second.Contains(addr, size)) {
LOG_ERROR(Core, "Address range not mapped");
return ORBIS_KERNEL_ERROR_EINVAL;
}
VirtualMemoryArea& vma = it->second;
if (vma.type == VMAType::Free) {
LOG_ERROR(Core, "Cannot change protection on free memory region");
return ORBIS_KERNEL_ERROR_EINVAL;
}
// Validate protection flags
constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
MemoryProt invalid_flags = prot & ~valid_flags;
if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) {
LOG_ERROR(Core, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}", u32(prot),
u32(invalid_flags));
return ORBIS_KERNEL_ERROR_EINVAL;
}
// Change protection
vma.prot = prot;
// Set permissions
Core::MemoryPermission perms{};
if (True(prot & MemoryProt::CpuRead)) {
perms |= Core::MemoryPermission::Read;
}
if (True(prot & MemoryProt::CpuReadWrite)) {
perms |= Core::MemoryPermission::ReadWrite;
}
if (True(prot & MemoryProt::GpuRead)) {
perms |= Core::MemoryPermission::Read;
}
if (True(prot & MemoryProt::GpuWrite)) {
perms |= Core::MemoryPermission::Write;
}
if (True(prot & MemoryProt::GpuReadWrite)) {
perms |= Core::MemoryPermission::ReadWrite;
}
impl.Protect(addr, size, perms);
return ORBIS_OK;
}
int MemoryManager::MTypeProtect(VAddr addr, size_t size, VMAType mtype, MemoryProt prot) {
std::scoped_lock lk{mutex};
// Find the virtual memory area that contains the specified address range.
auto it = FindVMA(addr);
if (it == vma_map.end() || !it->second.Contains(addr, size)) {
LOG_ERROR(Core, "Address range not mapped");
return ORBIS_KERNEL_ERROR_EINVAL;
}
VirtualMemoryArea& vma = it->second;
if (vma.type == VMAType::Free) {
LOG_ERROR(Core, "Cannot change protection on free memory region");
return ORBIS_KERNEL_ERROR_EINVAL;
}
// Validate protection flags
constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
MemoryProt invalid_flags = prot & ~valid_flags;
if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) {
LOG_ERROR(Core, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}", u32(prot),
u32(invalid_flags));
return ORBIS_KERNEL_ERROR_EINVAL;
}
// Change type and protection
vma.type = mtype;
vma.prot = prot;
// Set permissions
Core::MemoryPermission perms{};
if (True(prot & MemoryProt::CpuRead)) {
perms |= Core::MemoryPermission::Read;
}
if (True(prot & MemoryProt::CpuReadWrite)) {
perms |= Core::MemoryPermission::ReadWrite;
}
if (True(prot & MemoryProt::GpuRead)) {
perms |= Core::MemoryPermission::Read;
}
if (True(prot & MemoryProt::GpuWrite)) {
perms |= Core::MemoryPermission::Write;
}
if (True(prot & MemoryProt::GpuReadWrite)) {
perms |= Core::MemoryPermission::ReadWrite;
}
impl.Protect(addr, size, perms);
return ORBIS_OK;
}
int MemoryManager::VirtualQuery(VAddr addr, int flags,
::Libraries::Kernel::OrbisVirtualQueryInfo* info) {
std::scoped_lock lk{mutex};

View file

@ -30,6 +30,7 @@ enum class MemoryProt : u32 {
GpuWrite = 32,
GpuReadWrite = 38,
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryProt)
enum class MemoryMapFlags : u32 {
NoFlags = 0,
@ -163,6 +164,10 @@ public:
int QueryProtection(VAddr addr, void** start, void** end, u32* prot);
int Protect(VAddr addr, size_t size, MemoryProt prot);
int MTypeProtect(VAddr addr, size_t size, VMAType mtype, MemoryProt prot);
int VirtualQuery(VAddr addr, int flags, ::Libraries::Kernel::OrbisVirtualQueryInfo* info);
int DirectMemoryQuery(PAddr addr, bool find_next,

View file

@ -3,6 +3,7 @@
#include <xbyak/xbyak.h>
#include "common/alignment.h"
#include "common/arch.h"
#include "common/assert.h"
#include "common/logging/log.h"
#ifdef ENABLE_QT_GUI
@ -134,9 +135,11 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
LOG_INFO(Core_Linker, "segment_mode ..........: {}", segment_mode);
add_segment(elf_pheader[i]);
#ifdef ARCH_X86_64
if (elf_pheader[i].p_flags & PF_EXEC) {
PatchInstructions(segment_addr, segment_file_size, c);
}
#endif
break;
}
case PT_DYNAMIC:

View file

@ -2,23 +2,28 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "common/arch.h"
#include "common/assert.h"
#include "common/types.h"
#include "core/tls.h"
#ifdef _WIN32
#include <windows.h>
#elif defined(__APPLE__)
#elif defined(__APPLE__) && defined(ARCH_X86_64)
#include <architecture/i386/table.h>
#include <boost/icl/interval_set.hpp>
#include <i386/user_ldt.h>
#include <sys/mman.h>
#elif !defined(ARCH_X86_64)
#include <pthread.h>
#endif
namespace Core {
#ifdef _WIN32
// Windows
static DWORD slot = 0;
static std::once_flag slot_alloc_flag;
@ -40,7 +45,9 @@ Tcb* GetTcbBase() {
return reinterpret_cast<Tcb*>(TlsGetValue(GetTcbKey()));
}
#elif defined(__APPLE__)
#elif defined(__APPLE__) && defined(ARCH_X86_64)
// Apple x86_64
// Reserve space in the 32-bit address range for allocating TCB pages.
asm(".zerofill TCB_SPACE,TCB_SPACE,__guest_system,0x3FC000");
@ -132,7 +139,9 @@ Tcb* GetTcbBase() {
return tcb;
}
#else
#elif defined(ARCH_X86_64)
// Other POSIX x86_64
void SetTcbBase(void* image_address) {
asm volatile("wrgsbase %0" ::"r"(image_address) : "memory");
@ -144,6 +153,32 @@ Tcb* GetTcbBase() {
return tcb;
}
#else
// POSIX non-x86_64
// Just sets up a simple thread-local variable to store it, then instruction translation can point
// code to it.
static pthread_key_t slot = 0;
static std::once_flag slot_alloc_flag;
static void AllocTcbKey() {
ASSERT(pthread_key_create(&slot, nullptr) == 0);
}
pthread_key_t GetTcbKey() {
std::call_once(slot_alloc_flag, &AllocTcbKey);
return slot;
}
void SetTcbBase(void* image_address) {
ASSERT(pthread_setspecific(GetTcbKey(), image_address) == 0);
}
Tcb* GetTcbBase() {
return static_cast<Tcb*>(pthread_getspecific(GetTcbKey()));
}
#endif
} // namespace Core

View file

@ -19,14 +19,15 @@
#include "core/file_format/playgo_chunk.h"
#include "core/file_format/psf.h"
#include "core/file_format/splash.h"
#include "core/file_format/trp.h"
#include "core/file_sys/fs.h"
#include "core/libraries/disc_map/disc_map.h"
#include "core/libraries/kernel/thread_management.h"
#include "core/libraries/libc_internal/libc_internal.h"
#include "core/libraries/libs.h"
#include "core/libraries/ngs2/ngs2.h"
#include "core/libraries/np_trophy/np_trophy.h"
#include "core/libraries/rtc/rtc.h"
#include "core/libraries/videoout/video_out.h"
#include "core/linker.h"
#include "core/memory.h"
#include "emulator.h"
@ -99,6 +100,15 @@ void Emulator::Run(const std::filesystem::path& file) {
auto* param_sfo = Common::Singleton<PSF>::Instance();
param_sfo->open(sce_sys_folder.string() + "/param.sfo", {});
id = std::string(param_sfo->GetString("CONTENT_ID"), 7, 9);
Libraries::NpTrophy::game_serial = id;
const auto trophyDir =
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / id / "TrophyFiles";
if (!std::filesystem::exists(trophyDir)) {
TRP trp;
if (!trp.Extract(file.parent_path())) {
LOG_ERROR(Loader, "Couldn't extract trophies");
}
}
#ifdef ENABLE_QT_GUI
MemoryPatcher::g_game_serial = id;
#endif
@ -195,7 +205,7 @@ void Emulator::Run(const std::filesystem::path& file) {
}
void Emulator::LoadSystemModules(const std::filesystem::path& file) {
constexpr std::array<SysModules, 9> ModulesToLoad{
constexpr std::array<SysModules, 10> ModulesToLoad{
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
{"libSceFiber.sprx", nullptr},
{"libSceUlt.sprx", nullptr},
@ -204,7 +214,8 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file) {
{"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal},
{"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap},
{"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc},
{"libSceJpegEnc.sprx", nullptr}},
{"libSceJpegEnc.sprx", nullptr},
{"libSceFont.sprx", nullptr}},
};
std::vector<std::filesystem::path> found_modules;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

29
src/imgui/imgui_config.h Normal file
View file

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// WARNING: All includes from this file must be relative to allow Dear_ImGui project to compile
// without having this project include paths.
#include <cstdint>
extern void assert_fail_debug_msg(const char* msg);
#define ImDrawIdx std::uint32_t
#define IM_STRINGIZE(x) IM_STRINGIZE2(x)
#define IM_STRINGIZE2(x) #x
#define IM_ASSERT(_EXPR) \
([&]() { \
if (!(_EXPR)) [[unlikely]] { \
assert_fail_debug_msg(#_EXPR " at " __FILE__ ":" IM_STRINGIZE(__LINE__)); \
} \
}())
#define IMGUI_USE_WCHAR32
#define IMGUI_ENABLE_STB_TRUETYPE
#define IMGUI_DEFINE_MATH_OPERATORS
#define IM_VEC2_CLASS_EXTRA \
constexpr ImVec2(float _v) : x(_v), y(_v) {}

21
src/imgui/imgui_layer.h Normal file
View file

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace ImGui {
class Layer {
public:
virtual ~Layer() = default;
static void AddLayer(Layer* layer);
static void RemoveLayer(Layer* layer);
virtual void Draw() = 0;
virtual bool ShouldGrabGamepad() {
return false;
}
};
} // namespace ImGui

27
src/imgui/imgui_std.h Normal file
View file

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <imgui.h>
#include "imgui_internal.h"
namespace ImGui {
inline void CentralizeWindow() {
const auto display_size = GetIO().DisplaySize;
SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f});
}
inline void KeepNavHighlight() {
GetCurrentContext()->NavDisableHighlight = false;
}
inline void SetItemCurrentNavFocus() {
const auto ctx = GetCurrentContext();
SetFocusID(ctx->LastItemData.ID, ctx->CurrentWindow);
ctx->NavInitResult.Clear();
}
} // namespace ImGui

View file

@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <imgui.h>
#include "video_info.h"
void ImGui::Layers::VideoInfo::Draw() {
const ImGuiIO& io = GetIO();
m_show = IsKeyPressed(ImGuiKey_F10, false) ^ m_show;
if (m_show) {
if (Begin("Video Info")) {
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
}
End();
}
}

View file

@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "imgui/imgui_layer.h"
namespace Vulkan {
class RendererVulkan;
}
namespace ImGui::Layers {
class VideoInfo : public Layer {
bool m_show = false;
::Vulkan::RendererVulkan* renderer{};
public:
explicit VideoInfo(::Vulkan::RendererVulkan* renderer) : renderer(renderer) {}
void Draw() override;
};
} // namespace ImGui::Layers

View file

@ -0,0 +1,195 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL_events.h>
#include <imgui.h>
#include "common/config.h"
#include "common/path_util.h"
#include "imgui/imgui_layer.h"
#include "imgui_core.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_vulkan.h"
#include "sdl_window.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
static void CheckVkResult(const vk::Result err) {
LOG_ERROR(ImGui, "Vulkan error {}", vk::to_string(err));
}
static std::vector<ImGui::Layer*> layers;
// Update layers before rendering to allow layer changes to be applied during rendering.
// Using deque to keep the order of changes in case a Layer is removed then added again between
// frames.
static std::deque<std::pair<bool, ImGui::Layer*>> change_layers;
static std::mutex change_layers_mutex{};
namespace ImGui {
namespace Core {
void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& window,
const u32 image_count, vk::Format surface_format,
const vk::AllocationCallbacks* allocator) {
const auto config_path = GetUserPath(Common::FS::PathType::UserDir) / "imgui.ini";
const auto log_path = GetUserPath(Common::FS::PathType::LogDir) / "imgui_log.txt";
CreateContext();
ImGuiIO& io = GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.DisplaySize = ImVec2((float)window.getWidth(), (float)window.getHeight());
io.IniFilename = SDL_strdup(config_path.string().c_str());
io.LogFilename = SDL_strdup(log_path.string().c_str());
StyleColorsDark();
Sdl::Init(window.GetSdlWindow());
const Vulkan::InitInfo vk_info{
.instance = instance.GetInstance(),
.physical_device = instance.GetPhysicalDevice(),
.device = instance.GetDevice(),
.queue_family = instance.GetPresentQueueFamilyIndex(),
.queue = instance.GetPresentQueue(),
.image_count = image_count,
.min_allocation_size = 1024 * 1024,
.pipeline_rendering_create_info{
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &surface_format,
},
.allocator = allocator,
.check_vk_result_fn = &CheckVkResult,
};
Vulkan::Init(vk_info);
}
void OnResize() {
Sdl::OnResize();
}
void Shutdown(const vk::Device& device) {
device.waitIdle();
const ImGuiIO& io = GetIO();
const auto ini_filename = (void*)io.IniFilename;
const auto log_filename = (void*)io.LogFilename;
Vulkan::Shutdown();
Sdl::Shutdown();
DestroyContext();
SDL_free(ini_filename);
SDL_free(log_filename);
}
bool ProcessEvent(SDL_Event* event) {
Sdl::ProcessEvent(event);
switch (event->type) {
case SDL_EVENT_MOUSE_MOTION:
case SDL_EVENT_MOUSE_WHEEL:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
return GetIO().WantCaptureMouse;
case SDL_EVENT_TEXT_INPUT:
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
return GetIO().WantCaptureKeyboard;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
case SDL_EVENT_GAMEPAD_ADDED:
case SDL_EVENT_GAMEPAD_REMOVED:
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
return (GetIO().BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
default:
return false;
}
}
void NewFrame() {
{
std::scoped_lock lock{change_layers_mutex};
while (!change_layers.empty()) {
const auto [to_be_added, layer] = change_layers.front();
if (to_be_added) {
layers.push_back(layer);
} else {
const auto [begin, end] = std::ranges::remove(layers, layer);
layers.erase(begin, end);
}
change_layers.pop_front();
}
}
Vulkan::NewFrame();
Sdl::NewFrame();
ImGui::NewFrame();
bool capture_gamepad = false;
for (auto* layer : layers) {
layer->Draw();
if (layer->ShouldGrabGamepad()) {
capture_gamepad = true;
}
}
if (capture_gamepad) {
GetIO().BackendFlags |= ImGuiBackendFlags_HasGamepad;
} else {
GetIO().BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
}
}
void Render(const vk::CommandBuffer& cmdbuf, ::Vulkan::Frame* frame) {
ImGui::Render();
ImDrawData* draw_data = GetDrawData();
if (draw_data->CmdListsCount == 0) {
return;
}
if (Config::vkMarkersEnabled()) {
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
.pLabelName = "ImGui Render",
});
}
vk::RenderingAttachmentInfo color_attachments[1]{
{
.imageView = frame->image_view,
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
.loadOp = vk::AttachmentLoadOp::eLoad,
.storeOp = vk::AttachmentStoreOp::eStore,
},
};
vk::RenderingInfo render_info = {};
render_info.renderArea = vk::Rect2D{
.offset = {0, 0},
.extent = {frame->width, frame->height},
};
render_info.layerCount = 1;
render_info.colorAttachmentCount = 1;
render_info.pColorAttachments = color_attachments;
cmdbuf.beginRendering(render_info);
Vulkan::RenderDrawData(*draw_data, cmdbuf);
cmdbuf.endRendering();
if (Config::vkMarkersEnabled()) {
cmdbuf.endDebugUtilsLabelEXT();
}
}
} // namespace Core
void Layer::AddLayer(Layer* layer) {
std::scoped_lock lock{change_layers_mutex};
change_layers.emplace_back(true, layer);
}
void Layer::RemoveLayer(Layer* layer) {
std::scoped_lock lock{change_layers_mutex};
change_layers.emplace_back(false, layer);
}
} // namespace ImGui

View file

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "video_core/renderer_vulkan/vk_instance.h"
#include "vulkan/vulkan_handles.hpp"
union SDL_Event;
namespace Vulkan {
struct Frame;
}
namespace ImGui::Core {
void Initialize(const Vulkan::Instance& instance, const Frontend::WindowSDL& window,
u32 image_count, vk::Format surface_format,
const vk::AllocationCallbacks* allocator = nullptr);
void OnResize();
void Shutdown(const vk::Device& device);
bool ProcessEvent(SDL_Event* event);
void NewFrame();
void Render(const vk::CommandBuffer& cmdbuf, Vulkan::Frame* frame);
} // namespace ImGui::Core

View file

@ -0,0 +1,789 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Based on imgui_impl_sdl3.cpp from Dear ImGui repository
#include <imgui.h>
#include "imgui_impl_sdl3.h"
// SDL
#include <SDL3/SDL.h>
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#endif
namespace ImGui::Sdl {
// SDL Data
struct SdlData {
SDL_Window* window{};
SDL_WindowID window_id{};
Uint64 time{};
const char* clipboard_text_data{};
// IME handling
SDL_Window* ime_window{};
// Mouse handling
Uint32 mouse_window_id{};
int mouse_buttons_down{};
SDL_Cursor* mouse_cursors[ImGuiMouseCursor_COUNT]{};
SDL_Cursor* mouse_last_cursor{};
int mouse_pending_leave_frame{};
// Gamepad handling
ImVector<SDL_Gamepad*> gamepads{};
GamepadMode gamepad_mode{};
bool want_update_gamepads_list{};
};
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui
// contexts It is STRONGLY preferred that you use docking branch with multi-viewports (== single
// Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
static SdlData* GetBackendData() {
return ImGui::GetCurrentContext() ? (SdlData*)ImGui::GetIO().BackendPlatformUserData : nullptr;
}
static const char* GetClipboardText(ImGuiContext*) {
SdlData* bd = GetBackendData();
if (bd->clipboard_text_data)
SDL_free((void*)bd->clipboard_text_data);
const char* sdl_clipboard_text = SDL_GetClipboardText();
bd->clipboard_text_data = sdl_clipboard_text;
return bd->clipboard_text_data;
}
static void SetClipboardText(ImGuiContext*, const char* text) {
SDL_SetClipboardText(text);
}
static void PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) {
SdlData* bd = GetBackendData();
auto window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle;
SDL_Window* window = SDL_GetWindowFromID(window_id);
if ((!data->WantVisible || bd->ime_window != window) && bd->ime_window != nullptr) {
SDL_StopTextInput(bd->ime_window);
bd->ime_window = nullptr;
}
if (data->WantVisible) {
SDL_Rect r;
r.x = (int)data->InputPos.x;
r.y = (int)data->InputPos.y;
r.w = 1;
r.h = (int)data->InputLineHeight;
SDL_SetTextInputArea(window, &r, 0);
SDL_StartTextInput(window);
bd->ime_window = window;
}
}
static ImGuiKey KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) {
// Keypad doesn't have individual key values in SDL3
switch (scancode) {
case SDL_SCANCODE_KP_0:
return ImGuiKey_Keypad0;
case SDL_SCANCODE_KP_1:
return ImGuiKey_Keypad1;
case SDL_SCANCODE_KP_2:
return ImGuiKey_Keypad2;
case SDL_SCANCODE_KP_3:
return ImGuiKey_Keypad3;
case SDL_SCANCODE_KP_4:
return ImGuiKey_Keypad4;
case SDL_SCANCODE_KP_5:
return ImGuiKey_Keypad5;
case SDL_SCANCODE_KP_6:
return ImGuiKey_Keypad6;
case SDL_SCANCODE_KP_7:
return ImGuiKey_Keypad7;
case SDL_SCANCODE_KP_8:
return ImGuiKey_Keypad8;
case SDL_SCANCODE_KP_9:
return ImGuiKey_Keypad9;
case SDL_SCANCODE_KP_PERIOD:
return ImGuiKey_KeypadDecimal;
case SDL_SCANCODE_KP_DIVIDE:
return ImGuiKey_KeypadDivide;
case SDL_SCANCODE_KP_MULTIPLY:
return ImGuiKey_KeypadMultiply;
case SDL_SCANCODE_KP_MINUS:
return ImGuiKey_KeypadSubtract;
case SDL_SCANCODE_KP_PLUS:
return ImGuiKey_KeypadAdd;
case SDL_SCANCODE_KP_ENTER:
return ImGuiKey_KeypadEnter;
case SDL_SCANCODE_KP_EQUALS:
return ImGuiKey_KeypadEqual;
default:
break;
}
switch (keycode) {
case SDLK_TAB:
return ImGuiKey_Tab;
case SDLK_LEFT:
return ImGuiKey_LeftArrow;
case SDLK_RIGHT:
return ImGuiKey_RightArrow;
case SDLK_UP:
return ImGuiKey_UpArrow;
case SDLK_DOWN:
return ImGuiKey_DownArrow;
case SDLK_PAGEUP:
return ImGuiKey_PageUp;
case SDLK_PAGEDOWN:
return ImGuiKey_PageDown;
case SDLK_HOME:
return ImGuiKey_Home;
case SDLK_END:
return ImGuiKey_End;
case SDLK_INSERT:
return ImGuiKey_Insert;
case SDLK_DELETE:
return ImGuiKey_Delete;
case SDLK_BACKSPACE:
return ImGuiKey_Backspace;
case SDLK_SPACE:
return ImGuiKey_Space;
case SDLK_RETURN:
return ImGuiKey_Enter;
case SDLK_ESCAPE:
return ImGuiKey_Escape;
case SDLK_APOSTROPHE:
return ImGuiKey_Apostrophe;
case SDLK_COMMA:
return ImGuiKey_Comma;
case SDLK_MINUS:
return ImGuiKey_Minus;
case SDLK_PERIOD:
return ImGuiKey_Period;
case SDLK_SLASH:
return ImGuiKey_Slash;
case SDLK_SEMICOLON:
return ImGuiKey_Semicolon;
case SDLK_EQUALS:
return ImGuiKey_Equal;
case SDLK_LEFTBRACKET:
return ImGuiKey_LeftBracket;
case SDLK_BACKSLASH:
return ImGuiKey_Backslash;
case SDLK_RIGHTBRACKET:
return ImGuiKey_RightBracket;
case SDLK_GRAVE:
return ImGuiKey_GraveAccent;
case SDLK_CAPSLOCK:
return ImGuiKey_CapsLock;
case SDLK_SCROLLLOCK:
return ImGuiKey_ScrollLock;
case SDLK_NUMLOCKCLEAR:
return ImGuiKey_NumLock;
case SDLK_PRINTSCREEN:
return ImGuiKey_PrintScreen;
case SDLK_PAUSE:
return ImGuiKey_Pause;
case SDLK_LCTRL:
return ImGuiKey_LeftCtrl;
case SDLK_LSHIFT:
return ImGuiKey_LeftShift;
case SDLK_LALT:
return ImGuiKey_LeftAlt;
case SDLK_LGUI:
return ImGuiKey_LeftSuper;
case SDLK_RCTRL:
return ImGuiKey_RightCtrl;
case SDLK_RSHIFT:
return ImGuiKey_RightShift;
case SDLK_RALT:
return ImGuiKey_RightAlt;
case SDLK_RGUI:
return ImGuiKey_RightSuper;
case SDLK_APPLICATION:
return ImGuiKey_Menu;
case SDLK_0:
return ImGuiKey_0;
case SDLK_1:
return ImGuiKey_1;
case SDLK_2:
return ImGuiKey_2;
case SDLK_3:
return ImGuiKey_3;
case SDLK_4:
return ImGuiKey_4;
case SDLK_5:
return ImGuiKey_5;
case SDLK_6:
return ImGuiKey_6;
case SDLK_7:
return ImGuiKey_7;
case SDLK_8:
return ImGuiKey_8;
case SDLK_9:
return ImGuiKey_9;
case SDLK_A:
return ImGuiKey_A;
case SDLK_B:
return ImGuiKey_B;
case SDLK_C:
return ImGuiKey_C;
case SDLK_D:
return ImGuiKey_D;
case SDLK_E:
return ImGuiKey_E;
case SDLK_F:
return ImGuiKey_F;
case SDLK_G:
return ImGuiKey_G;
case SDLK_H:
return ImGuiKey_H;
case SDLK_I:
return ImGuiKey_I;
case SDLK_J:
return ImGuiKey_J;
case SDLK_K:
return ImGuiKey_K;
case SDLK_L:
return ImGuiKey_L;
case SDLK_M:
return ImGuiKey_M;
case SDLK_N:
return ImGuiKey_N;
case SDLK_O:
return ImGuiKey_O;
case SDLK_P:
return ImGuiKey_P;
case SDLK_Q:
return ImGuiKey_Q;
case SDLK_R:
return ImGuiKey_R;
case SDLK_S:
return ImGuiKey_S;
case SDLK_T:
return ImGuiKey_T;
case SDLK_U:
return ImGuiKey_U;
case SDLK_V:
return ImGuiKey_V;
case SDLK_W:
return ImGuiKey_W;
case SDLK_X:
return ImGuiKey_X;
case SDLK_Y:
return ImGuiKey_Y;
case SDLK_Z:
return ImGuiKey_Z;
case SDLK_F1:
return ImGuiKey_F1;
case SDLK_F2:
return ImGuiKey_F2;
case SDLK_F3:
return ImGuiKey_F3;
case SDLK_F4:
return ImGuiKey_F4;
case SDLK_F5:
return ImGuiKey_F5;
case SDLK_F6:
return ImGuiKey_F6;
case SDLK_F7:
return ImGuiKey_F7;
case SDLK_F8:
return ImGuiKey_F8;
case SDLK_F9:
return ImGuiKey_F9;
case SDLK_F10:
return ImGuiKey_F10;
case SDLK_F11:
return ImGuiKey_F11;
case SDLK_F12:
return ImGuiKey_F12;
case SDLK_F13:
return ImGuiKey_F13;
case SDLK_F14:
return ImGuiKey_F14;
case SDLK_F15:
return ImGuiKey_F15;
case SDLK_F16:
return ImGuiKey_F16;
case SDLK_F17:
return ImGuiKey_F17;
case SDLK_F18:
return ImGuiKey_F18;
case SDLK_F19:
return ImGuiKey_F19;
case SDLK_F20:
return ImGuiKey_F20;
case SDLK_F21:
return ImGuiKey_F21;
case SDLK_F22:
return ImGuiKey_F22;
case SDLK_F23:
return ImGuiKey_F23;
case SDLK_F24:
return ImGuiKey_F24;
case SDLK_AC_BACK:
return ImGuiKey_AppBack;
case SDLK_AC_FORWARD:
return ImGuiKey_AppForward;
default:
break;
}
return ImGuiKey_None;
}
static void UpdateKeyModifiers(SDL_Keymod sdl_key_mods) {
ImGuiIO& io = ImGui::GetIO();
io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL_KMOD_CTRL) != 0);
io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL_KMOD_SHIFT) != 0);
io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL_KMOD_ALT) != 0);
io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0);
}
static ImGuiViewport* GetViewportForWindowId(SDL_WindowID window_id) {
SdlData* bd = GetBackendData();
return (window_id == bd->window_id) ? ImGui::GetMainViewport() : nullptr;
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to
// use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or
// clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main
// application, or clear/overwrite your copy of the keyboard data. Generally you may always pass all
// inputs to dear imgui, and hide them from your application based on those two flags. If you have
// multiple SDL events and some of them are not meant to be used by dear imgui, you may need to
// filter events based on their windowID field.
bool ProcessEvent(const SDL_Event* event) {
SdlData* bd = GetBackendData();
IM_ASSERT(bd != nullptr &&
"Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?");
ImGuiIO& io = ImGui::GetIO();
switch (event->type) {
case SDL_EVENT_MOUSE_MOTION: {
if (GetViewportForWindowId(event->motion.windowID) == NULL)
return false;
ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID
? ImGuiMouseSource_TouchScreen
: ImGuiMouseSource_Mouse);
io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
return true;
}
case SDL_EVENT_MOUSE_WHEEL: {
if (GetViewportForWindowId(event->wheel.windowID) == NULL)
return false;
// IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x,
// (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
float wheel_x = -event->wheel.x;
float wheel_y = event->wheel.y;
#ifdef __EMSCRIPTEN__
wheel_x /= 100.0f;
#endif
io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID
? ImGuiMouseSource_TouchScreen
: ImGuiMouseSource_Mouse);
io.AddMouseWheelEvent(wheel_x, wheel_y);
return true;
}
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP: {
if (GetViewportForWindowId(event->button.windowID) == NULL)
return false;
int mouse_button = -1;
if (event->button.button == SDL_BUTTON_LEFT) {
mouse_button = 0;
}
if (event->button.button == SDL_BUTTON_RIGHT) {
mouse_button = 1;
}
if (event->button.button == SDL_BUTTON_MIDDLE) {
mouse_button = 2;
}
if (event->button.button == SDL_BUTTON_X1) {
mouse_button = 3;
}
if (event->button.button == SDL_BUTTON_X2) {
mouse_button = 4;
}
if (mouse_button == -1)
break;
io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID
? ImGuiMouseSource_TouchScreen
: ImGuiMouseSource_Mouse);
io.AddMouseButtonEvent(mouse_button, (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN));
bd->mouse_buttons_down = (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
? (bd->mouse_buttons_down | (1 << mouse_button))
: (bd->mouse_buttons_down & ~(1 << mouse_button));
return true;
}
case SDL_EVENT_TEXT_INPUT: {
if (GetViewportForWindowId(event->text.windowID) == NULL)
return false;
io.AddInputCharactersUTF8(event->text.text);
return true;
}
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP: {
if (GetViewportForWindowId(event->key.windowID) == NULL)
return false;
// IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type ==
// SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode,
// event->key.mod);
UpdateKeyModifiers((SDL_Keymod)event->key.mod);
ImGuiKey key = KeyEventToImGuiKey(event->key.key, event->key.scancode);
io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN));
io.SetKeyEventNativeData(
key, event->key.key, event->key.scancode,
event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend
// uses SDLK_*** as indices to IsKeyXXX() functions.
return true;
}
case SDL_EVENT_WINDOW_MOUSE_ENTER: {
if (GetViewportForWindowId(event->window.windowID) == NULL)
return false;
bd->mouse_window_id = event->window.windowID;
bd->mouse_pending_leave_frame = 0;
return true;
}
// - In some cases, when detaching a window from main viewport SDL may send
// SDL_WINDOWEVENT_ENTER one frame too late,
// causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse
// position. This is why we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See
// issue #5012 for details.
// FIXME: Unconfirmed whether this is still needed with SDL3.
case SDL_EVENT_WINDOW_MOUSE_LEAVE: {
if (GetViewportForWindowId(event->window.windowID) == NULL)
return false;
bd->mouse_pending_leave_frame = ImGui::GetFrameCount() + 1;
return true;
}
case SDL_EVENT_WINDOW_FOCUS_GAINED:
case SDL_EVENT_WINDOW_FOCUS_LOST: {
if (GetViewportForWindowId(event->window.windowID) == NULL)
return false;
io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED);
return true;
}
case SDL_EVENT_GAMEPAD_ADDED:
case SDL_EVENT_GAMEPAD_REMOVED: {
bd->want_update_gamepads_list = true;
return true;
}
}
return false;
}
static void SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window) {
viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window);
viewport->PlatformHandleRaw = nullptr;
#if defined(_WIN32) && !defined(__WINRT__)
viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(
SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
viewport->PlatformHandleRaw = SDL_GetPointerProperty(
SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
#endif
}
bool Init(SDL_Window* window) {
ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
// Setup backend capabilities flags
SdlData* bd = IM_NEW(SdlData)();
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = "imgui_impl_sdl3_shadps4";
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests
// (optional, rarely used)
bd->window = window;
bd->window_id = SDL_GetWindowID(window);
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Platform_SetClipboardTextFn = SetClipboardText;
platform_io.Platform_GetClipboardTextFn = GetClipboardText;
platform_io.Platform_SetImeDataFn = PlatformSetImeData;
// Gamepad handling
bd->gamepad_mode = ImGui_ImplSDL3_GamepadMode_AutoFirst;
bd->want_update_gamepads_list = true;
// Load mouse cursors
#define CURSOR(left, right) \
bd->mouse_cursors[ImGuiMouseCursor_##left] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_##right)
CURSOR(Arrow, DEFAULT);
CURSOR(TextInput, TEXT);
CURSOR(ResizeAll, MOVE);
CURSOR(ResizeNS, NS_RESIZE);
CURSOR(ResizeEW, EW_RESIZE);
CURSOR(ResizeNESW, NESW_RESIZE);
CURSOR(ResizeNWSE, NWSE_RESIZE);
CURSOR(Hand, POINTER);
CURSOR(NotAllowed, NOT_ALLOWED);
#undef CURSOR
// Set platform dependent data in viewport
// Our mouse update function expect PlatformHandle to be filled for the main viewport
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
SetupPlatformHandles(main_viewport, window);
// From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't
// emit the event. Without this, when clicking to gain focus, our widgets wouldn't activate even
// though they showed as hovered. (This is unfortunately a global SDL setting, so enabling it
// might have a side-effect on your application. It is unlikely to make a difference, but if
// your app absolutely needs to ignore the initial on-focus click: you can ignore
// SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
#endif
// From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows
// (see #5710)
#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
#endif
return true;
}
static void CloseGamepads();
void Shutdown() {
SdlData* bd = GetBackendData();
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
if (bd->clipboard_text_data) {
SDL_free((void*)bd->clipboard_text_data);
}
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
SDL_DestroyCursor(bd->mouse_cursors[cursor_n]);
CloseGamepads();
io.BackendPlatformName = nullptr;
io.BackendPlatformUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos |
ImGuiBackendFlags_HasGamepad);
IM_DELETE(bd);
}
static void UpdateMouseData() {
SdlData* bd = GetBackendData();
ImGuiIO& io = ImGui::GetIO();
// We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused
// (below)
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries
// shouldn't e.g. trigger other operations outside
SDL_CaptureMouse((bd->mouse_buttons_down != 0) ? SDL_TRUE : SDL_FALSE);
SDL_Window* focused_window = SDL_GetKeyboardFocus();
const bool is_app_focused = (bd->window == focused_window);
if (is_app_focused) {
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when
// ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
SDL_WarpMouseInWindow(bd->window, io.MousePos.x, io.MousePos.y);
// (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION
// already provides this when hovered or captured)
if (bd->mouse_buttons_down == 0) {
// Single-viewport mode: mouse position in client window coordinates (io.MousePos is
// (0,0) when the mouse is on the upper-left corner of the app window)
float mouse_x_global, mouse_y_global;
int window_x, window_y;
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
SDL_GetWindowPosition(focused_window, &window_x, &window_y);
io.AddMousePosEvent(mouse_x_global - (float)window_x, mouse_y_global - (float)window_y);
}
}
}
static void UpdateMouseCursor() {
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return;
SdlData* bd = GetBackendData();
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) {
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
SDL_HideCursor();
} else {
// Show OS mouse cursor
SDL_Cursor* expected_cursor = bd->mouse_cursors[imgui_cursor]
? bd->mouse_cursors[imgui_cursor]
: bd->mouse_cursors[ImGuiMouseCursor_Arrow];
if (bd->mouse_last_cursor != expected_cursor) {
SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
bd->mouse_last_cursor = expected_cursor;
}
SDL_ShowCursor();
}
}
static void CloseGamepads() {
SdlData* bd = GetBackendData();
if (bd->gamepad_mode != ImGui_ImplSDL3_GamepadMode_Manual)
for (SDL_Gamepad* gamepad : bd->gamepads)
SDL_CloseGamepad(gamepad);
bd->gamepads.resize(0);
}
void SetGamepadMode(GamepadMode mode, SDL_Gamepad** manual_gamepads_array,
int manual_gamepads_count) {
SdlData* bd = GetBackendData();
CloseGamepads();
if (mode == ImGui_ImplSDL3_GamepadMode_Manual) {
IM_ASSERT(manual_gamepads_array != nullptr && manual_gamepads_count > 0);
for (int n = 0; n < manual_gamepads_count; n++)
bd->gamepads.push_back(manual_gamepads_array[n]);
} else {
IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0);
bd->want_update_gamepads_list = true;
}
bd->gamepad_mode = mode;
}
static void UpdateGamepadButton(SdlData* bd, ImGuiIO& io, ImGuiKey key,
SDL_GamepadButton button_no) {
bool merged_value = false;
for (SDL_Gamepad* gamepad : bd->gamepads)
merged_value |= SDL_GetGamepadButton(gamepad, button_no) != 0;
io.AddKeyEvent(key, merged_value);
}
static inline float Saturate(float v) {
return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v;
}
static void UpdateGamepadAnalog(SdlData* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadAxis axis_no,
float v0, float v1) {
float merged_value = 0.0f;
for (SDL_Gamepad* gamepad : bd->gamepads) {
float vn = Saturate((float)(SDL_GetGamepadAxis(gamepad, axis_no) - v0) / (float)(v1 - v0));
if (merged_value < vn)
merged_value = vn;
}
io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value);
}
static void UpdateGamepads() {
ImGuiIO& io = ImGui::GetIO();
SdlData* bd = GetBackendData();
// Update list of gamepads to use
if (bd->want_update_gamepads_list && bd->gamepad_mode != ImGui_ImplSDL3_GamepadMode_Manual) {
CloseGamepads();
int sdl_gamepads_count = 0;
const SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count);
for (int n = 0; n < sdl_gamepads_count; n++)
if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n])) {
bd->gamepads.push_back(gamepad);
if (bd->gamepad_mode == ImGui_ImplSDL3_GamepadMode_AutoFirst)
break;
}
bd->want_update_gamepads_list = false;
}
// FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
if (bd->gamepads.Size == 0)
return;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
// Update gamepad inputs
const int thumb_dead_zone = 8000; // SDL_gamepad.h suggests using this value.
UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart, SDL_GAMEPAD_BUTTON_START);
UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack, SDL_GAMEPAD_BUTTON_BACK);
UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft,
SDL_GAMEPAD_BUTTON_WEST); // Xbox X, PS Square
UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight,
SDL_GAMEPAD_BUTTON_EAST); // Xbox B, PS Circle
UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp,
SDL_GAMEPAD_BUTTON_NORTH); // Xbox Y, PS Triangle
UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown,
SDL_GAMEPAD_BUTTON_SOUTH); // Xbox A, PS Cross
UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp, SDL_GAMEPAD_BUTTON_DPAD_UP);
UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0.0f, 32767);
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767);
UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3, SDL_GAMEPAD_BUTTON_LEFT_STICK);
UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3, SDL_GAMEPAD_BUTTON_RIGHT_STICK);
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft, SDL_GAMEPAD_AXIS_LEFTX,
-thumb_dead_zone, -32768);
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_GAMEPAD_AXIS_LEFTX,
+thumb_dead_zone, +32767);
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp, SDL_GAMEPAD_AXIS_LEFTY, -thumb_dead_zone,
-32768);
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown, SDL_GAMEPAD_AXIS_LEFTY,
+thumb_dead_zone, +32767);
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft, SDL_GAMEPAD_AXIS_RIGHTX,
-thumb_dead_zone, -32768);
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_GAMEPAD_AXIS_RIGHTX,
+thumb_dead_zone, +32767);
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp, SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone,
-32768);
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY,
+thumb_dead_zone, +32767);
}
void NewFrame() {
SdlData* bd = GetBackendData();
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
// (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens
// in VMs and Emscripten, see #6189, #6114, #3644)
static Uint64 frequency = SDL_GetPerformanceFrequency();
Uint64 current_time = SDL_GetPerformanceCounter();
if (current_time <= bd->time)
current_time = bd->time + 1;
io.DeltaTime = bd->time > 0 ? (float)((double)(current_time - bd->time) / (double)frequency)
: (float)(1.0f / 60.0f);
bd->time = current_time;
if (bd->mouse_pending_leave_frame && bd->mouse_pending_leave_frame >= ImGui::GetFrameCount() &&
bd->mouse_buttons_down == 0) {
bd->mouse_window_id = 0;
bd->mouse_pending_leave_frame = 0;
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
}
UpdateMouseData();
UpdateMouseCursor();
// Update game controllers (if enabled and available)
UpdateGamepads();
}
void OnResize() {
SdlData* bd = GetBackendData();
ImGuiIO& io = ImGui::GetIO();
int w, h;
int display_w, display_h;
SDL_GetWindowSize(bd->window, &w, &h);
if (SDL_GetWindowFlags(bd->window) & SDL_WINDOW_MINIMIZED) {
w = h = 0;
}
SDL_GetWindowSizeInPixels(bd->window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0) {
io.DisplayFramebufferScale = {(float)display_w / (float)w, (float)display_h / (float)h};
}
}
} // namespace ImGui::Sdl

View file

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Based on imgui_impl_sdl3.h from Dear ImGui repository
#pragma once
struct SDL_Window;
struct SDL_Renderer;
struct SDL_Gamepad;
typedef union SDL_Event SDL_Event;
namespace ImGui::Sdl {
bool Init(SDL_Window* window);
void Shutdown();
void NewFrame();
bool ProcessEvent(const SDL_Event* event);
void OnResize();
// Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad.
// You may override this. When using manual mode, caller is responsible for opening/closing gamepad.
enum GamepadMode {
ImGui_ImplSDL3_GamepadMode_AutoFirst,
ImGui_ImplSDL3_GamepadMode_AutoAll,
ImGui_ImplSDL3_GamepadMode_Manual
};
void SetGamepadMode(GamepadMode mode, SDL_Gamepad** manual_gamepads_array = NULL,
int manual_gamepads_count = -1);
}; // namespace ImGui::Sdl

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Based on imgui_impl_vulkan.h from Dear ImGui repository
#pragma once
#define VULKAN_HPP_NO_EXCEPTIONS
#include "video_core/renderer_vulkan/vk_common.h"
struct ImDrawData;
namespace ImGui::Vulkan {
struct InitInfo {
vk::Instance instance;
vk::PhysicalDevice physical_device;
vk::Device device;
uint32_t queue_family;
vk::Queue queue;
uint32_t image_count; // >= 2
vk::DeviceSize min_allocation_size; // Minimum allocation size
vk::PipelineCache pipeline_cache;
uint32_t subpass;
vk::PipelineRenderingCreateInfoKHR pipeline_rendering_create_info;
// (Optional) Allocation, Logging
const vk::AllocationCallbacks* allocator{};
void (*check_vk_result_fn)(vk::Result err);
};
vk::DescriptorSet AddTexture(vk::Sampler sampler, vk::ImageView image_view,
vk::ImageLayout image_layout);
void RemoveTexture(vk::DescriptorSet descriptor_set);
bool Init(InitInfo info);
void Shutdown();
void NewFrame();
void RenderDrawData(ImDrawData& draw_data, vk::CommandBuffer command_buffer,
vk::Pipeline pipeline = VK_NULL_HANDLE);
} // namespace ImGui::Vulkan

View file

@ -669,105 +669,86 @@ void CheatsPatches::populateFileListPatches() {
void CheatsPatches::downloadPatches(const QString repository, const bool showMessageBox) {
QString url;
if (repository == "GoldHEN") {
url = "https://github.com/illusion0001/PS4-PS5-Game-Patch/tree/main/"
"patches/xml";
url = "https://api.github.com/repos/illusion0001/PS4-PS5-Game-Patch/contents/patches/xml";
}
if (repository == "shadPS4") {
url = "https://github.com/shadps4-emu/ps4_cheats/tree/main/"
"PATCHES";
url = "https://api.github.com/repos/shadps4-emu/ps4_cheats/contents/PATCHES";
}
QNetworkAccessManager* manager = new QNetworkAccessManager(this);
QNetworkRequest request(url);
request.setRawHeader("Accept", "application/vnd.github.v3+json");
QNetworkReply* reply = manager->get(request);
connect(reply, &QNetworkReply::finished, [=, this]() {
connect(reply, &QNetworkReply::finished, [=]() {
if (reply->error() == QNetworkReply::NoError) {
QByteArray htmlData = reply->readAll();
QByteArray jsonData = reply->readAll();
reply->deleteLater();
// Parsear HTML e extrair JSON usando QRegularExpression
QString htmlString = QString::fromUtf8(htmlData);
QRegularExpression jsonRegex(
R"(<script type="application/json" data-target="react-app.embeddedData">(.+?)</script>)");
QRegularExpressionMatch match = jsonRegex.match(htmlString);
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
QJsonArray itemsArray = jsonDoc.array();
if (match.hasMatch()) {
QByteArray jsonData = match.captured(1).toUtf8();
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
QJsonObject jsonObj = jsonDoc.object();
QJsonArray itemsArray =
jsonObj["payload"].toObject()["tree"].toObject()["items"].toArray();
QDir dir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
QString fullPath = dir.filePath(repository);
if (!dir.exists(fullPath)) {
dir.mkpath(fullPath);
}
dir.setPath(fullPath);
foreach (const QJsonValue& value, itemsArray) {
QJsonObject fileObj = value.toObject();
QString fileName = fileObj["name"].toString();
QString filePath = fileObj["path"].toString();
if (fileName.endsWith(".xml")) {
QString fileUrl;
if (repository == "GoldHEN") {
fileUrl = QString("https://raw.githubusercontent.com/illusion0001/"
"PS4-PS5-Game-Patch/main/%1")
.arg(filePath);
}
if (repository == "shadPS4") {
fileUrl = QString("https://raw.githubusercontent.com/shadps4-emu/"
"ps4_cheats/main/%1")
.arg(filePath);
}
QNetworkRequest fileRequest(fileUrl);
QNetworkReply* fileReply = manager->get(fileRequest);
connect(fileReply, &QNetworkReply::finished, [=, this]() {
if (fileReply->error() == QNetworkReply::NoError) {
QByteArray fileData = fileReply->readAll();
QFile localFile(dir.filePath(fileName));
if (localFile.open(QIODevice::WriteOnly)) {
localFile.write(fileData);
localFile.close();
} else {
if (showMessageBox) {
QMessageBox::warning(
this, tr("Error"),
QString(tr("Failed to save:") + "\n%1").arg(fileName));
}
}
} else {
if (showMessageBox) {
QMessageBox::warning(
this, tr("Error"),
QString(tr("Failed to download:") + "\n%1").arg(fileUrl));
}
}
fileReply->deleteLater();
});
}
}
if (showMessageBox) {
QMessageBox::information(this, tr("Download Complete"),
QString(tr("DownloadComplete_MSG")));
}
// Create the files.json file with the identification of which file to open
createFilesJson(repository);
populateFileListPatches();
} else {
if (itemsArray.isEmpty()) {
if (showMessageBox) {
QMessageBox::warning(this, tr("Error"),
tr("Failed to parse JSON data from HTML."));
}
return;
}
QDir dir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
QString fullPath = dir.filePath(repository);
if (!dir.exists(fullPath)) {
dir.mkpath(fullPath);
}
dir.setPath(fullPath);
foreach (const QJsonValue& value, itemsArray) {
QJsonObject fileObj = value.toObject();
QString fileName = fileObj["name"].toString();
QString filePath = fileObj["path"].toString();
QString downloadUrl = fileObj["download_url"].toString();
if (fileName.endsWith(".xml")) {
QNetworkRequest fileRequest(downloadUrl);
QNetworkReply* fileReply = manager->get(fileRequest);
connect(fileReply, &QNetworkReply::finished, [=]() {
if (fileReply->error() == QNetworkReply::NoError) {
QByteArray fileData = fileReply->readAll();
QFile localFile(dir.filePath(fileName));
if (localFile.open(QIODevice::WriteOnly)) {
localFile.write(fileData);
localFile.close();
} else {
if (showMessageBox) {
QMessageBox::warning(
this, tr("Error"),
QString(tr("Failed to save:") + "\n%1").arg(fileName));
}
}
} else {
if (showMessageBox) {
QMessageBox::warning(
this, tr("Error"),
QString(tr("Failed to download:") + "\n%1").arg(downloadUrl));
}
}
fileReply->deleteLater();
});
}
}
if (showMessageBox) {
QMessageBox::information(this, tr("Download Complete"),
QString(tr("DownloadComplete_MSG")));
}
// Create the files.json file with the identification of which file to open
createFilesJson(repository);
populateFileListPatches();
} else {
if (showMessageBox) {
QMessageBox::warning(this, tr("Error"), tr("Failed to retrieve HTML page."));
QMessageBox::warning(this, tr("Error"),
QString(tr("Failed to retrieve HTML page.") + "\n%1")
.arg(reply->errorString()));
}
}
emit downloadFinished();

View file

@ -114,8 +114,8 @@ void GameGridFrame::SetGridBackgroundImage(int row, int column) {
QWidget* item = this->cellWidget(row, column);
if (item) {
QString pic1Path = QString::fromStdString((*m_games_shared)[itemID].pic_path);
const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::UserDir) /
"game_data" / (*m_games_shared)[itemID].serial / "pic1.png";
const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
(*m_games_shared)[itemID].serial / "pic1.png";
#ifdef _WIN32
const auto blurredPic1PathQt = QString::fromStdWString(blurredPic1Path.wstring());
#else
@ -128,7 +128,8 @@ void GameGridFrame::SetGridBackgroundImage(int row, int column) {
backgroundImage = m_game_list_utils.BlurImage(image, image.rect(), 16);
std::filesystem::path img_path =
std::filesystem::path("user/game_data/") / (*m_games_shared)[itemID].serial;
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
(*m_games_shared)[itemID].serial;
std::filesystem::create_directories(img_path);
if (!backgroundImage.save(blurredPic1PathQt, "PNG")) {
// qDebug() << "Error: Unable to save image.";

View file

@ -31,14 +31,8 @@ GameListFrame::GameListFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidg
this->setColumnWidth(5, 90); // Size
this->setColumnWidth(6, 90); // Version
QStringList headers;
headers << "Icon"
<< "Name"
<< "Serial"
<< "Region"
<< "Firmware"
<< "Size"
<< "Version"
<< "Path";
headers << tr("Icon") << tr("Name") << tr("Serial") << tr("Region") << tr("Firmware")
<< tr("Size") << tr("Version") << tr("Path");
this->setHorizontalHeaderLabels(headers);
this->horizontalHeader()->setSortIndicatorShown(true);
this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
@ -96,9 +90,8 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
}
QString pic1Path = QString::fromStdString(m_game_info->m_games[item->row()].pic_path);
const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::UserDir) /
"game_data" / m_game_info->m_games[item->row()].serial /
"pic1.png";
const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
m_game_info->m_games[item->row()].serial / "pic1.png";
#ifdef _WIN32
const auto blurredPic1PathQt = QString::fromStdWString(blurredPic1Path.wstring());
#else
@ -111,7 +104,8 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
backgroundImage = m_game_list_utils.BlurImage(image, image.rect(), 16);
std::filesystem::path img_path =
std::filesystem::path("user/game_data/") / m_game_info->m_games[item->row()].serial;
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
m_game_info->m_games[item->row()].serial;
std::filesystem::create_directories(img_path);
if (!backgroundImage.save(blurredPic1PathQt, "PNG")) {
// qDebug() << "Error: Unable to save image.";

View file

@ -16,7 +16,6 @@ int main(int argc, char* argv[]) {
// Load configurations and initialize Qt application
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
Config::load(user_dir / "config.toml");
std::filesystem::create_directory(user_dir / "game_data");
// Check if elf or eboot.bin path was passed as a command line argument
bool has_command_line_argument = argc > 1;

View file

@ -34,12 +34,12 @@ bool MainWindow::Init() {
CreateActions();
CreateRecentGameActions();
ConfigureGuiFromSettings();
LoadTranslation();
CreateDockWindows();
CreateConnects();
SetLastUsedTheme();
SetLastIconSizeBullet();
GetPhysicalDevices();
LoadTranslation();
// show ui
setMinimumSize(350, minimumSizeHint().height());
setWindowTitle(QString::fromStdString("shadPS4 v" + std::string(Common::VERSION)));
@ -89,6 +89,7 @@ void MainWindow::AddUiWidgets() {
ui->toolBar->addWidget(ui->playButton);
ui->toolBar->addWidget(ui->pauseButton);
ui->toolBar->addWidget(ui->stopButton);
ui->toolBar->addWidget(ui->refreshButton);
ui->toolBar->addWidget(ui->settingsButton);
auto connection = QObject::connect(ui->controllerButton, &QPushButton::clicked, this,
&MainWindow::ControllerConfigurationButtonPressed);
@ -111,7 +112,7 @@ void MainWindow::CreateDockWindows() {
QWidget* phCentralWidget = new QWidget(this);
setCentralWidget(phCentralWidget);
m_dock_widget.reset(new QDockWidget("Game List", this));
m_dock_widget.reset(new QDockWidget(tr("Game List"), this));
m_game_list_frame.reset(new GameListFrame(m_game_info, this));
m_game_list_frame->setObjectName("gamelist");
m_game_grid_frame.reset(new GameGridFrame(m_game_info, this));
@ -185,6 +186,7 @@ void MainWindow::CreateConnects() {
connect(ui->mw_searchbar, &QLineEdit::textChanged, this, &MainWindow::SearchGameTable);
connect(ui->exitAct, &QAction::triggered, this, &QWidget::close);
connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable);
connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable);
connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList);
connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable);
@ -583,7 +585,7 @@ void MainWindow::SaveWindowState() const {
void MainWindow::InstallPkg() {
QFileDialog dialog;
dialog.setFileMode(QFileDialog::ExistingFiles);
dialog.setNameFilter(tr("PKG File (*.PKG)"));
dialog.setNameFilter(tr("PKG File (*.PKG *.pkg)"));
if (dialog.exec()) {
QStringList fileNames = dialog.selectedFiles();
int nPkg = fileNames.size();
@ -864,6 +866,7 @@ void MainWindow::SetUiIcons(bool isWhite) {
ui->playButton->setIcon(RecolorIcon(ui->playButton->icon(), isWhite));
ui->pauseButton->setIcon(RecolorIcon(ui->pauseButton->icon(), isWhite));
ui->stopButton->setIcon(RecolorIcon(ui->stopButton->icon(), isWhite));
ui->refreshButton->setIcon(RecolorIcon(ui->refreshButton->icon(), isWhite));
ui->settingsButton->setIcon(RecolorIcon(ui->settingsButton->icon(), isWhite));
ui->controllerButton->setIcon(RecolorIcon(ui->controllerButton->icon(), isWhite));
ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite));
@ -958,4 +961,4 @@ void MainWindow::OnLanguageChanged(const std::string& locale) {
Config::setEmulatorLanguage(locale);
LoadTranslation();
}
}

View file

@ -8,13 +8,13 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
switch (theme) {
case Theme::Dark:
mw_searchbar->setStyleSheet("background-color: #1e1e1e; /* Dark background */"
"color: #ffffff; /* White text */"
"border: 1px solid #ffffff; /* White border */"
mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background
"color: #ffffff;" // White text
"border: 2px solid #ffffff;" // White border
"padding: 5px;");
themePalette.setColor(QPalette::Window, QColor(53, 53, 53));
themePalette.setColor(QPalette::Window, QColor(50, 50, 50));
themePalette.setColor(QPalette::WindowText, Qt::white);
themePalette.setColor(QPalette::Base, QColor(25, 25, 25));
themePalette.setColor(QPalette::Base, QColor(20, 20, 20));
themePalette.setColor(QPalette::AlternateBase, QColor(25, 25, 25));
themePalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
themePalette.setColor(QPalette::ToolTipBase, Qt::white);
@ -30,8 +30,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
break;
case Theme::Light:
mw_searchbar->setStyleSheet("background-color: #ffffff; /* Light gray background */"
"color: #000000; /* Black text */"
mw_searchbar->setStyleSheet("background-color: #ffffff;" // Light gray background
"color: #000000;" // Black text
"border: 2px solid #000000;" // Black border
"padding: 5px;");
themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray
themePalette.setColor(QPalette::WindowText, Qt::black); // Black
@ -49,9 +50,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
break;
case Theme::Green:
mw_searchbar->setStyleSheet("background-color: #354535; /* Dark green background */"
"color: #ffffff; /* White text */"
"border: 1px solid #ffffff; /* White border */"
mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background
"color: #ffffff;" // White text
"border: 2px solid #ffffff;" // White border
"padding: 5px;");
themePalette.setColor(QPalette::Window, QColor(53, 69, 53)); // Dark green background
themePalette.setColor(QPalette::WindowText, Qt::white); // White text
@ -72,9 +73,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
break;
case Theme::Blue:
mw_searchbar->setStyleSheet("background-color: #283c5a; /* Dark blue background */"
"color: #ffffff; /* White text */"
"border: 1px solid #ffffff; /* White border */"
mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background
"color: #ffffff;" // White text
"border: 2px solid #ffffff;" // White border
"padding: 5px;");
themePalette.setColor(QPalette::Window, QColor(40, 60, 90)); // Dark blue background
themePalette.setColor(QPalette::WindowText, Qt::white); // White text
@ -95,9 +96,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
break;
case Theme::Violet:
mw_searchbar->setStyleSheet("background-color: #643278; /* Violet background */"
"color: #ffffff; /* White text */"
"border: 1px solid #ffffff; /* White border */"
mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background
"color: #ffffff;" // White text
"border: 2px solid #ffffff;" // White border
"padding: 5px;");
themePalette.setColor(QPalette::Window, QColor(100, 50, 120)); // Violet background
themePalette.setColor(QPalette::WindowText, Qt::white); // White text

View file

@ -38,6 +38,7 @@ public:
QPushButton* playButton;
QPushButton* pauseButton;
QPushButton* stopButton;
QPushButton* refreshButton;
QPushButton* settingsButton;
QPushButton* controllerButton;
@ -176,6 +177,10 @@ public:
stopButton->setFlat(true);
stopButton->setIcon(QIcon(":images/stop_icon.png"));
stopButton->setIconSize(QSize(40, 40));
refreshButton = new QPushButton(centralWidget);
refreshButton->setFlat(true);
refreshButton->setIcon(QIcon(":images/refresh_icon.png"));
refreshButton->setIconSize(QSize(32, 32));
settingsButton = new QPushButton(centralWidget);
settingsButton->setFlat(true);
settingsButton->setIcon(QIcon(":images/settings_icon.png"));
@ -262,8 +267,8 @@ public:
menuView->addAction(menuGame_List_Mode->menuAction());
menuView->addAction(menuGame_List_Icons->menuAction());
menuView->addAction(menuThemes->menuAction());
menuThemes->addAction(setThemeLight);
menuThemes->addAction(setThemeDark);
menuThemes->addAction(setThemeLight);
menuThemes->addAction(setThemeGreen);
menuThemes->addAction(setThemeBlue);
menuThemes->addAction(setThemeViolet);

View file

@ -80,9 +80,13 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
}
});
connect(ui->tabWidgetSettings, &QTabWidget::currentChanged, this, [this]() {
ui->buttonBox->button(QDialogButtonBox::StandardButton::Close)->setFocus();
});
ui->buttonBox->button(QDialogButtonBox::Save)->setText(tr("Save"));
ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Apply"));
ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults"));
ui->buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close"));
connect(ui->tabWidgetSettings, &QTabWidget::currentChanged, this,
[this]() { ui->buttonBox->button(QDialogButtonBox::Close)->setFocus(); });
// GENERAL TAB
{

View file

@ -502,6 +502,11 @@
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../main_window.cpp" line="106"/>
<source>Game List</source>
<translation>ققائمة الألعاب</translation>
</message>
<message>
<location filename="../main_window.cpp" line="168"/>
<source> * Unsupported Vulkan Version</source>
@ -864,7 +869,7 @@
<message>
<location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source>
<translation>تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش.</translation>
<translation>تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. إذا لم يظهر التحديث، قد يكون السبب أنه غير متوفر للإصدار وسيريال اللعبة المحدد. قد تحتاج إلى تحديث اللعبة.</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="773"/>
@ -911,5 +916,76 @@
<source>Name:</source>
<translation>:الاسم</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source>
<translation>لا يمكن تطبيق الغش قبل بدء اللعبة.</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings_dialog.cpp" line="83"/>
<source>Save</source>
<translation>حفظ</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="84"/>
<source>Apply</source>
<translation>تطبيق</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="85"/>
<source>Restore Defaults</source>
<translation>استعادة الإعدادات الافتراضية</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="86"/>
<source>Close</source>
<translation>إغلاق</translation>
</message>
</context>
<context>
<name>GameListFrame</name>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Icon</source>
<translation>أيقونة</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Name</source>
<translation>اسم</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Serial</source>
<translation>سيريال</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
<translation>منطقة</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Firmware</source>
<translation>البرمجيات الثابتة</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Size</source>
<translation>حجم</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Version</source>
<translation>إصدار</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Path</source>
<translation>مسار</translation>
</message>
</context>
</TS>

View file

@ -500,6 +500,11 @@
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../main_window.cpp" line="106"/>
<source>Game List</source>
<translation>Spiloversigt</translation>
</message>
<message>
<location filename="../main_window.cpp" line="168"/>
<source> * Unsupported Vulkan Version</source>
@ -851,7 +856,7 @@
<message>
<location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source>
<translation>Patcher hentet med succes! Alle patches til alle spil er blevet hentet, der er ikke behov for at hente dem individuelt for hvert spil, som det sker med snyd.</translation>
<translation>Patcher hentet med succes! Alle patches til alle spil er blevet hentet, der er ikke behov for at hente dem individuelt for hvert spil, som det sker med snyd. Hvis opdateringen ikke vises, kan det være, at den ikke findes for den specifikke serie og version af spillet. Det kan være nødvendigt at opdatere spillet.</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="773"/>
@ -898,5 +903,76 @@
<source>Name:</source>
<translation>Navn:</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source>
<translation>Kan ikke anvende snyd før spillet er startet.</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings_dialog.cpp" line="83"/>
<source>Save</source>
<translation>Gem</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="84"/>
<source>Apply</source>
<translation>Anvend</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="85"/>
<source>Restore Defaults</source>
<translation>Gendan standardindstillinger</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="86"/>
<source>Close</source>
<translation>Luk</translation>
</message>
</context>
<context>
<name>GameListFrame</name>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Icon</source>
<translation>Ikon</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Name</source>
<translation>Navn</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Serial</source>
<translation>Seriel</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
<translation>Region</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Firmware</source>
<translation>Firmware</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Size</source>
<translation>Størrelse</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Version</source>
<translation>Version</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Path</source>
<translation>Sti</translation>
</message>
</context>
</TS>

View file

@ -500,6 +500,11 @@
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../main_window.cpp" line="106"/>
<source>Game List</source>
<translation>Spieleliste</translation>
</message>
<message>
<location filename="../main_window.cpp" line="168"/>
<source> * Unsupported Vulkan Version</source>
@ -851,7 +856,7 @@
<message>
<location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source>
<translation>Patches erfolgreich heruntergeladen! Alle Patches für alle Spiele wurden heruntergeladen, es ist nicht notwendig, sie einzeln für jedes Spiel herunterzuladen, wie es bei Cheats der Fall ist.</translation>
<translation>Patches erfolgreich heruntergeladen! Alle Patches für alle Spiele wurden heruntergeladen, es ist nicht notwendig, sie einzeln für jedes Spiel herunterzuladen, wie es bei Cheats der Fall ist. Wenn der Patch nicht angezeigt wird, könnte es sein, dass er für die spezifische Seriennummer und Version des Spiels nicht existiert. Möglicherweise müssen Sie das Spiel aktualisieren.</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="773"/>
@ -898,5 +903,76 @@
<source>Name:</source>
<translation>Name:</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source>
<translation>Kann keine Cheats anwenden, bevor das Spiel gestartet ist.</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings_dialog.cpp" line="83"/>
<source>Save</source>
<translation>Speichern</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="84"/>
<source>Apply</source>
<translation>Übernehmen</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="85"/>
<source>Restore Defaults</source>
<translation>Werkseinstellungen wiederherstellen</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="86"/>
<source>Close</source>
<translation>Schließen</translation>
</message>
</context>
<context>
<name>GameListFrame</name>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Icon</source>
<translation>Symbol</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Name</source>
<translation>Name</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Serial</source>
<translation>Seriennummer</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
<translation>Region</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Firmware</source>
<translation>Firmware</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Size</source>
<translation>Größe</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Version</source>
<translation>Version</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Path</source>
<translation>Pfad</translation>
</message>
</context>
</TS>

View file

@ -500,6 +500,11 @@
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../main_window.cpp" line="106"/>
<source>Game List</source>
<translation>Λίστα παιχνιδιών</translation>
</message>
<message>
<location filename="../main_window.cpp" line="168"/>
<source> * Unsupported Vulkan Version</source>
@ -851,7 +856,7 @@
<message>
<location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source>
<translation>Τα Patches κατεβάστηκαν επιτυχώς! Όλα τα Patches για όλα τα παιχνίδια έχουν κατέβει, δεν είναι απαραίτητο να τα κατεβάσετε ένα-ένα για κάθε παιχνίδι, όπως με τα Cheats.</translation>
<translation>Τα Patches κατεβάστηκαν επιτυχώς! Όλα τα Patches για όλα τα παιχνίδια έχουν κατέβει, δεν είναι απαραίτητο να τα κατεβάσετε ένα-ένα για κάθε παιχνίδι, όπως με τα Cheats. Εάν η ενημέρωση δεν εμφανίζεται, μπορεί να μην υπάρχει για τον συγκεκριμένο σειριακό αριθμό και έκδοση του παιχνιδιού. Μπορεί να χρειαστεί να ενημερώσετε το παιχνίδι.</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="773"/>
@ -898,5 +903,76 @@
<source>Name:</source>
<translation>Όνομα:</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source>
<translation>Δεν μπορείτε να εφαρμόσετε cheats πριν ξεκινήσει το παιχνίδι.</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings_dialog.cpp" line="83"/>
<source>Save</source>
<translation>Αποθήκευση</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="84"/>
<source>Apply</source>
<translation>Εφαρμογή</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="85"/>
<source>Restore Defaults</source>
<translation>Επαναφορά Προεπιλογών</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="86"/>
<source>Close</source>
<translation>Κλείσιμο</translation>
</message>
</context>
<context>
<name>GameListFrame</name>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Icon</source>
<translation>Εικονίδιο</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Name</source>
<translation>Όνομα</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Serial</source>
<translation>Σειριακός αριθμός</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
<translation>Περιοχή</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Firmware</source>
<translation>Λογισμικό</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Size</source>
<translation>Μέγεθος</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Version</source>
<translation>Έκδοση</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Path</source>
<translation>Διαδρομή</translation>
</message>
</context>
</TS>

View file

@ -500,6 +500,11 @@
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../main_window.cpp" line="106"/>
<source>Game List</source>
<translation>Game List</translation>
</message>
<message>
<location filename="../main_window.cpp" line="168"/>
<source> * Unsupported Vulkan Version</source>
@ -851,7 +856,7 @@
<message>
<location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source>
<translation>Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats.</translation>
<translation>Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. It may be necessary to update the game.</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="773"/>
@ -898,5 +903,76 @@
<source>Name:</source>
<translation>Name:</translation>
</message>
</context>
<message>
<location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source>
<translation>Can't apply cheats before the game is started.</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings_dialog.cpp" line="83"/>
<source>Save</source>
<translation>Save</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="84"/>
<source>Apply</source>
<translation>Apply</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="85"/>
<source>Restore Defaults</source>
<translation>Restore Defaults</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="86"/>
<source>Close</source>
<translation>Close</translation>
</message>
</context>
<context>
<name>GameListFrame</name>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Icon</source>
<translation>Icon</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Name</source>
<translation>Name</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Serial</source>
<translation>Serial</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
<translation>Region</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Firmware</source>
<translation>Firmware</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Size</source>
<translation>Size</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Version</source>
<translation>Version</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Path</source>
<translation>Path</translation>
</message>
</context>
</TS>

View file

@ -500,6 +500,11 @@
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../main_window.cpp" line="106"/>
<source>Game List</source>
<translation>Lista de juegos</translation>
</message>
<message>
<location filename="../main_window.cpp" line="168"/>
<source> * Unsupported Vulkan Version</source>
@ -851,7 +856,7 @@
<message>
<location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source>
<translation>¡Parches descargados exitosamente! Todos los parches disponibles para todos los juegos han sido descargados, no es necesario descargarlos individualmente para cada juego como ocurre con los trucos.</translation>
<translation>¡Parches descargados exitosamente! Todos los parches disponibles para todos los juegos han sido descargados, no es necesario descargarlos individualmente para cada juego como ocurre con los trucos. Si el parche no aparece, puede ser que no exista para el número de serie y versión específicos del juego. Puede ser necesario actualizar el juego.</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="773"/>
@ -898,5 +903,76 @@
<source>Name:</source>
<translation>Nombre:</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source>
<translation>No se pueden aplicar trucos antes de que se inicie el juego.</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings_dialog.cpp" line="83"/>
<source>Save</source>
<translation>Guardar</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="84"/>
<source>Apply</source>
<translation>Aplicar</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="85"/>
<source>Restore Defaults</source>
<translation>Restaurar Valores Predeterminados</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="86"/>
<source>Close</source>
<translation>Cerrar</translation>
</message>
</context>
<context>
<name>GameListFrame</name>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Icon</source>
<translation>Ícono</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Name</source>
<translation>Nombre</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Serial</source>
<translation>Serie</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
<translation>Región</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Firmware</source>
<translation>Firmware</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Size</source>
<translation>Tamaño</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Version</source>
<translation>Versión</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Path</source>
<translation>Ruta</translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load diff

View file

@ -500,6 +500,11 @@
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../main_window.cpp" line="106"/>
<source>Game List</source>
<translation>Pelilista</translation>
</message>
<message>
<location filename="../main_window.cpp" line="168"/>
<source> * Unsupported Vulkan Version</source>
@ -851,7 +856,7 @@
<message>
<location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source>
<translation>Korjaukset ladattu onnistuneesti! Kaikki saatavilla olevat korjaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille kuten huijauksissa.</translation>
<translation>Korjaukset ladattu onnistuneesti! Kaikki saatavilla olevat korjaukset kaikille peleille on ladattu, eikä niitä tarvitse ladata yksittäin jokaiselle pelille kuten huijauksissa. Jos päivitystä ei näy, se saattaa olla, että sitä ei ole saatavilla tietylle sarjanumerolle ja peliversiolle. Saattaa olla tarpeen päivittää peli.</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="773"/>
@ -898,5 +903,76 @@
<source>Name:</source>
<translation>Nimi:</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source>
<translation>Ei voi käyttää huijauksia ennen kuin peli on aloitettu.</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings_dialog.cpp" line="83"/>
<source>Save</source>
<translation>Tallenna</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="84"/>
<source>Apply</source>
<translation>Ota käyttöön</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="85"/>
<source>Restore Defaults</source>
<translation>Palauta oletukset</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="86"/>
<source>Close</source>
<translation>Sulje</translation>
</message>
</context>
<context>
<name>GameListFrame</name>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Icon</source>
<translation>Ikoni</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Name</source>
<translation>Nimi</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Serial</source>
<translation>Sarjanumero</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
<translation>Alue</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Firmware</source>
<translation>Ohjelmisto</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Size</source>
<translation>Koko</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Version</source>
<translation>Versio</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Path</source>
<translation>Polku</translation>
</message>
</context>
</TS>

View file

@ -21,7 +21,7 @@
<message>
<location filename="../about_dialog.ui" line="99"/>
<source>This software should not be used to play games you have not legally obtained.</source>
<translation>Ce logiciel ne doit pas être utilisé pour jouer à des jeux que vous n&apos;avez pas obtenus légalement.</translation>
<translation>Ce logiciel ne doit pas être utilisé pour jouer à des jeux que vous n'avez pas obtenus légalement.</translation>
</message>
</context>
<context>
@ -60,7 +60,7 @@
<message>
<location filename="../game_install_dialog.cpp" line="31"/>
<source>Directory to install games</source>
<translation>Répertoire d&apos;installation des jeux</translation>
<translation>Répertoire d'installation des jeux</translation>
</message>
<message>
<location filename="../game_install_dialog.cpp" line="50"/>
@ -75,7 +75,7 @@
<message>
<location filename="../game_install_dialog.cpp" line="75"/>
<source>The value for location to install games is not valid.</source>
<translation>Le répertoire d'installation des jeux n&apos;est pas valide.</translation>
<translation>Le répertoire d'installation des jeux n'est pas valide.</translation>
</message>
</context>
<context>
@ -118,7 +118,7 @@
<message>
<location filename="../gui_context_menus.h" line="61"/>
<source>Copy Serial</source>
<translation>Copier le numéro de série</translation>
<translation>Copier le N° de série</translation>
</message>
<message>
<location filename="../gui_context_menus.h" line="62"/>
@ -201,7 +201,7 @@
<message>
<location filename="../main_window_ui.h" line="327"/>
<source>Exit the application.</source>
<translation>Fermer l&apos;application.</translation>
<translation>Fermer l'application.</translation>
</message>
<message>
<location filename="../main_window_ui.h" line="330"/>
@ -291,7 +291,7 @@
<message>
<location filename="../main_window_ui.h" line="354"/>
<source>Game List Mode</source>
<translation>Mode d&apos;affichage</translation>
<translation>Mode d'affichage</translation>
</message>
<message>
<location filename="../main_window_ui.h" line="355"/>
@ -301,7 +301,7 @@
<message>
<location filename="../main_window_ui.h" line="356"/>
<source>Utils</source>
<translation>Utilitaire</translation>
<translation>Utilitaires</translation>
</message>
<message>
<location filename="../main_window_ui.h" line="357"/>
@ -316,12 +316,12 @@
<message>
<location filename="../main_window_ui.h" line="359"/>
<source>Dark</source>
<translation>Noir</translation>
<translation>Sombre</translation>
</message>
<message>
<location filename="../main_window_ui.h" line="360"/>
<source>Light</source>
<translation>Blanc</translation>
<translation>Clair</translation>
</message>
<message>
<location filename="../main_window_ui.h" line="361"/>
@ -341,7 +341,7 @@
<message>
<location filename="../main_window_ui.h" line="364"/>
<source>toolBar</source>
<translation>Bare d&apos;outils</translation>
<translation>Bare d'outils</translation>
</message>
</context>
<context>
@ -385,7 +385,7 @@
<message>
<location filename="../settings_dialog.ui" line="95"/>
<source>Emulator Language</source>
<translation>Langage de l&apos;émulateur</translation>
<translation>Langage de l'émulateur</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="114"/>
@ -400,7 +400,7 @@
<message>
<location filename="../settings_dialog.ui" line="129"/>
<source>Show Splash</source>
<translation>Afficher l&apos;image du jeu</translation>
<translation>Afficher l'image du jeu</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="136"/>
@ -410,7 +410,7 @@
<message>
<location filename="../settings_dialog.ui" line="155"/>
<source>Username</source>
<translation>Nom d&apos;utilisateur</translation>
<translation>Nom d'utilisateur</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
@ -500,6 +500,11 @@
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../main_window.cpp" line="106"/>
<source>Game List</source>
<translation>Liste de jeux</translation>
</message>
<message>
<location filename="../main_window.cpp" line="168"/>
<source> * Unsupported Vulkan Version</source>
@ -533,7 +538,7 @@
<message>
<location filename="../main_window.cpp" line="392"/>
<source>All Patches available for all games have been downloaded.</source>
<translation>Tous les patchs disponibles pour les jeux ont é téléchargés.</translation>
<translation>Tous les patchs disponibles ont é téléchargés.</translation>
</message>
<message>
<location filename="../main_window.cpp" line="549"/>
@ -851,7 +856,7 @@
<message>
<location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source>
<translation>Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont é téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats.</translation>
<translation>Patchs téléchargés avec succès ! Tous les patches disponibles pour tous les jeux ont é téléchargés, il n'est pas nécessaire de les télécharger individuellement pour chaque jeu comme c'est le cas pour les Cheats. Si le correctif n'apparaît pas, il se peut qu'il n'existe pas pour le numéro de série et la version spécifiques du jeu. Il peut être nécessaire de mettre à jour le jeu.</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="773"/>
@ -898,5 +903,76 @@
<source>Name:</source>
<translation>Nom :</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source>
<translation>Impossible d'appliquer les Cheats avant que le jeu ne commence.</translation>
</message>
</context>
</TS>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings_dialog.cpp" line="83"/>
<source>Save</source>
<translation>Enregistrer</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="84"/>
<source>Apply</source>
<translation>Appliquer</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="85"/>
<source>Restore Defaults</source>
<translation>Restaurer les paramètres par défaut</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="86"/>
<source>Close</source>
<translation>Fermer</translation>
</message>
</context>
<context>
<name>GameListFrame</name>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Icon</source>
<translation>Icône</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Name</source>
<translation>Nom</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Serial</source>
<translation>Série</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
<translation>Région</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Firmware</source>
<translation>Firmware</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Size</source>
<translation>Taille</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Version</source>
<translation>Version</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Path</source>
<translation>Répertoire</translation>
</message>
</context>
</TS>

View file

@ -500,6 +500,11 @@
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../main_window.cpp" line="106"/>
<source>Game List</source>
<translation>Játéklista</translation>
</message>
<message>
<location filename="../main_window.cpp" line="168"/>
<source> * Unsupported Vulkan Version</source>
@ -851,7 +856,7 @@
<message>
<location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source>
<translation>Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében.</translation>
<translation>Frissítések sikeresen letöltve! Minden elérhető frissítés letöltésre került, nem szükséges egyesével letölteni őket minden játékhoz, mint a csalások esetében. Ha a javítás nem jelenik meg, lehet, hogy nem létezik a játék adott sorozatszámához és verziójához. Lehet, hogy frissítenie kell a játékot.</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="773"/>
@ -898,5 +903,76 @@
<source>Name:</source>
<translation>Név:</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source>
<translation>Nem lehet csalásokat alkalmazni, mielőtt a játék elindul.</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings_dialog.cpp" line="83"/>
<source>Save</source>
<translation>Mentés</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="84"/>
<source>Apply</source>
<translation>Alkalmaz</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="85"/>
<source>Restore Defaults</source>
<translation>Alapértelmezett értékek visszaállítása</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="86"/>
<source>Close</source>
<translation>Bezárás</translation>
</message>
</context>
<context>
<name>GameListFrame</name>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Icon</source>
<translation>Ikon</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Name</source>
<translation>Név</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Serial</source>
<translation>Sorozatszám</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
<translation>Régió</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Firmware</source>
<translation>Firmware</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Size</source>
<translation>Méret</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Version</source>
<translation>Verzió</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Path</source>
<translation>Útvonal</translation>
</message>
</context>
</TS>

View file

@ -500,6 +500,11 @@
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../main_window.cpp" line="106"/>
<source>Game List</source>
<translation>Daftar game</translation>
</message>
<message>
<location filename="../main_window.cpp" line="168"/>
<source> * Unsupported Vulkan Version</source>
@ -851,7 +856,7 @@
<message>
<location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source>
<translation>Patch Berhasil Diunduh! Semua Patch yang tersedia untuk semua game telah diunduh, tidak perlu mengunduhnya satu per satu seperti yang terjadi pada Cheat.</translation>
<translation>Patch Berhasil Diunduh! Semua Patch yang tersedia untuk semua game telah diunduh, tidak perlu mengunduhnya satu per satu seperti yang terjadi pada Cheat. Jika patch tidak muncul, mungkin patch tersebut tidak ada untuk nomor seri dan versi game yang spesifik. Mungkin perlu memperbarui game.</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="773"/>
@ -898,5 +903,76 @@
<source>Name:</source>
<translation>Nama:</translation>
</message>
<message>
<location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source>
<translation>Tidak bisa menerapkan cheat sebelum permainan dimulai.</translation>
</message>
</context>
<context>
<name>SettingsDialog</name>
<message>
<location filename="../settings_dialog.cpp" line="83"/>
<source>Save</source>
<translation>Simpan</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="84"/>
<source>Apply</source>
<translation>Terapkan</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="85"/>
<source>Restore Defaults</source>
<translation>Kembalikan Pengaturan Default</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="86"/>
<source>Close</source>
<translation>Tutup</translation>
</message>
</context>
<context>
<name>GameListFrame</name>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Icon</source>
<translation>Ikon</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Name</source>
<translation>Nama</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Serial</source>
<translation>Serial</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Region</source>
<translation>Wilayah</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="34"/>
<source>Firmware</source>
<translation>Firmware</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Size</source>
<translation>Ukuran</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Version</source>
<translation>Versi</translation>
</message>
<message>
<location filename="../game_list_frame.cpp" line="35"/>
<source>Path</source>
<translation>Jalur</translation>
</message>
</context>
</TS>

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