diff --git a/.github/ISSUE_TEMPLATE/app-bug-report.yaml b/.github/ISSUE_TEMPLATE/app-bug-report.yaml index cd540e06e..8959221a0 100644 --- a/.github/ISSUE_TEMPLATE/app-bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/app-bug-report.yaml @@ -53,3 +53,24 @@ body: placeholder: "Example: Windows 11, Arch Linux, MacOS 15" validations: required: true + - type: input + id: cpu + attributes: + label: CPU + placeholder: "Example: Intel Core i7-8700" + validations: + required: true + - type: input + id: gpu + attributes: + label: GPU + placeholder: "Example: nVidia GTX 1650" + validations: + required: true + - type: input + id: ram + attributes: + label: Amount of RAM in GB + placeholder: "Example: 16 GB" + validations: + required: true diff --git a/.gitmodules b/.gitmodules index 98fba2098..9daefe305 100644 --- a/.gitmodules +++ b/.gitmodules @@ -97,7 +97,7 @@ shallow = true [submodule "externals/MoltenVK/SPIRV-Cross"] path = externals/MoltenVK/SPIRV-Cross - url = https://github.com/KhronosGroup/SPIRV-Cross + url = https://github.com/billhollings/SPIRV-Cross shallow = true [submodule "externals/MoltenVK/MoltenVK"] path = externals/MoltenVK/MoltenVK @@ -107,3 +107,6 @@ path = externals/MoltenVK/cereal url = https://github.com/USCiLab/cereal shallow = true +[submodule "externals/libusb"] + path = externals/libusb + url = https://github.com/libusb/libusb-cmake.git diff --git a/CMakeLists.txt b/CMakeLists.txt index bd79c12d0..a7791dc7e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) if(APPLE) list(APPEND ADDITIONAL_LANGUAGES OBJC) - set(CMAKE_OSX_DEPLOYMENT_TARGET 14) + # Starting with 15.4, Rosetta 2 has support for all the necessary instruction sets. + set(CMAKE_OSX_DEPLOYMENT_TARGET 15.4 CACHE STRING "") endif() if (NOT CMAKE_BUILD_TYPE) @@ -53,8 +54,9 @@ else() endif() if (ARCHITECTURE STREQUAL "x86_64") - # Set x86_64 target level to Sandy Bridge to generally match what is supported for PS4 guest code with CPU patches. - add_compile_options(-march=sandybridge) + # Target the same CPU architecture as the PS4, to maintain the same level of compatibility. + # Exclude SSE4a as it is only available on AMD CPUs. + add_compile_options(-march=btver2 -mtune=generic -mno-sse4a) endif() if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") @@ -103,11 +105,8 @@ if (CLANG_FORMAT) unset(CCOMMENT) endif() -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - # generate git revision information -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules/") -include(GetGitRevisionDescription) +include("${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules/GetGitRevisionDescription.cmake") get_git_head_revision(GIT_REF_SPEC GIT_REV) git_describe(GIT_DESC --always --long --dirty) git_branch_name(GIT_BRANCH) @@ -201,10 +200,20 @@ execute_process( OUTPUT_STRIP_TRAILING_WHITESPACE ) +# Set Version +set(EMULATOR_VERSION_MAJOR "0") +set(EMULATOR_VERSION_MINOR "7") +set(EMULATOR_VERSION_PATCH "1") + +set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}") + +set(APP_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH} WIP") +set(APP_IS_RELEASE false) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY) message("end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") find_package(Boost 1.84.0 CONFIG) find_package(FFmpeg 5.1.2 MODULE) find_package(fmt 10.2.0 CONFIG) @@ -217,21 +226,18 @@ find_package(SDL3 3.1.2 CONFIG) find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) -find_package(VulkanHeaders 1.4.305 CONFIG) +find_package(VulkanHeaders 1.4.309 CONFIG) find_package(VulkanMemoryAllocator 3.1.0 CONFIG) find_package(xbyak 7.07 CONFIG) find_package(xxHash 0.8.2 MODULE) find_package(ZLIB 1.3 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) -endif() - +find_package(libusb 1.0.27 MODULE) if (APPLE) find_package(date 3.0.1 CONFIG) endif() +list(POP_BACK CMAKE_MODULE_PATH) # Note: Windows always has these functions through winpthreads include(CheckSymbolExists) @@ -451,8 +457,6 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp 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 src/core/libraries/game_live_streaming/gamelivestreaming.cpp src/core/libraries/game_live_streaming/gamelivestreaming.h src/core/libraries/remote_play/remoteplay.cpp @@ -570,6 +574,8 @@ set(NP_LIBS src/core/libraries/np_common/np_common.cpp src/core/libraries/np_web_api/np_web_api.h src/core/libraries/np_party/np_party.cpp src/core/libraries/np_party/np_party.h + src/core/libraries/np_auth/np_auth.cpp + src/core/libraries/np_auth/np_auth.h ) set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp @@ -585,6 +591,8 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp src/core/libraries/screenshot/screenshot.h src/core/libraries/move/move.cpp src/core/libraries/move/move.h + src/core/libraries/ulobjmgr/ulobjmgr.cpp + src/core/libraries/ulobjmgr/ulobjmgr.h ) set(DEV_TOOLS src/core/devtools/layer.cpp @@ -675,7 +683,6 @@ set(COMMON src/common/logging/backend.cpp src/common/uint128.h src/common/unique_function.h src/common/va_ctx.h - src/common/version.h src/common/ntapi.h src/common/ntapi.cpp src/common/number_utils.h @@ -1065,7 +1072,7 @@ endif() create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb) 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") @@ -1176,7 +1183,7 @@ target_include_directories(shadps4 PRIVATE ${HOST_SHADERS_INCLUDE}) # embed resources -include(CMakeRC) +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CMakeRC.cmake") cmrc_add_resource_library(embedded-resources ALIAS res::embedded NAMESPACE res @@ -1197,8 +1204,8 @@ if (ENABLE_QT_GUI) MACOSX_BUNDLE ON MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in" MACOSX_BUNDLE_ICON_FILE "shadPS4.icns" - MACOSX_BUNDLE_SHORT_VERSION_STRING "0.4.1" - ) + MACOSX_BUNDLE_SHORT_VERSION_STRING "${APP_VERSION}" + ) set_source_files_properties(src/images/shadPS4.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) diff --git a/README.md b/README.md index 9259ab875..985bba586 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ SPDX-License-Identifier: GPL-2.0-or-later

- + @@ -71,7 +71,7 @@ Check the build instructions for [**Linux**](https://github.com/shadps4-emu/shad Check the build instructions for [**macOS**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-macos.md). > [!IMPORTANT] -> macOS users need at least macOS 15 on Apple Silicon-based Mac devices and at least macOS 14 on Intel-based Mac devices. +> macOS users need at least macOS 15.4 to run shadPS4. Due to GPU issues there are currently heavy bugs on Intel Macs. # Debugging and reporting issues @@ -122,19 +122,37 @@ R3 | M | Keyboard and mouse inputs can be customized in the settings menu by clicking the Controller button, and further details and help on controls are also found there. Custom bindings are saved per-game. Inputs support up to three keys per binding, mouse buttons, mouse movement mapped to joystick input, and more. +# Firmware files + +shadPS4 can load some PlayStation 4 firmware files, these must be dumped from your legally owned PlayStation 4 console.\ +The following firmware modules are supported and must be placed in shadPS4's `user/sys_modules` folder. + +
+ +| Modules | Modules | Modules | Modules | +|-------------------------|-------------------------|-------------------------|-------------------------| +| libSceCesCs.sprx | libSceFont.sprx | libSceFontFt.sprx | libSceFreeTypeOt.sprx | +| libSceJson.sprx | libSceJson2.sprx | libSceLibcInternal.sprx | libSceNgs2.sprx | +| libSceRtc.sprx | libSceUlt.sprx | | | + +
+ +> [!Caution] +> The above modules are required to run the games properly and must be extracted from your PlayStation 4.\ +> **We do not provide any information or support on how to do this**. + + + # Main team - [**georgemoralis**](https://github.com/georgemoralis) -- [**raphaelthegreat**](https://github.com/raphaelthegreat) - [**psucien**](https://github.com/psucien) -- [**skmp**](https://github.com/skmp) -- [**wheremyfoodat**](https://github.com/wheremyfoodat) -- [**raziel1000**](https://github.com/raziel1000) - [**viniciuslrangel**](https://github.com/viniciuslrangel) - [**roamic**](https://github.com/vladmikhalin) -- [**poly**](https://github.com/polybiusproxy) - [**squidbus**](https://github.com/squidbus) - [**frodo**](https://github.com/baggins183) +- [**Stephen Miller**](https://github.com/StevenMiller123) +- [**kalaposfos13**](https://github.com/kalaposfos13) Logo is done by [**Xphalnos**](https://github.com/Xphalnos) @@ -145,11 +163,11 @@ Open a PR and we'll check it :) # Translations -If you want to translate shadPS4 to your language we use [**crowdin**](https://crowdin.com/project/shadps4-emulator). +If you want to translate shadPS4 to your language we use [**Crowdin**](https://crowdin.com/project/shadps4-emulator). # Contributors
- + diff --git a/REUSE.toml b/REUSE.toml index ad2bc3678..662987611 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -57,11 +57,18 @@ path = [ "src/images/utils_icon.png", "src/images/shadPS4.icns", "src/images/shadps4.ico", + "src/images/shadps4.png", "src/images/net.shadps4.shadPS4.svg", "src/images/themes_icon.png", "src/images/update_icon.png", "src/images/youtube.png", "src/images/website.png", + "src/images/discord.svg", + "src/images/github.svg", + "src/images/ko-fi.svg", + "src/images/shadps4.svg", + "src/images/website.svg", + "src/images/youtube.svg", "src/shadps4.qrc", "src/shadps4.rc", "src/qt_gui/translations/update_translation.sh", diff --git a/cmake/Findcryptopp.cmake b/cmake/Findcryptopp.cmake deleted file mode 100644 index d57c0bc54..000000000 --- a/cmake/Findcryptopp.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -# SPDX-License-Identifier: GPL-2.0-or-later - -find_package(PkgConfig QUIET) -pkg_search_module(CRYPTOPP QUIET IMPORTED_TARGET libcryptopp) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(cryptopp - REQUIRED_VARS CRYPTOPP_LINK_LIBRARIES - VERSION_VAR CRYPTOPP_VERSION -) - -if (cryptopp_FOUND AND NOT TARGET cryptopp::cryptopp) - add_library(cryptopp::cryptopp ALIAS PkgConfig::CRYPTOPP) -endif() diff --git a/cmake/Findlibusb.cmake b/cmake/Findlibusb.cmake new file mode 100644 index 000000000..c8b35e979 --- /dev/null +++ b/cmake/Findlibusb.cmake @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +find_package(PkgConfig QUIET) +pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(libusb + REQUIRED_VARS LIBUSB_LINK_LIBRARIES + VERSION_VAR LIBUSB_VERSION +) + +if (libusb_FOUND AND NOT TARGET libusb::usb) + add_library(libusb::usb ALIAS PkgConfig::LIBUSB) +endif() diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md index 9c6bc5a6f..62df95e71 100644 --- a/documents/Quickstart/Quickstart.md +++ b/documents/Quickstart/Quickstart.md @@ -24,13 +24,13 @@ SPDX-License-Identifier: GPL-2.0-or-later - A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM - **Intel**: Haswell generation or newer - **AMD**: Jaguar generation or newer - - **Apple**: Rosetta 2 on macOS 15 or newer + - **Apple**: Rosetta 2 on macOS 15.4 or newer ### GPU - A graphics card with at least 1GB of VRAM -- Keep your graphics drivers up to date -- Vulkan 1.3 support (required) +- Up-to-date graphics drivers +- Vulkan 1.3 with the `VK_KHR_swapchain` and `VK_KHR_push_descriptor` extensions ### RAM diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index d6bdda023..b92e13795 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -201,6 +201,12 @@ if (NOT TARGET pugixml::pugixml) add_subdirectory(pugixml) endif() +# libusb +if (NOT TARGET libusb::usb) + add_subdirectory(libusb) + add_library(libusb::usb ALIAS usb-1.0) +endif() + # Discord RPC if (ENABLE_DISCORD_RPC) add_subdirectory(discord-rpc) diff --git a/externals/libusb b/externals/libusb new file mode 160000 index 000000000..a63a7e43e --- /dev/null +++ b/externals/libusb @@ -0,0 +1 @@ +Subproject commit a63a7e43e0950a595cf4b98a0eaf4051749ace5f diff --git a/src/common/config.cpp b/src/common/config.cpp index 2657cd12a..111c0cfa9 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -7,10 +7,10 @@ #include // for wstring support #include +#include "common/config.h" +#include "common/logging/formatter.h" #include "common/path_util.h" -#include "config.h" -#include "logging/formatter.h" -#include "version.h" +#include "common/scm_rev.h" namespace toml { template @@ -763,7 +763,7 @@ void load(const std::filesystem::path& path) { logFilter = toml::find_or(general, "logFilter", ""); logType = toml::find_or(general, "logType", "sync"); userName = toml::find_or(general, "userName", "shadPS4"); - if (Common::isRelease) { + if (Common::g_is_release) { updateChannel = toml::find_or(general, "updateChannel", "Release"); } else { updateChannel = toml::find_or(general, "updateChannel", "Nightly"); @@ -1108,7 +1108,7 @@ void setDefaultValues() { logFilter = ""; logType = "sync"; userName = "shadPS4"; - if (Common::isRelease) { + if (Common::g_is_release) { updateChannel = "Release"; } else { updateChannel = "Nightly"; diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index bed7802ed..867d62916 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -101,6 +101,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Ssl2) \ SUB(Lib, SysModule) \ SUB(Lib, Move) \ + SUB(Lib, NpAuth) \ SUB(Lib, NpCommon) \ SUB(Lib, NpManager) \ SUB(Lib, NpScore) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index c07efbc0d..e5714a81a 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -69,6 +69,7 @@ enum class Class : u8 { Lib_Http2, ///< The LibSceHttp2 implementation. Lib_SysModule, ///< The LibSceSysModule implementation Lib_NpCommon, ///< The LibSceNpCommon implementation + Lib_NpAuth, ///< The LibSceNpAuth implementation Lib_NpManager, ///< The LibSceNpManager implementation Lib_NpScore, ///< The LibSceNpScore implementation Lib_NpTrophy, ///< The LibSceNpTrophy implementation diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index 2de04e0be..71c4c2d0a 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in @@ -3,21 +3,17 @@ #include "common/scm_rev.h" -#define GIT_REV "@GIT_REV@" -#define GIT_BRANCH "@GIT_BRANCH@" -#define GIT_DESC "@GIT_DESC@" -#define GIT_REMOTE_NAME "@GIT_REMOTE_NAME@" -#define GIT_REMOTE_URL "@GIT_REMOTE_URL@" -#define BUILD_DATE "@BUILD_DATE@" - namespace Common { -const char g_scm_rev[] = GIT_REV; -const char g_scm_branch[] = GIT_BRANCH; -const char g_scm_desc[] = GIT_DESC; -const char g_scm_remote_name[] = GIT_REMOTE_NAME; -const char g_scm_remote_url[] = GIT_REMOTE_URL; -const char g_scm_date[] = BUILD_DATE; +constexpr char g_version[] = "@APP_VERSION@"; +constexpr bool g_is_release = @APP_IS_RELEASE@; + +constexpr char g_scm_rev[] = "@GIT_REV@"; +constexpr char g_scm_branch[] = "@GIT_BRANCH@"; +constexpr char g_scm_desc[] = "@GIT_DESC@"; +constexpr char g_scm_remote_name[] = "@GIT_REMOTE_NAME@"; +constexpr char g_scm_remote_url[] = "@GIT_REMOTE_URL@"; +constexpr char g_scm_date[] = "@BUILD_DATE@"; } // namespace diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index f38efff42..36b844e94 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h @@ -5,6 +5,9 @@ namespace Common { +extern const char g_version[]; +extern const bool g_is_release; + extern const char g_scm_rev[]; extern const char g_scm_branch[]; extern const char g_scm_desc[]; diff --git a/src/common/version.h b/src/common/version.h deleted file mode 100644 index 652f36e6d..000000000 --- a/src/common/version.h +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -namespace Common { - -constexpr char VERSION[] = "0.7.1 WIP"; -constexpr bool isRelease = false; - -} // namespace Common diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index a9f6c67a8..c8106b270 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -22,10 +22,6 @@ #include #else #include -#ifdef __APPLE__ -#include -#include -#endif #endif using namespace Xbyak::util; @@ -81,538 +77,6 @@ static Xbyak::Address ZydisToXbyakMemoryOperand(const ZydisDecodedOperand& opera return ptr[expression]; } -static u64 ZydisToXbyakImmediateOperand(const ZydisDecodedOperand& operand) { - ASSERT_MSG(operand.type == ZYDIS_OPERAND_TYPE_IMMEDIATE, - "Expected immediate operand, got type: {}", static_cast(operand.type)); - return operand.imm.value.u; -} - -static std::unique_ptr ZydisToXbyakOperand(const ZydisDecodedOperand& operand) { - switch (operand.type) { - case ZYDIS_OPERAND_TYPE_REGISTER: { - return std::make_unique(ZydisToXbyakRegisterOperand(operand)); - } - case ZYDIS_OPERAND_TYPE_MEMORY: { - return std::make_unique(ZydisToXbyakMemoryOperand(operand)); - } - default: - UNREACHABLE_MSG("Unsupported operand type: {}", static_cast(operand.type)); - } -} - -static bool OperandUsesRegister(const Xbyak::Operand* operand, int index) { - if (operand->isREG()) { - return operand->getIdx() == index; - } - if (operand->isMEM()) { - const Xbyak::RegExp& reg_exp = operand->getAddress().getRegExp(); - return reg_exp.getBase().getIdx() == index || reg_exp.getIndex().getIdx() == index; - } - UNREACHABLE_MSG("Unsupported operand kind: {}", static_cast(operand->getKind())); -} - -static bool IsRegisterAllocated( - const std::initializer_list& allocated_registers, const int index) { - return std::ranges::find_if(allocated_registers.begin(), allocated_registers.end(), - [index](const Xbyak::Operand* operand) { - return OperandUsesRegister(operand, index); - }) != allocated_registers.end(); -} - -static Xbyak::Reg AllocateScratchRegister( - const std::initializer_list allocated_registers, const u32 bits) { - for (int index = Xbyak::Operand::R8; index <= Xbyak::Operand::R15; index++) { - if (!IsRegisterAllocated(allocated_registers, index)) { - return Xbyak::Reg32e(index, static_cast(bits)); - } - } - UNREACHABLE_MSG("Out of scratch registers!"); -} - -#ifdef __APPLE__ - -static pthread_key_t stack_pointer_slot; -static pthread_key_t patch_stack_slot; -static std::once_flag patch_context_slots_init_flag; -static constexpr u32 patch_stack_size = 0x1000; - -static_assert(sizeof(void*) == sizeof(u64), - "Cannot fit a register inside a thread local storage slot."); - -static void FreePatchStack(void* patch_stack) { - // Subtract back to the bottom of the stack for free. - std::free(static_cast(patch_stack) - patch_stack_size); -} - -static void InitializePatchContextSlots() { - ASSERT_MSG(pthread_key_create(&stack_pointer_slot, nullptr) == 0, - "Unable to allocate thread-local register for stack pointer."); - ASSERT_MSG(pthread_key_create(&patch_stack_slot, FreePatchStack) == 0, - "Unable to allocate thread-local register for patch stack."); -} - -void InitializeThreadPatchStack() { - std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots); - - pthread_setspecific(patch_stack_slot, - static_cast(std::malloc(patch_stack_size)) + patch_stack_size); -} - -/// Saves the stack pointer to thread local storage and loads the patch stack. -static void SaveStack(Xbyak::CodeGenerator& c) { - std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots); - - // Save original stack pointer and load patch stack. - c.putSeg(gs); - c.mov(qword[reinterpret_cast(stack_pointer_slot * sizeof(void*))], rsp); - c.putSeg(gs); - c.mov(rsp, qword[reinterpret_cast(patch_stack_slot * sizeof(void*))]); -} - -/// Restores the stack pointer from thread local storage. -static void RestoreStack(Xbyak::CodeGenerator& c) { - std::call_once(patch_context_slots_init_flag, InitializePatchContextSlots); - - // Save patch stack pointer and load original stack. - c.putSeg(gs); - c.mov(qword[reinterpret_cast(patch_stack_slot * sizeof(void*))], rsp); - c.putSeg(gs); - c.mov(rsp, qword[reinterpret_cast(stack_pointer_slot * sizeof(void*))]); -} - -/// Validates that the dst register is supported given the SaveStack/RestoreStack implementation. -static void ValidateDst(const Xbyak::Reg& dst) { - // No restrictions. -} - -#else - -void InitializeThreadPatchStack() { - // No-op -} - -// NOTE: Since stack pointer here is subtracted through safe zone and not saved anywhere, -// it must not be modified during the instruction. Otherwise, we will not be able to find -// and load registers back from where they were saved. Thus, a limitation is placed on -// instructions, that they must not use the stack pointer register as a destination. - -/// Saves the stack pointer to thread local storage and loads the patch stack. -static void SaveStack(Xbyak::CodeGenerator& c) { - c.lea(rsp, ptr[rsp - 128]); // red zone -} - -/// Restores the stack pointer from thread local storage. -static void RestoreStack(Xbyak::CodeGenerator& c) { - c.lea(rsp, ptr[rsp + 128]); // red zone -} - -/// Validates that the dst register is supported given the SaveStack/RestoreStack implementation. -static void ValidateDst(const Xbyak::Reg& dst) { - // Stack pointer is not preserved, so it can't be used as a dst. - ASSERT_MSG(dst.getIdx() != rsp.getIdx(), "Stack pointer not supported as destination."); -} - -#endif - -/// Switches to the patch stack, saves registers, and restores the original stack. -static void SaveRegisters(Xbyak::CodeGenerator& c, const std::initializer_list regs) { - // Uses a more robust solution for saving registers on MacOS to avoid potential stack corruption - // if games decide to not follow the ABI and use the red zone. - SaveStack(c); - for (const auto& reg : regs) { - c.push(reg.cvt64()); - } - RestoreStack(c); -} - -/// Switches to the patch stack, restores registers, and restores the original stack. -static void RestoreRegisters(Xbyak::CodeGenerator& c, - const std::initializer_list regs) { - SaveStack(c); - for (const auto& reg : regs) { - c.pop(reg.cvt64()); - } - RestoreStack(c); -} - -/// Switches to the patch stack and stores all registers. -static void SaveContext(Xbyak::CodeGenerator& c, bool save_flags = false) { - SaveStack(c); - for (int reg = Xbyak::Operand::RAX; reg <= Xbyak::Operand::R15; reg++) { - c.push(Xbyak::Reg64(reg)); - } - c.lea(rsp, ptr[rsp - 32 * 16]); - for (int reg = 0; reg <= 15; reg++) { - c.vmovdqu(ptr[rsp + 32 * reg], Xbyak::Ymm(reg)); - } - if (save_flags) { - c.pushfq(); - } -} - -/// Restores all registers and restores the original stack. -/// If the destination is a register, it is not restored to preserve the output. -static void RestoreContext(Xbyak::CodeGenerator& c, const Xbyak::Operand& dst, - bool restore_flags = false) { - if (restore_flags) { - c.popfq(); - } - for (int reg = 15; reg >= 0; reg--) { - if ((!dst.isXMM() && !dst.isYMM()) || dst.getIdx() != reg) { - c.vmovdqu(Xbyak::Ymm(reg), ptr[rsp + 32 * reg]); - } - } - c.lea(rsp, ptr[rsp + 32 * 16]); - for (int reg = Xbyak::Operand::R15; reg >= Xbyak::Operand::RAX; reg--) { - if (!dst.isREG() || dst.getIdx() != reg) { - c.pop(Xbyak::Reg64(reg)); - } else { - c.lea(rsp, ptr[rsp + 8]); - } - } - RestoreStack(c); -} - -static void GenerateANDN(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { - const auto dst = ZydisToXbyakRegisterOperand(operands[0]); - const auto src1 = ZydisToXbyakRegisterOperand(operands[1]); - const auto src2 = ZydisToXbyakOperand(operands[2]); - ValidateDst(dst); - - // Check if src2 is a memory operand or a register different to dst. - // In those cases, we don't need to use a temporary register and are free to modify dst. - // In cases where dst and src2 are the same register, a temporary needs to be used to avoid - // modifying src2. - bool src2_uses_dst = false; - if (src2->isMEM()) { - const auto base = src2->getAddress().getRegExp().getBase().getIdx(); - const auto index = src2->getAddress().getRegExp().getIndex().getIdx(); - src2_uses_dst = base == dst.getIdx() || index == dst.getIdx(); - } else { - ASSERT(src2->isREG()); - src2_uses_dst = src2->getReg() == dst; - } - - if (!src2_uses_dst) { - if (dst != src1) - c.mov(dst, src1); - c.not_(dst); - c.and_(dst, *src2); - } else { - const auto scratch = AllocateScratchRegister({&dst, &src1, src2.get()}, dst.getBit()); - - SaveRegisters(c, {scratch}); - - c.mov(scratch, src1); - c.not_(scratch); - c.and_(scratch, *src2); - c.mov(dst, scratch); - - RestoreRegisters(c, {scratch}); - } -} - -static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { - const auto dst = ZydisToXbyakRegisterOperand(operands[0]); - const auto src = ZydisToXbyakOperand(operands[1]); - const auto start_len = ZydisToXbyakRegisterOperand(operands[2]); - ValidateDst(dst); - - const Xbyak::Reg32e shift(Xbyak::Operand::RCX, static_cast(start_len.getBit())); - const auto scratch1 = - AllocateScratchRegister({&dst, src.get(), &start_len, &shift}, dst.getBit()); - const auto scratch2 = - AllocateScratchRegister({&dst, src.get(), &start_len, &shift, &scratch1}, dst.getBit()); - - if (dst.getIdx() == shift.getIdx()) { - SaveRegisters(c, {scratch1, scratch2}); - } else { - SaveRegisters(c, {scratch1, scratch2, shift}); - } - - c.mov(scratch1, *src); - if (shift.getIdx() != start_len.getIdx()) { - c.mov(shift, start_len); - } - - c.shr(scratch1, shift.cvt8()); - c.shr(shift, 8); - c.mov(scratch2, 1); - c.shl(scratch2, shift.cvt8()); - c.dec(scratch2); - - c.mov(dst, scratch1); - c.and_(dst, scratch2); - - if (dst.getIdx() == shift.getIdx()) { - RestoreRegisters(c, {scratch2, scratch1}); - } else { - RestoreRegisters(c, {shift, scratch2, scratch1}); - } -} - -static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { - const auto dst = ZydisToXbyakRegisterOperand(operands[0]); - const auto src = ZydisToXbyakOperand(operands[1]); - ValidateDst(dst); - - const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit()); - - SaveRegisters(c, {scratch}); - - // BLSI sets CF to zero if source is zero, otherwise it sets CF to one. - 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.jnc(clear_carry); - - c.and_(scratch, *src); - c.stc(); // setting/clearing carry needs to happen after the AND because that clears CF - c.jmp(end); - - c.L(clear_carry); - c.and_(scratch, *src); - // We don't need to clear carry here since AND does that for us - - c.L(end); - c.mov(dst, scratch); - - RestoreRegisters(c, {scratch}); -} - -static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { - const auto dst = ZydisToXbyakRegisterOperand(operands[0]); - const auto src = ZydisToXbyakOperand(operands[1]); - ValidateDst(dst); - - const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit()); - - SaveRegisters(c, {scratch}); - - 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.jnz(clear_carry); - - c.dec(scratch); - c.xor_(scratch, *src); - c.stc(); - c.jmp(end); - - c.L(clear_carry); - c.dec(scratch); - c.xor_(scratch, *src); - // We don't need to clear carry here since XOR does that for us - - c.L(end); - c.mov(dst, scratch); - - RestoreRegisters(c, {scratch}); -} - -static void GenerateTZCNT(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { - const auto dst = ZydisToXbyakRegisterOperand(operands[0]); - const auto src = ZydisToXbyakOperand(operands[1]); - ValidateDst(dst); - - Xbyak::Label src_zero, end; - - c.cmp(*src, 0); - c.je(src_zero); - - // If src is not zero, functions like a BSF, but also clears the CF - c.bsf(dst, *src); - c.clc(); - c.jmp(end); - - c.L(src_zero); - c.mov(dst, operands[0].size); - // Since dst is not zero, also set ZF to zero. Testing dst with itself when we know - // it isn't zero is a good way to do this. - // Use cvt32 to avoid REX/Operand size prefixes. - c.test(dst.cvt32(), dst.cvt32()); - // When source is zero, TZCNT also sets CF. - c.stc(); - - c.L(end); -} - -static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { - const auto dst = ZydisToXbyakRegisterOperand(operands[0]); - const auto src = ZydisToXbyakOperand(operands[1]); - ValidateDst(dst); - - const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit()); - - SaveRegisters(c, {scratch}); - - 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.jnz(clear_carry); - - c.dec(scratch); - c.and_(scratch, *src); - c.stc(); - c.jmp(end); - - c.L(clear_carry); - c.dec(scratch); - c.and_(scratch, *src); - // We don't need to clear carry here since AND does that for us - - c.L(end); - c.mov(dst, scratch); - - RestoreRegisters(c, {scratch}); -} - -#ifdef __APPLE__ - -static __attribute__((sysv_abi)) void PerformVCVTPH2PS(float* out, const half_float::half* in, - const u32 count) { - for (u32 i = 0; i < count; i++) { - out[i] = half_float::half_cast(in[i]); - } -} - -static void GenerateVCVTPH2PS(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { - const auto dst = ZydisToXbyakRegisterOperand(operands[0]); - const auto src = ZydisToXbyakOperand(operands[1]); - - const auto float_count = dst.getBit() / 32; - const auto byte_count = float_count * 4; - - SaveContext(c, true); - - // Allocate stack space for outputs and load into first parameter. - c.sub(rsp, byte_count); - c.mov(rdi, rsp); - - if (src->isXMM()) { - // Allocate stack space for inputs and load into second parameter. - c.sub(rsp, byte_count); - c.mov(rsi, rsp); - - // Move input to the allocated space. - c.movdqu(ptr[rsp], *reinterpret_cast(src.get())); - } else { - c.lea(rsi, src->getAddress()); - } - - // Load float count into third parameter. - c.mov(rdx, float_count); - - c.mov(rax, reinterpret_cast(PerformVCVTPH2PS)); - c.call(rax); - - if (src->isXMM()) { - // Clean up after inputs space. - c.add(rsp, byte_count); - } - - // Load outputs into destination register and clean up space. - if (dst.isYMM()) { - c.vmovdqu(*reinterpret_cast(&dst), ptr[rsp]); - } else { - c.movdqu(*reinterpret_cast(&dst), ptr[rsp]); - } - c.add(rsp, byte_count); - - RestoreContext(c, dst, true); -} - -using SingleToHalfFloatConverter = half_float::half (*)(float); -static const SingleToHalfFloatConverter SingleToHalfFloatConverters[4] = { - half_float::half_cast, - half_float::half_cast, - half_float::half_cast, - half_float::half_cast, -}; - -static __attribute__((sysv_abi)) void PerformVCVTPS2PH(half_float::half* out, const float* in, - const u32 count, const u8 rounding_mode) { - const auto conversion_func = SingleToHalfFloatConverters[rounding_mode]; - - for (u32 i = 0; i < count; i++) { - out[i] = conversion_func(in[i]); - } -} - -static void GenerateVCVTPS2PH(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { - const auto dst = ZydisToXbyakOperand(operands[0]); - const auto src = ZydisToXbyakRegisterOperand(operands[1]); - const auto ctrl = ZydisToXbyakImmediateOperand(operands[2]); - - const auto float_count = src.getBit() / 32; - const auto byte_count = float_count * 4; - - SaveContext(c, true); - - if (dst->isXMM()) { - // Allocate stack space for outputs and load into first parameter. - c.sub(rsp, byte_count); - c.mov(rdi, rsp); - } else { - c.lea(rdi, dst->getAddress()); - } - - // Allocate stack space for inputs and load into second parameter. - c.sub(rsp, byte_count); - c.mov(rsi, rsp); - - // Move input to the allocated space. - if (src.isYMM()) { - c.vmovdqu(ptr[rsp], *reinterpret_cast(&src)); - } else { - c.movdqu(ptr[rsp], *reinterpret_cast(&src)); - } - - // Load float count into third parameter. - c.mov(rdx, float_count); - - // Load rounding mode into fourth parameter. - if (ctrl & 4) { - // Load from MXCSR.RC. - c.stmxcsr(ptr[rsp - 4]); - c.mov(rcx, ptr[rsp - 4]); - c.shr(rcx, 13); - c.and_(rcx, 3); - } else { - c.mov(rcx, ctrl & 3); - } - - c.mov(rax, reinterpret_cast(PerformVCVTPS2PH)); - c.call(rax); - - // Clean up after inputs space. - c.add(rsp, byte_count); - - if (dst->isXMM()) { - // Load outputs into destination register and clean up space. - c.movdqu(*reinterpret_cast(dst.get()), ptr[rsp]); - c.add(rsp, byte_count); - } - - RestoreContext(c, *dst, true); -} - -static bool FilterRosetta2Only(const ZydisDecodedOperand*) { - int ret = 0; - size_t size = sizeof(ret); - if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) != 0) { - return false; - } - return ret; -} - -#else // __APPLE__ - static bool FilterTcbAccess(const ZydisDecodedOperand* operands) { const auto& dst_op = operands[0]; const auto& src_op = operands[1]; @@ -657,18 +121,11 @@ static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGe #endif } -#endif // __APPLE__ - static bool FilterNoSSE4a(const ZydisDecodedOperand*) { Cpu cpu; return !cpu.has(Cpu::tSSE4a); } -static bool FilterNoBMI1(const ZydisDecodedOperand*) { - Cpu cpu; - return !cpu.has(Cpu::tBMI1); -} - static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; @@ -940,30 +397,16 @@ struct PatchInfo { }; static const std::unordered_map Patches = { + // SSE4a + {ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}}, + {ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}}, + #if defined(_WIN32) // Windows needs a trampoline. {ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, true}}, #elif !defined(__APPLE__) {ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, false}}, #endif - - {ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}}, - {ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}}, - - // BMI1 - {ZYDIS_MNEMONIC_ANDN, {FilterNoBMI1, GenerateANDN, true}}, - {ZYDIS_MNEMONIC_BEXTR, {FilterNoBMI1, GenerateBEXTR, true}}, - {ZYDIS_MNEMONIC_BLSI, {FilterNoBMI1, GenerateBLSI, true}}, - {ZYDIS_MNEMONIC_BLSMSK, {FilterNoBMI1, GenerateBLSMSK, true}}, - {ZYDIS_MNEMONIC_BLSR, {FilterNoBMI1, GenerateBLSR, true}}, - {ZYDIS_MNEMONIC_TZCNT, {FilterNoBMI1, GenerateTZCNT, true}}, - -#ifdef __APPLE__ - // Patches for instruction sets not supported by Rosetta 2. - // F16C - {ZYDIS_MNEMONIC_VCVTPH2PS, {FilterRosetta2Only, GenerateVCVTPH2PS, true}}, - {ZYDIS_MNEMONIC_VCVTPS2PH, {FilterRosetta2Only, GenerateVCVTPS2PH, true}}, -#endif }; static std::once_flag init_flag; @@ -1280,18 +723,7 @@ void RegisterPatchModule(void* module_ptr, u64 module_size, void* trampoline_are } void PrePatchInstructions(u64 segment_addr, u64 segment_size) { -#if defined(__APPLE__) - // HACK: For some reason patching in the signal handler at the start of a page does not work - // under Rosetta 2. Patch any instructions at the start of a page ahead of time. - if (!Patches.empty()) { - auto* code_page = reinterpret_cast(Common::AlignUp(segment_addr, 0x1000)); - const auto* end_page = code_page + Common::AlignUp(segment_size, 0x1000); - while (code_page < end_page) { - TryPatchJit(code_page); - code_page += 0x1000; - } - } -#elif !defined(_WIN32) +#if !defined(_WIN32) && !defined(__APPLE__) // Linux and others have an FS segment pointing to valid memory, so continue to do full // ahead-of-time patching for now until a better solution is worked out. if (!Patches.empty()) { diff --git a/src/core/cpu_patches.h b/src/core/cpu_patches.h index 1ccac073a..7a0546046 100644 --- a/src/core/cpu_patches.h +++ b/src/core/cpu_patches.h @@ -7,12 +7,6 @@ namespace Core { -/// Initializes a stack for the current thread for use by patch implementations. -void InitializeThreadPatchStack(); - -/// Cleans up the patch stack for the current thread. -void CleanupThreadPatchStack(); - /// Registers a module for patching, providing an area to generate trampoline code. void RegisterPatchModule(void* module_ptr, u64 module_size, void* trampoline_area_ptr, u64 trampoline_area_size); diff --git a/src/core/devtools/widget/text_editor.cpp b/src/core/devtools/widget/text_editor.cpp index 7171cac47..12031d1ef 100644 --- a/src/core/devtools/widget/text_editor.cpp +++ b/src/core/devtools/widget/text_editor.cpp @@ -627,65 +627,56 @@ void TextEditor::HandleKeyboardInputs() { io.WantCaptureKeyboard = true; io.WantTextInput = true; - if (!IsReadOnly() && ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Z))) + if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Z)) Undo(); - else if (!IsReadOnly() && !ctrl && !shift && alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) + else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGuiKey_Backspace)) Undo(); - else if (!IsReadOnly() && ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y))) + else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Y)) Redo(); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow))) + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_UpArrow)) MoveUp(1, shift); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow))) + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_DownArrow)) MoveDown(1, shift); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow))) + else if (!alt && ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) MoveLeft(1, shift, ctrl); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow))) + else if (!alt && ImGui::IsKeyPressed(ImGuiKey_RightArrow)) MoveRight(1, shift, ctrl); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp))) + else if (!alt && ImGui::IsKeyPressed(ImGuiKey_PageUp)) MoveUp(GetPageSize() - 4, shift); - else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown))) + else if (!alt && ImGui::IsKeyPressed(ImGuiKey_PageDown)) MoveDown(GetPageSize() - 4, shift); - else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) + else if (!alt && ctrl && ImGui::IsKeyPressed(ImGuiKey_Home)) MoveTop(shift); - else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) + else if (ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_End)) MoveBottom(shift); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Home)) MoveHome(shift); - else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_End)) MoveEnd(shift); - else if (!IsReadOnly() && !ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) + else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete)) Delete(); else if (!IsReadOnly() && !ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Backspace))) + ImGui::IsKeyPressed(ImGuiKey_Backspace)) Backspace(); - else if (!ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) + else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert)) mOverwrite ^= true; - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert)) Copy(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_C)) Copy(); - else if (!IsReadOnly() && !ctrl && shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert))) + else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert)) Paste(); - else if (!IsReadOnly() && ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V))) + else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_V)) Paste(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_X))) + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_X)) Cut(); - else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) + else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete)) Cut(); - else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_A))) + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_A)) SelectAll(); - else if (!IsReadOnly() && !ctrl && !shift && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter))) + else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Enter)) EnterCharacter('\n', false); - else if (!IsReadOnly() && !ctrl && !alt && - ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Tab))) + else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Tab)) EnterCharacter('\t', shift); if (!IsReadOnly() && !io.InputQueueCharacters.empty()) { diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index dea8115e9..92488443f 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -191,6 +191,7 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta case OrbisAudioOutPort::Main: case OrbisAudioOutPort::Bgm: case OrbisAudioOutPort::Voice: + case OrbisAudioOutPort::Audio3d: state->output = 1; state->channel = port.format_info.num_channels > 2 ? 2 : port.format_info.num_channels; break; @@ -316,7 +317,7 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, return ORBIS_AUDIO_OUT_ERROR_NOT_INIT; } if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) && - (port_type != OrbisAudioOutPort::Aux)) { + (port_type != OrbisAudioOutPort::Audio3d && port_type != OrbisAudioOutPort::Aux)) { LOG_ERROR(Lib_AudioOut, "Invalid port type"); return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT_TYPE; } diff --git a/src/core/libraries/audio/audioout.h b/src/core/libraries/audio/audioout.h index 5eafb43a1..7fcc25095 100644 --- a/src/core/libraries/audio/audioout.h +++ b/src/core/libraries/audio/audioout.h @@ -20,7 +20,15 @@ class PortBackend; constexpr s32 SCE_AUDIO_OUT_NUM_PORTS = 22; constexpr s32 SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value -enum class OrbisAudioOutPort { Main = 0, Bgm = 1, Voice = 2, Personal = 3, Padspk = 4, Aux = 127 }; +enum class OrbisAudioOutPort { + Main = 0, + Bgm = 1, + Voice = 2, + Personal = 3, + Padspk = 4, + Audio3d = 126, + Aux = 127, +}; enum class OrbisAudioOutParamFormat : u32 { S16Mono = 0, diff --git a/src/core/libraries/audio3d/audio3d.cpp b/src/core/libraries/audio3d/audio3d.cpp index d896524c6..646c28949 100644 --- a/src/core/libraries/audio3d/audio3d.cpp +++ b/src/core/libraries/audio3d/audio3d.cpp @@ -1,8 +1,13 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + +#include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/audio/audioout.h" +#include "core/libraries/audio/audioout_error.h" #include "core/libraries/audio3d/audio3d.h" #include "core/libraries/audio3d/audio3d_error.h" #include "core/libraries/error_codes.h" @@ -10,331 +15,577 @@ namespace Libraries::Audio3d { -int PS4_SYSV_ABI sceAudio3dInitialize(s64 iReserved) { - LOG_INFO(Lib_Audio3d, "iReserved = {}", iReserved); - return ORBIS_OK; +static constexpr u32 AUDIO3D_SAMPLE_RATE = 48000; + +static constexpr AudioOut::OrbisAudioOutParamFormat AUDIO3D_OUTPUT_FORMAT = + AudioOut::OrbisAudioOutParamFormat::S16Stereo; +static constexpr u32 AUDIO3D_OUTPUT_NUM_CHANNELS = 2; +static constexpr u32 AUDIO3D_OUTPUT_BUFFER_FRAMES = 0x100; + +static std::unique_ptr state; + +s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(const s32 handle) { + LOG_INFO(Lib_Audio3d, "called, handle = {}", handle); + return AudioOut::sceAudioOutClose(handle); } -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* parameters) { - if (parameters == nullptr) { - LOG_ERROR(Lib_Audio3d, "Invalid OpenParameters ptr"); - return; - } - - parameters->size_this = sizeof(OrbisAudio3dOpenParameters); - parameters->granularity = 256; - parameters->rate = OrbisAudio3dRate::Rate48000; - parameters->max_objects = 512; - parameters->queue_depth = 2; - parameters->buffer_mode = OrbisAudio3dBufferMode::AdvanceAndPush; - parameters->num_beds = 2; -} - -int PS4_SYSV_ABI sceAudio3dPortOpen(OrbisUserServiceUserId iUserId, - 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_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId uiPortId, OrbisAudio3dBlocking eBlocking) { - LOG_TRACE(Lib_Audio3d, "uiPortId = {}", uiPortId); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported(OrbisAudio3dPortId uiPortId, - OrbisAudio3dAttributeId* pCapabilities, - u32* pNumCapabilities) { - LOG_INFO(Lib_Audio3d, "uiPortId = {}", uiPortId); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId uiPortId, u32* pQueueLevel, - u32* pQueueAvailable) { - LOG_TRACE(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, u32 uiNumChannels, - OrbisAudio3dFormat eFormat, const void* pBuffer, - u32 uiNumSamples) { - LOG_TRACE(Lib_Audio3d, "uiPortId = {}, uiNumChannels = {}, uiNumSamples = {}", uiPortId, - uiNumChannels, uiNumSamples); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceAudio3dBedWrite2(OrbisAudio3dPortId uiPortId, u32 uiNumChannels, - OrbisAudio3dFormat eFormat, const void* pBuffer, - u32 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(u32 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, - u32 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, - u32 uiNumCoefficients, bool bHeightAware, - float fDownmixSpreadRadius) { +s32 PS4_SYSV_ABI +sceAudio3dAudioOutOpen(const OrbisAudio3dPortId port_id, const OrbisUserServiceUserId user_id, + s32 type, const s32 index, const u32 len, const u32 freq, + const AudioOut::OrbisAudioOutParamExtendedInformation param) { LOG_INFO(Lib_Audio3d, - "fSpread = {}, uiNumCoefficients = {}, bHeightAware = {}, fDownmixSpreadRadius = {}", - fSpread, uiNumCoefficients, bHeightAware, fDownmixSpreadRadius); - if (handle == nullptr) { - LOG_ERROR(Lib_Audio3d, "invalid SpeakerArrayHandle"); + "called, port_id = {}, user_id = {}, type = {}, index = {}, len = {}, freq = {}", + port_id, user_id, type, index, len, freq); + + if (!state->ports.contains(port_id)) { + LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)"); + return ORBIS_AUDIO3D_ERROR_INVALID_PORT; + } + + if (len != state->ports[port_id].parameters.granularity) { + LOG_ERROR(Lib_Audio3d, "len != state->ports[port_id].parameters.granularity"); return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; } + + return sceAudioOutOpen(user_id, static_cast(type), index, len, + freq, param); +} + +s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(const s32 handle, void* ptr) { + LOG_DEBUG(Lib_Audio3d, "called, handle = {}, ptr = {}", handle, ptr); + + if (!ptr) { + LOG_ERROR(Lib_Audio3d, "!ptr"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (handle < 0 || (handle & 0xFFFF) > 25) { + LOG_ERROR(Lib_Audio3d, "handle < 0 || (handle & 0xFFFF) > 25"); + return ORBIS_AUDIO3D_ERROR_INVALID_PORT; + } + + return AudioOut::sceAudioOutOutput(handle, ptr); +} + +s32 PS4_SYSV_ABI sceAudio3dAudioOutOutputs(AudioOut::OrbisAudioOutOutputParam* param, + const u32 num) { + LOG_DEBUG(Lib_Audio3d, "called, param = {}, num = {}", static_cast(param), num); + + if (!param || !num) { + LOG_ERROR(Lib_Audio3d, "!param || !num"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + return AudioOut::sceAudioOutOutputs(param, num); +} + +static s32 PortQueueAudio(Port& port, const OrbisAudio3dPcm& pcm, const u32 num_channels) { + // Audio3d output is configured for stereo signed 16-bit PCM. Convert the data to match. + const SDL_AudioSpec src_spec = { + .format = pcm.format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16 ? SDL_AUDIO_S16LE + : SDL_AUDIO_F32LE, + .channels = static_cast(num_channels), + .freq = AUDIO3D_SAMPLE_RATE, + }; + constexpr SDL_AudioSpec dst_spec = { + .format = SDL_AUDIO_S16LE, + .channels = AUDIO3D_OUTPUT_NUM_CHANNELS, + .freq = AUDIO3D_SAMPLE_RATE, + }; + const auto src_size = pcm.num_samples * + (pcm.format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16 ? 2 : 4) * + num_channels; + + u8* dst_data; + int dst_len; + if (!SDL_ConvertAudioSamples(&src_spec, static_cast(pcm.sample_buffer), + static_cast(src_size), &dst_spec, &dst_data, &dst_len)) { + LOG_ERROR(Lib_Audio3d, "SDL_ConvertAudioSamples failed: {}", SDL_GetError()); + return ORBIS_AUDIO3D_ERROR_OUT_OF_MEMORY; + } + + port.queue.emplace_back(AudioData{ + .sample_buffer = dst_data, + .num_samples = pcm.num_samples, + }); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dBedWrite(const OrbisAudio3dPortId port_id, const u32 num_channels, + const OrbisAudio3dFormat format, void* buffer, + const u32 num_samples) { + return sceAudio3dBedWrite2(port_id, num_channels, format, buffer, num_samples, + OrbisAudio3dOutputRoute::ORBIS_AUDIO3D_OUTPUT_BOTH, false); +} + +s32 PS4_SYSV_ABI sceAudio3dBedWrite2(const OrbisAudio3dPortId port_id, const u32 num_channels, + const OrbisAudio3dFormat format, void* buffer, + const u32 num_samples, + const OrbisAudio3dOutputRoute output_route, + const bool restricted) { + LOG_DEBUG( + Lib_Audio3d, + "called, port_id = {}, num_channels = {}, format = {}, num_samples = {}, output_route " + "= {}, restricted = {}", + port_id, num_channels, magic_enum::enum_name(format), num_samples, + magic_enum::enum_name(output_route), restricted); + + if (!state->ports.contains(port_id)) { + LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)"); + return ORBIS_AUDIO3D_ERROR_INVALID_PORT; + } + + if (output_route > OrbisAudio3dOutputRoute::ORBIS_AUDIO3D_OUTPUT_BOTH) { + LOG_ERROR(Lib_Audio3d, "output_route > ORBIS_AUDIO3D_OUTPUT_BOTH"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (format > OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_FLOAT) { + LOG_ERROR(Lib_Audio3d, "format > ORBIS_AUDIO3D_FORMAT_FLOAT"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (num_channels != 2 && num_channels != 8) { + LOG_ERROR(Lib_Audio3d, "num_channels != 2 && num_channels != 8"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (!buffer || !num_samples) { + LOG_ERROR(Lib_Audio3d, "!buffer || !num_samples"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_FLOAT) { + if ((reinterpret_cast(buffer) & 3) != 0) { + LOG_ERROR(Lib_Audio3d, "buffer & 3 != 0"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + } else if (format == OrbisAudio3dFormat::ORBIS_AUDIO3D_FORMAT_S16) { + if ((reinterpret_cast(buffer) & 1) != 0) { + LOG_ERROR(Lib_Audio3d, "buffer & 1 != 0"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + } + + return PortQueueAudio(state->ports[port_id], + OrbisAudio3dPcm{ + .format = format, + .sample_buffer = buffer, + .num_samples = num_samples, + }, + num_channels); +} + +s32 PS4_SYSV_ABI sceAudio3dCreateSpeakerArray() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dDeleteSpeakerArray() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* params) { + LOG_DEBUG(Lib_Audio3d, "called"); + if (params) { + *params = OrbisAudio3dOpenParameters{ + .size_this = 0x20, + .granularity = 0x100, + .rate = OrbisAudio3dRate::ORBIS_AUDIO3D_RATE_48000, + .max_objects = 512, + .queue_depth = 2, + .buffer_mode = OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH, + }; + } return ORBIS_OK; } -s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(OrbisAudio3dPortId uiPortId, OrbisUserServiceUserId userId, - s32 type, s32 index, u32 len, u32 freq, u32 param) { +s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dInitialize(const s64 reserved) { + LOG_INFO(Lib_Audio3d, "called, reserved = {}", reserved); + + if (reserved != 0) { + LOG_ERROR(Lib_Audio3d, "reserved != 0"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + if (state) { + LOG_ERROR(Lib_Audio3d, "already initialized"); + return ORBIS_AUDIO3D_ERROR_NOT_READY; + } + + state = std::make_unique(); + + if (const auto init_ret = AudioOut::sceAudioOutInit(); + init_ret < 0 && init_ret != ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT) { + return init_ret; + } + + AudioOut::OrbisAudioOutParamExtendedInformation ext_info{}; + ext_info.data_format.Assign(AUDIO3D_OUTPUT_FORMAT); + state->audio_out_handle = + AudioOut::sceAudioOutOpen(0xFF, AudioOut::OrbisAudioOutPort::Audio3d, 0, + AUDIO3D_OUTPUT_BUFFER_FRAMES, AUDIO3D_SAMPLE_RATE, ext_info); + if (state->audio_out_handle < 0) { + return state->audio_out_handle; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dObjectReserve(const OrbisAudio3dPortId port_id, + OrbisAudio3dObjectId* object_id) { + LOG_INFO(Lib_Audio3d, "called, port_id = {}, object_id = {}", port_id, + static_cast(object_id)); + + if (!state->ports.contains(port_id)) { + LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)"); + return ORBIS_AUDIO3D_ERROR_INVALID_PORT; + } + + if (!object_id) { + LOG_ERROR(Lib_Audio3d, "!object_id"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + static int last_id = 0; + *object_id = ++last_id; + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dObjectSetAttributes(const OrbisAudio3dPortId port_id, + OrbisAudio3dObjectId object_id, + const u64 num_attributes, + const OrbisAudio3dAttribute* attribute_array) { + LOG_DEBUG(Lib_Audio3d, + "called, port_id = {}, object_id = {}, num_attributes = {}, attribute_array = {}", + port_id, object_id, num_attributes, fmt::ptr(attribute_array)); + + if (!state->ports.contains(port_id)) { + LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)"); + return ORBIS_AUDIO3D_ERROR_INVALID_PORT; + } + + auto& port = state->ports[port_id]; + + for (u64 i = 0; i < num_attributes; i++) { + const auto& attribute = attribute_array[i]; + + switch (attribute.attribute_id) { + case OrbisAudio3dAttributeId::ORBIS_AUDIO3D_ATTRIBUTE_PCM: { + const auto pcm = static_cast(attribute.value); + // Object audio has 1 channel. + if (const auto ret = PortQueueAudio(port, *pcm, 1); ret != ORBIS_OK) { + return ret; + } + break; + } + default: + LOG_ERROR(Lib_Audio3d, "Unsupported attribute ID: {:#x}", + static_cast(attribute.attribute_id)); + break; + } + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortAdvance(const OrbisAudio3dPortId port_id) { + LOG_DEBUG(Lib_Audio3d, "called, port_id = {}", port_id); + + if (!state->ports.contains(port_id)) { + LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)"); + return ORBIS_AUDIO3D_ERROR_INVALID_PORT; + } + + if (state->ports[port_id].parameters.buffer_mode == + OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_NO_ADVANCE) { + LOG_ERROR(Lib_Audio3d, "port doesn't have advance capability"); + return ORBIS_AUDIO3D_ERROR_NOT_SUPPORTED; + } + + auto& port = state->ports[port_id]; + if (port.current_buffer.has_value()) { + // Free existing buffer before replacing. + SDL_free(port.current_buffer->sample_buffer); + } + + if (!port.queue.empty()) { + port.current_buffer = port.queue.front(); + port.queue.pop_front(); + } else { + // Nothing to advance to. + LOG_DEBUG(Lib_Audio3d, "Port advance with no buffer queued"); + port.current_buffer = std::nullopt; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortClose() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortCreate() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortDestroy() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortFlush() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortFreeState() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortGetList() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortGetParameters() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(const OrbisAudio3dPortId port_id, u32* queue_level, + u32* queue_available) { + LOG_DEBUG(Lib_Audio3d, "called, port_id = {}, queue_level = {}, queue_available = {}", port_id, + static_cast(queue_level), static_cast(queue_available)); + + if (!state->ports.contains(port_id)) { + LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)"); + return ORBIS_AUDIO3D_ERROR_INVALID_PORT; + } + + if (!queue_level && !queue_available) { + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + const auto port = state->ports[port_id]; + const size_t size = port.queue.size(); + + if (queue_level) { + *queue_level = size; + } + + if (queue_available) { + *queue_available = port.parameters.queue_depth - size; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortGetState() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortGetStatus() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortOpen(const OrbisUserServiceUserId user_id, + const OrbisAudio3dOpenParameters* parameters, + OrbisAudio3dPortId* port_id) { + LOG_INFO(Lib_Audio3d, "called, user_id = {}, parameters = {}, id = {}", user_id, + static_cast(parameters), static_cast(port_id)); + + if (!state) { + LOG_ERROR(Lib_Audio3d, "!initialized"); + return ORBIS_AUDIO3D_ERROR_NOT_READY; + } + + if (!parameters || !port_id) { + LOG_ERROR(Lib_Audio3d, "!parameters || !id"); + return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; + } + + const int id = static_cast(state->ports.size()) + 1; + + if (id > 3) { + LOG_ERROR(Lib_Audio3d, "id > 3"); + return ORBIS_AUDIO3D_ERROR_OUT_OF_RESOURCES; + } + + *port_id = id; + std::memcpy(&state->ports[id].parameters, parameters, sizeof(OrbisAudio3dOpenParameters)); + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortPush(const OrbisAudio3dPortId port_id, + const OrbisAudio3dBlocking blocking) { + LOG_DEBUG(Lib_Audio3d, "called, port_id = {}, blocking = {}", port_id, + magic_enum::enum_name(blocking)); + + if (!state->ports.contains(port_id)) { + LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)"); + return ORBIS_AUDIO3D_ERROR_INVALID_PORT; + } + + const auto& port = state->ports[port_id]; + if (port.parameters.buffer_mode != + OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH) { + LOG_ERROR(Lib_Audio3d, "port doesn't have push capability"); + return ORBIS_AUDIO3D_ERROR_NOT_SUPPORTED; + } + + if (!port.current_buffer.has_value()) { + // Nothing to push. + LOG_DEBUG(Lib_Audio3d, "Port push with no buffer ready"); + return ORBIS_OK; + } + + // TODO: Implement asynchronous blocking mode. + const auto& [sample_buffer, num_samples] = port.current_buffer.value(); + return AudioOut::sceAudioOutOutput(state->audio_out_handle, sample_buffer); +} + +s32 PS4_SYSV_ABI sceAudio3dPortQueryDebug() { + LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceAudio3dPortSetAttribute(const OrbisAudio3dPortId port_id, + const OrbisAudio3dAttributeId attribute_id, + void* attribute, const u64 attribute_size) { LOG_INFO(Lib_Audio3d, - "uiPortId = {}, userId = {}, type = {}, index = {}, len = {}, freq = {}, param = {}", - uiPortId, userId, type, index, len, freq, param); - return ORBIS_OK; -} + "called, port_id = {}, attribute_id = {}, attribute = {}, attribute_size = {}", + port_id, static_cast(attribute_id), attribute, attribute_size); -s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle) { - LOG_INFO(Lib_Audio3d, "handle = {}", handle); - return ORBIS_OK; -} + if (!state->ports.contains(port_id)) { + LOG_ERROR(Lib_Audio3d, "!state->ports.contains(port_id)"); + return ORBIS_AUDIO3D_ERROR_INVALID_PORT; + } -s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, const void* ptr) { - LOG_TRACE(Lib_Audio3d, "handle = {}", handle); - if (ptr == nullptr) { - LOG_ERROR(Lib_Audio3d, "invalid Output ptr"); + if (!attribute) { + LOG_ERROR(Lib_Audio3d, "!attribute"); return ORBIS_AUDIO3D_ERROR_INVALID_PARAMETER; } + + // TODO + 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(u32 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() { +s32 PS4_SYSV_ABI sceAudio3dReportRegisterHandler() { LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dPortGetStatus() { +s32 PS4_SYSV_ABI sceAudio3dReportUnregisterHandler() { LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dReportRegisterHandler() { +s32 PS4_SYSV_ABI sceAudio3dSetGpuRenderer() { LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dReportUnregisterHandler() { +s32 PS4_SYSV_ABI sceAudio3dStrError() { LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceAudio3dSetGpuRenderer() { +s32 PS4_SYSV_ABI sceAudio3dTerminate() { LOG_ERROR(Lib_Audio3d, "(STUBBED) called"); return ORBIS_OK; } void RegisterlibSceAudio3d(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("pZlOm1aF3aA", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dAudioOutClose); + LIB_FUNCTION("ucEsi62soTo", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dAudioOutOpen); + LIB_FUNCTION("7NYEzJ9SJbM", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dAudioOutOutput); + LIB_FUNCTION("HbxYY27lK6E", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dAudioOutOutputs); + LIB_FUNCTION("9tEwE0GV0qo", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dBedWrite); + LIB_FUNCTION("xH4Q9UILL3o", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dBedWrite2); + LIB_FUNCTION("lvWMW6vEqFU", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dCreateSpeakerArray); + LIB_FUNCTION("8hm6YdoQgwg", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dDeleteSpeakerArray); + LIB_FUNCTION("Im+jOoa5WAI", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dGetDefaultOpenParameters); + LIB_FUNCTION("kEqqyDkmgdI", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dGetSpeakerArrayMemorySize); 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("UmCvjSmuZIw", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dInitialize); + LIB_FUNCTION("jO2tec4dJ2M", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dObjectReserve); 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("1HXxo-+1qCw", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dObjectUnreserve); + LIB_FUNCTION("lw0qrdSjZt8", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortAdvance); + LIB_FUNCTION("OyVqOeVNtSk", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortClose); + LIB_FUNCTION("UHFOgVNz0kk", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortCreate); + LIB_FUNCTION("Mw9mRQtWepY", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortDestroy); + LIB_FUNCTION("ZOGrxWLgQzE", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortFlush); + LIB_FUNCTION("uJ0VhGcxCTQ", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortFreeState); 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("YaaDbDwKpFM", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dPortGetQueueLevel); + LIB_FUNCTION("CKHlRW2E9dA", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortGetState); 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("XeDDK0xJWQA", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortOpen); + LIB_FUNCTION("VEVhZ9qd4ZY", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dPortPush); + LIB_FUNCTION("-pzYDZozm+M", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dPortQueryDebug); + LIB_FUNCTION("Yq9bfUQ0uJg", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dPortSetAttribute); + LIB_FUNCTION("QfNXBrKZeI0", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, + sceAudio3dReportRegisterHandler); 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); + LIB_FUNCTION("Aacl5qkRU6U", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dStrError); + LIB_FUNCTION("WW1TS2iz5yc", "libSceAudio3d", 1, "libSceAudio3d", 1, 1, sceAudio3dTerminate); }; } // namespace Libraries::Audio3d diff --git a/src/core/libraries/audio3d/audio3d.h b/src/core/libraries/audio3d/audio3d.h index 6f344226f..f4e9ada8a 100644 --- a/src/core/libraries/audio3d/audio3d.h +++ b/src/core/libraries/audio3d/audio3d.h @@ -1,11 +1,13 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include "common/types.h" +#include +#include -#include +#include "common/types.h" +#include "core/libraries/audio/audioout.h" namespace Core::Loader { class SymbolsResolver; @@ -13,123 +15,131 @@ class SymbolsResolver; namespace Libraries::Audio3d { -class Audio3d; - using OrbisUserServiceUserId = s32; -using OrbisAudio3dPortId = u32; -using OrbisAudio3dObjectId = u32; -using OrbisAudio3dAttributeId = u32; -enum class OrbisAudio3dFormat { - S16 = 0, - Float = 1, +enum class OrbisAudio3dRate : u32 { + ORBIS_AUDIO3D_RATE_48000 = 0, }; -enum class OrbisAudio3dRate { - Rate48000 = 0, +enum class OrbisAudio3dBufferMode : u32 { + ORBIS_AUDIO3D_BUFFER_NO_ADVANCE = 0, + ORBIS_AUDIO3D_BUFFER_ADVANCE_NO_PUSH = 1, + ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH = 2, }; -enum class OrbisAudio3dBufferMode { NoAdvance = 0, AdvanceNoPush = 1, AdvanceAndPush = 2 }; - -enum class OrbisAudio3dBlocking { - Async = 0, - Sync = 1, -}; - -enum class OrbisAudio3dPassthrough { - None = 0, - Left = 1, - Right = 2, -}; - -enum class OrbisAudio3dOutputRoute { - Both = 0, - HmuOnly = 1, - TvOnly = 2, -}; - -enum class OrbisAudio3dAmbisonics : u32 { - None = ~0U, - W = 0, - X = 1, - Y = 2, - Z = 3, - R = 4, - S = 5, - T = 6, - U = 7, - V = 8, - K = 9, - L = 10, - M = 11, - N = 12, - O = 13, - P = 14, - 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 size_this; + u64 size_this; u32 granularity; OrbisAudio3dRate rate; u32 max_objects; u32 queue_depth; OrbisAudio3dBufferMode buffer_mode; - char padding[32]; + int : 32; u32 num_beds; }; -struct OrbisAudio3dAttribute { - OrbisAudio3dAttributeId attribute_id; - char padding[32]; - const void* p_value; - size_t value; +enum class OrbisAudio3dFormat : u32 { + ORBIS_AUDIO3D_FORMAT_S16 = 0, + ORBIS_AUDIO3D_FORMAT_FLOAT = 1, }; -struct OrbisAudio3dPosition { - float fX; - float fY; - float fZ; +enum class OrbisAudio3dOutputRoute : u32 { + ORBIS_AUDIO3D_OUTPUT_BOTH = 0, + ORBIS_AUDIO3D_OUTPUT_HMU_ONLY = 1, + ORBIS_AUDIO3D_OUTPUT_TV_ONLY = 2, +}; + +enum class OrbisAudio3dBlocking : u32 { + ORBIS_AUDIO3D_BLOCKING_ASYNC = 0, + ORBIS_AUDIO3D_BLOCKING_SYNC = 1, }; struct OrbisAudio3dPcm { OrbisAudio3dFormat format; - const void* sample_buffer; + void* sample_buffer; u32 num_samples; }; -struct OrbisAudio3dSpeakerArrayParameters { - OrbisAudio3dPosition* speaker_position; - u32 num_speakers; - bool is_3d; - void* buffer; - size_t size; +enum class OrbisAudio3dAttributeId : u32 { + ORBIS_AUDIO3D_ATTRIBUTE_PCM = 1, }; -struct OrbisAudio3dApplicationSpecific { - size_t size_this; - u8 application_specific[32]; +using OrbisAudio3dPortId = u32; +using OrbisAudio3dObjectId = u32; + +struct OrbisAudio3dAttribute { + OrbisAudio3dAttributeId attribute_id; + int : 32; + void* value; + u64 value_size; }; -void PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* sParameters); +struct AudioData { + u8* sample_buffer; + u32 num_samples; +}; + +struct Port { + OrbisAudio3dOpenParameters parameters{}; + std::deque queue; // Only stores PCM buffers for now + std::optional current_buffer{}; +}; + +struct Audio3dState { + std::unordered_map ports; + s32 audio_out_handle; +}; + +s32 PS4_SYSV_ABI sceAudio3dAudioOutClose(s32 handle); +s32 PS4_SYSV_ABI sceAudio3dAudioOutOpen(OrbisAudio3dPortId port_id, OrbisUserServiceUserId user_id, + s32 type, s32 index, u32 len, u32 freq, + AudioOut::OrbisAudioOutParamExtendedInformation param); +s32 PS4_SYSV_ABI sceAudio3dAudioOutOutput(s32 handle, void* ptr); +s32 PS4_SYSV_ABI sceAudio3dAudioOutOutputs(AudioOut::OrbisAudioOutOutputParam* param, u32 num); +s32 PS4_SYSV_ABI sceAudio3dBedWrite(OrbisAudio3dPortId port_id, u32 num_channels, + OrbisAudio3dFormat format, void* buffer, u32 num_samples); +s32 PS4_SYSV_ABI sceAudio3dBedWrite2(OrbisAudio3dPortId port_id, u32 num_channels, + OrbisAudio3dFormat format, void* buffer, u32 num_samples, + OrbisAudio3dOutputRoute output_route, bool restricted); +s32 PS4_SYSV_ABI sceAudio3dCreateSpeakerArray(); +s32 PS4_SYSV_ABI sceAudio3dDeleteSpeakerArray(); +s32 PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* params); +s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMemorySize(); +s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients(); +s32 PS4_SYSV_ABI sceAudio3dGetSpeakerArrayMixCoefficients2(); +s32 PS4_SYSV_ABI sceAudio3dInitialize(s64 reserved); +s32 PS4_SYSV_ABI sceAudio3dObjectReserve(OrbisAudio3dPortId port_id, + OrbisAudio3dObjectId* object_id); +s32 PS4_SYSV_ABI sceAudio3dObjectSetAttributes(OrbisAudio3dPortId port_id, + OrbisAudio3dObjectId object_id, u64 num_attributes, + const OrbisAudio3dAttribute* attribute_array); +s32 PS4_SYSV_ABI sceAudio3dObjectUnreserve(); +s32 PS4_SYSV_ABI sceAudio3dPortAdvance(OrbisAudio3dPortId port_id); +s32 PS4_SYSV_ABI sceAudio3dPortClose(); +s32 PS4_SYSV_ABI sceAudio3dPortCreate(); +s32 PS4_SYSV_ABI sceAudio3dPortDestroy(); +s32 PS4_SYSV_ABI sceAudio3dPortFlush(); +s32 PS4_SYSV_ABI sceAudio3dPortFreeState(); +s32 PS4_SYSV_ABI sceAudio3dPortGetAttributesSupported(); +s32 PS4_SYSV_ABI sceAudio3dPortGetList(); +s32 PS4_SYSV_ABI sceAudio3dPortGetParameters(); +s32 PS4_SYSV_ABI sceAudio3dPortGetQueueLevel(OrbisAudio3dPortId port_id, u32* queue_level, + u32* queue_available); +s32 PS4_SYSV_ABI sceAudio3dPortGetState(); +s32 PS4_SYSV_ABI sceAudio3dPortGetStatus(); +s32 PS4_SYSV_ABI sceAudio3dPortOpen(OrbisUserServiceUserId user_id, + const OrbisAudio3dOpenParameters* parameters, + OrbisAudio3dPortId* port_id); +s32 PS4_SYSV_ABI sceAudio3dPortPush(OrbisAudio3dPortId port_id, OrbisAudio3dBlocking blocking); +s32 PS4_SYSV_ABI sceAudio3dPortQueryDebug(); +s32 PS4_SYSV_ABI sceAudio3dPortSetAttribute(OrbisAudio3dPortId port_id, + OrbisAudio3dAttributeId attribute_id, void* attribute, + u64 attribute_size); +s32 PS4_SYSV_ABI sceAudio3dReportRegisterHandler(); +s32 PS4_SYSV_ABI sceAudio3dReportUnregisterHandler(); +s32 PS4_SYSV_ABI sceAudio3dSetGpuRenderer(); +s32 PS4_SYSV_ABI sceAudio3dStrError(); +s32 PS4_SYSV_ABI sceAudio3dTerminate(); void RegisterlibSceAudio3d(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Audio3d diff --git a/src/core/libraries/audio3d/audio3d_error.h b/src/core/libraries/audio3d/audio3d_error.h index 626ac8699..ff9d9749c 100644 --- a/src/core/libraries/audio3d/audio3d_error.h +++ b/src/core/libraries/audio3d/audio3d_error.h @@ -3,8 +3,6 @@ #pragma once -#include "core/libraries/error_codes.h" - constexpr int ORBIS_AUDIO3D_ERROR_UNKNOWN = 0x80EA0001; constexpr int ORBIS_AUDIO3D_ERROR_INVALID_PORT = 0x80EA0002; constexpr int ORBIS_AUDIO3D_ERROR_INVALID_OBJECT = 0x80EA0003; diff --git a/src/core/libraries/audio3d/audio3d_impl.cpp b/src/core/libraries/audio3d/audio3d_impl.cpp deleted file mode 100644 index 3069e8800..000000000 --- a/src/core/libraries/audio3d/audio3d_impl.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// 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/kernel.h" - -using namespace Libraries::Kernel; - -namespace Libraries::Audio3d {} // namespace Libraries::Audio3d diff --git a/src/core/libraries/audio3d/audio3d_impl.h b/src/core/libraries/audio3d/audio3d_impl.h deleted file mode 100644 index 1213a030e..000000000 --- a/src/core/libraries/audio3d/audio3d_impl.h +++ /dev/null @@ -1,16 +0,0 @@ -// 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: - using OrbisAudio3dPluginId = u32; -}; - -} // namespace Libraries::Audio3d diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index e8560b2b8..25ac4921c 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -2804,7 +2804,7 @@ void RegisterlibSceGnmDriver(Core::Loader::SymbolsResolver* sym) { liverpool = std::make_unique(); presenter = std::make_unique(*g_window, liverpool.get()); - const int result = sceKernelGetCompiledSdkVersion(&sdk_version); + const s32 result = sceKernelGetCompiledSdkVersion(&sdk_version); if (result != ORBIS_OK) { sdk_version = 0; } diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 3321559ed..bc34dff98 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -190,16 +190,16 @@ s32 PS4_SYSV_ABI sceKernelOpen(const char* path, s32 flags, /* SceKernelMode*/ u } s32 PS4_SYSV_ABI close(s32 fd) { - if (fd < 3) { - // This is technically possible, but it's usually caused by some stubbed function instead. - LOG_WARNING(Kernel_Fs, "called on an std handle, fd = {}", fd); - } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); if (file == nullptr) { *__Error() = POSIX_EBADF; return -1; } + if (fd < 3) { + // This is technically possible, but it's usually caused by some stubbed function instead. + LOG_WARNING(Kernel_Fs, "called on an std handle, fd = {}", fd); + } if (file->type == Core::FileSys::FileType::Regular) { file->f.Close(); } diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index 7b3ac5646..8a0c91479 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -19,7 +19,7 @@ namespace Libraries::Kernel { u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize() { - LOG_WARNING(Kernel_Vmm, "called"); + LOG_TRACE(Kernel_Vmm, "called"); const auto* memory = Core::Memory::Instance(); return memory->GetTotalDirectSize(); } @@ -106,12 +106,6 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE if (physAddrOut == nullptr || sizeOut == nullptr) { return ORBIS_KERNEL_ERROR_EINVAL; } - if (searchEnd > sceKernelGetDirectMemorySize()) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - if (searchEnd <= searchStart) { - return ORBIS_KERNEL_ERROR_ENOMEM; - } auto* memory = Core::Memory::Instance(); diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index cd0fe650b..3f5baf640 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -27,6 +27,7 @@ #include "core/libraries/network/netctl.h" #include "core/libraries/network/ssl.h" #include "core/libraries/network/ssl2.h" +#include "core/libraries/np_auth/np_auth.h" #include "core/libraries/np_common/np_common.h" #include "core/libraries/np_manager/np_manager.h" #include "core/libraries/np_party/np_party.h" @@ -50,6 +51,7 @@ #include "core/libraries/system/sysmodule.h" #include "core/libraries/system/systemservice.h" #include "core/libraries/system/userservice.h" +#include "core/libraries/ulobjmgr/ulobjmgr.h" #include "core/libraries/usbd/usbd.h" #include "core/libraries/videodec/videodec.h" #include "core/libraries/videodec/videodec2.h" @@ -87,6 +89,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::NpScore::RegisterlibSceNpScore(sym); Libraries::NpTrophy::RegisterlibSceNpTrophy(sym); Libraries::NpWebApi::RegisterlibSceNpWebApi(sym); + Libraries::NpAuth::RegisterlibSceNpAuth(sym); Libraries::ScreenShot::RegisterlibSceScreenShot(sym); Libraries::AppContent::RegisterlibSceAppContent(sym); Libraries::PngDec::RegisterlibScePngDec(sym); @@ -116,6 +119,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Zlib::RegisterlibSceZlib(sym); Libraries::Hmd::RegisterlibSceHmd(sym); Libraries::DiscMap::RegisterlibSceDiscMap(sym); + Libraries::Ulobjmgr::RegisterlibSceUlobjmgr(sym); } } // namespace Libraries diff --git a/src/core/libraries/move/move.cpp b/src/core/libraries/move/move.cpp index 626fed9b4..500d89586 100644 --- a/src/core/libraries/move/move.cpp +++ b/src/core/libraries/move/move.cpp @@ -9,7 +9,7 @@ namespace Libraries::Move { int PS4_SYSV_ABI sceMoveOpen() { - LOG_ERROR(Lib_Move, "(STUBBED) called"); + LOG_TRACE(Lib_Move, "(STUBBED) called"); return ORBIS_FAIL; } @@ -18,6 +18,11 @@ int PS4_SYSV_ABI sceMoveGetDeviceInfo() { return ORBIS_OK; } +int PS4_SYSV_ABI sceMoveReadStateLatest() { + LOG_TRACE(Lib_Move, "(STUBBED) called"); + return ORBIS_OK; +} + int PS4_SYSV_ABI sceMoveReadStateRecent() { LOG_TRACE(Lib_Move, "(STUBBED) called"); return ORBIS_OK; @@ -36,6 +41,7 @@ int PS4_SYSV_ABI sceMoveInit() { void RegisterlibSceMove(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("HzC60MfjJxU", "libSceMove", 1, "libSceMove", 1, 1, sceMoveOpen); LIB_FUNCTION("GWXTyxs4QbE", "libSceMove", 1, "libSceMove", 1, 1, sceMoveGetDeviceInfo); + LIB_FUNCTION("ttU+JOhShl4", "libSceMove", 1, "libSceMove", 1, 1, sceMoveReadStateLatest); LIB_FUNCTION("f2bcpK6kJfg", "libSceMove", 1, "libSceMove", 1, 1, sceMoveReadStateRecent); LIB_FUNCTION("tsZi60H4ypY", "libSceMove", 1, "libSceMove", 1, 1, sceMoveTerm); LIB_FUNCTION("j1ITE-EoJmE", "libSceMove", 1, "libSceMove", 1, 1, sceMoveInit); diff --git a/src/core/libraries/ngs2/ngs2.cpp b/src/core/libraries/ngs2/ngs2.cpp index 0b42e2471..743be5fd6 100644 --- a/src/core/libraries/ngs2/ngs2.cpp +++ b/src/core/libraries/ngs2/ngs2.cpp @@ -18,36 +18,32 @@ namespace Libraries::Ngs2 { s32 PS4_SYSV_ABI sceNgs2CalcWaveformBlock(const OrbisNgs2WaveformFormat* format, u32 samplePos, u32 numSamples, OrbisNgs2WaveformBlock* outBlock) { - LOG_INFO(Lib_Ngs2, "samplePos = {}, numSamples = {}", samplePos, numSamples); + LOG_ERROR(Lib_Ngs2, "samplePos = {}, numSamples = {}", samplePos, numSamples); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2GetWaveformFrameInfo(const OrbisNgs2WaveformFormat* format, u32* outFrameSize, u32* outNumFrameSamples, u32* outUnitsPerFrame, u32* outNumDelaySamples) { - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2ParseWaveformData(const void* data, size_t dataSize, OrbisNgs2WaveformInfo* outInfo) { - LOG_INFO(Lib_Ngs2, "dataSize = {}", dataSize); + LOG_ERROR(Lib_Ngs2, "dataSize = {}", dataSize); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2ParseWaveformFile(const char* path, u64 offset, OrbisNgs2WaveformInfo* outInfo) { - LOG_INFO(Lib_Ngs2, "path = {}, offset = {}", path, offset); + LOG_ERROR(Lib_Ngs2, "path = {}, offset = {}", path, offset); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2ParseWaveformUser(OrbisNgs2ParseReadHandler handler, uintptr_t userData, OrbisNgs2WaveformInfo* outInfo) { - LOG_INFO(Lib_Ngs2, "userData = {}", userData); - if (!handler) { - LOG_ERROR(Lib_Ngs2, "handler is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_HANDLE; - } + LOG_ERROR(Lib_Ngs2, "userData = {}", userData); return ORBIS_OK; } @@ -55,7 +51,7 @@ s32 PS4_SYSV_ABI sceNgs2RackCreate(OrbisNgs2Handle systemHandle, u32 rackId, const OrbisNgs2RackOption* option, const OrbisNgs2ContextBufferInfo* bufferInfo, OrbisNgs2Handle* outHandle) { - LOG_INFO(Lib_Ngs2, "rackId = {}", rackId); + LOG_ERROR(Lib_Ngs2, "rackId = {}", rackId); if (!systemHandle) { LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; @@ -67,7 +63,7 @@ s32 PS4_SYSV_ABI sceNgs2RackCreateWithAllocator(OrbisNgs2Handle systemHandle, u3 const OrbisNgs2RackOption* option, const OrbisNgs2BufferAllocator* allocator, OrbisNgs2Handle* outHandle) { - LOG_INFO(Lib_Ngs2, "rackId = {}", rackId); + LOG_ERROR(Lib_Ngs2, "rackId = {}", rackId); if (!systemHandle) { LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; @@ -77,73 +73,45 @@ s32 PS4_SYSV_ABI sceNgs2RackCreateWithAllocator(OrbisNgs2Handle systemHandle, u3 s32 PS4_SYSV_ABI sceNgs2RackDestroy(OrbisNgs2Handle rackHandle, OrbisNgs2ContextBufferInfo* outBufferInfo) { - if (!rackHandle) { - LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; - } - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2RackGetInfo(OrbisNgs2Handle rackHandle, OrbisNgs2RackInfo* outInfo, size_t infoSize) { - LOG_INFO(Lib_Ngs2, "infoSize = {}", infoSize); - if (!rackHandle) { - LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; - } + LOG_ERROR(Lib_Ngs2, "infoSize = {}", infoSize); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2RackGetUserData(OrbisNgs2Handle rackHandle, uintptr_t* outUserData) { - if (!rackHandle) { - LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; - } - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2RackGetVoiceHandle(OrbisNgs2Handle rackHandle, u32 voiceIndex, OrbisNgs2Handle* outHandle) { - LOG_INFO(Lib_Ngs2, "voiceIndex = {}", voiceIndex); - if (!rackHandle) { - LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; - } + LOG_DEBUG(Lib_Ngs2, "(STUBBED) voiceIndex = {}", voiceIndex); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2RackLock(OrbisNgs2Handle rackHandle) { - if (!rackHandle) { - LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; - } - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2RackQueryBufferSize(u32 rackId, const OrbisNgs2RackOption* option, OrbisNgs2ContextBufferInfo* outBufferInfo) { - LOG_INFO(Lib_Ngs2, "rackId = {}", rackId); + LOG_ERROR(Lib_Ngs2, "rackId = {}", rackId); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2RackSetUserData(OrbisNgs2Handle rackHandle, uintptr_t userData) { - LOG_INFO(Lib_Ngs2, "userData = {}", userData); - if (!rackHandle) { - LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; - } + LOG_ERROR(Lib_Ngs2, "userData = {}", userData); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2RackUnlock(OrbisNgs2Handle rackHandle) { - if (!rackHandle) { - LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; - } - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } @@ -188,17 +156,17 @@ s32 PS4_SYSV_ABI sceNgs2SystemCreateWithAllocator(const OrbisNgs2SystemOption* o OrbisNgs2BufferAllocHandler hostAlloc = allocator->allocHandler; if (outHandle) { OrbisNgs2BufferFreeHandler hostFree = allocator->freeHandler; - OrbisNgs2ContextBufferInfo* bufferInfo = 0; - result = SystemSetup(option, bufferInfo, 0, 0); + OrbisNgs2ContextBufferInfo bufferInfo; + result = SystemSetup(option, &bufferInfo, 0, 0); if (result >= 0) { uintptr_t sysUserData = allocator->userData; - result = hostAlloc(bufferInfo); + result = Core::ExecuteGuest(hostAlloc, &bufferInfo); if (result >= 0) { OrbisNgs2Handle* handleCopy = outHandle; - result = SystemSetup(option, bufferInfo, hostFree, handleCopy); + result = SystemSetup(option, &bufferInfo, hostFree, handleCopy); if (result < 0) { if (hostFree) { - hostFree(bufferInfo); + Core::ExecuteGuest(hostFree, &bufferInfo); } } } @@ -226,13 +194,13 @@ s32 PS4_SYSV_ABI sceNgs2SystemDestroy(OrbisNgs2Handle systemHandle, } s32 PS4_SYSV_ABI sceNgs2SystemEnumHandles(OrbisNgs2Handle* aOutHandle, u32 maxHandles) { - LOG_INFO(Lib_Ngs2, "maxHandles = {}", maxHandles); + LOG_ERROR(Lib_Ngs2, "maxHandles = {}", maxHandles); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2SystemEnumRackHandles(OrbisNgs2Handle systemHandle, OrbisNgs2Handle* aOutHandle, u32 maxHandles) { - LOG_INFO(Lib_Ngs2, "maxHandles = {}", maxHandles); + LOG_ERROR(Lib_Ngs2, "maxHandles = {}", maxHandles); if (!systemHandle) { LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; @@ -242,11 +210,7 @@ s32 PS4_SYSV_ABI sceNgs2SystemEnumRackHandles(OrbisNgs2Handle systemHandle, s32 PS4_SYSV_ABI sceNgs2SystemGetInfo(OrbisNgs2Handle rackHandle, OrbisNgs2SystemInfo* outInfo, size_t infoSize) { - LOG_INFO(Lib_Ngs2, "infoSize = {}", infoSize); - if (!rackHandle) { - LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; - } + LOG_ERROR(Lib_Ngs2, "infoSize = {}", infoSize); return ORBIS_OK; } @@ -255,7 +219,7 @@ s32 PS4_SYSV_ABI sceNgs2SystemGetUserData(OrbisNgs2Handle systemHandle, uintptr_ LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; } - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } @@ -264,7 +228,7 @@ s32 PS4_SYSV_ABI sceNgs2SystemLock(OrbisNgs2Handle systemHandle) { LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; } - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } @@ -285,7 +249,7 @@ s32 PS4_SYSV_ABI sceNgs2SystemQueryBufferSize(const OrbisNgs2SystemOption* optio s32 PS4_SYSV_ABI sceNgs2SystemRender(OrbisNgs2Handle systemHandle, const OrbisNgs2RenderBufferInfo* aBufferInfo, u32 numBufferInfo) { - LOG_INFO(Lib_Ngs2, "numBufferInfo = {}", numBufferInfo); + LOG_DEBUG(Lib_Ngs2, "(STUBBED) numBufferInfo = {}", numBufferInfo); if (!systemHandle) { LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; @@ -308,7 +272,7 @@ static s32 PS4_SYSV_ABI sceNgs2SystemResetOption(OrbisNgs2SystemOption* outOptio } s32 PS4_SYSV_ABI sceNgs2SystemSetGrainSamples(OrbisNgs2Handle systemHandle, u32 numSamples) { - LOG_INFO(Lib_Ngs2, "numSamples = {}", numSamples); + LOG_ERROR(Lib_Ngs2, "numSamples = {}", numSamples); if (!systemHandle) { LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; @@ -317,7 +281,7 @@ s32 PS4_SYSV_ABI sceNgs2SystemSetGrainSamples(OrbisNgs2Handle systemHandle, u32 } s32 PS4_SYSV_ABI sceNgs2SystemSetSampleRate(OrbisNgs2Handle systemHandle, u32 sampleRate) { - LOG_INFO(Lib_Ngs2, "sampleRate = {}", sampleRate); + LOG_ERROR(Lib_Ngs2, "sampleRate = {}", sampleRate); if (!systemHandle) { LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; @@ -326,7 +290,7 @@ s32 PS4_SYSV_ABI sceNgs2SystemSetSampleRate(OrbisNgs2Handle systemHandle, u32 sa } s32 PS4_SYSV_ABI sceNgs2SystemSetUserData(OrbisNgs2Handle systemHandle, uintptr_t userData) { - LOG_INFO(Lib_Ngs2, "userData = {}", userData); + LOG_ERROR(Lib_Ngs2, "userData = {}", userData); if (!systemHandle) { LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; @@ -339,66 +303,42 @@ s32 PS4_SYSV_ABI sceNgs2SystemUnlock(OrbisNgs2Handle systemHandle) { LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; } - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2VoiceControl(OrbisNgs2Handle voiceHandle, const OrbisNgs2VoiceParamHeader* paramList) { - if (!voiceHandle) { - LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; - } - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2VoiceGetMatrixInfo(OrbisNgs2Handle voiceHandle, u32 matrixId, OrbisNgs2VoiceMatrixInfo* outInfo, size_t outInfoSize) { - LOG_INFO(Lib_Ngs2, "matrixId = {}, outInfoSize = {}", matrixId, outInfoSize); - if (!voiceHandle) { - LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; - } + LOG_ERROR(Lib_Ngs2, "matrixId = {}, outInfoSize = {}", matrixId, outInfoSize); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2VoiceGetOwner(OrbisNgs2Handle voiceHandle, OrbisNgs2Handle* outRackHandle, u32* outVoiceId) { - if (!voiceHandle) { - LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; - } - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2VoiceGetPortInfo(OrbisNgs2Handle voiceHandle, u32 port, OrbisNgs2VoicePortInfo* outInfo, size_t outInfoSize) { - LOG_INFO(Lib_Ngs2, "port = {}, outInfoSize = {}", port, outInfoSize); - if (!voiceHandle) { - LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; - } + LOG_ERROR(Lib_Ngs2, "port = {}, outInfoSize = {}", port, outInfoSize); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2VoiceGetState(OrbisNgs2Handle voiceHandle, OrbisNgs2VoiceState* outState, size_t stateSize) { - LOG_INFO(Lib_Ngs2, "stateSize = {}", stateSize); - if (!voiceHandle) { - LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; - } + LOG_ERROR(Lib_Ngs2, "stateSize = {}", stateSize); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2VoiceGetStateFlags(OrbisNgs2Handle voiceHandle, u32* outStateFlags) { - if (!voiceHandle) { - LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; - } - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } @@ -407,36 +347,32 @@ s32 PS4_SYSV_ABI sceNgs2VoiceGetStateFlags(OrbisNgs2Handle voiceHandle, u32* out s32 PS4_SYSV_ABI sceNgs2CustomRackGetModuleInfo(OrbisNgs2Handle rackHandle, u32 moduleIndex, OrbisNgs2CustomModuleInfo* outInfo, size_t infoSize) { - LOG_INFO(Lib_Ngs2, "moduleIndex = {}, infoSize = {}", moduleIndex, infoSize); - if (!rackHandle) { - LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); - return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; - } + LOG_ERROR(Lib_Ngs2, "moduleIndex = {}, infoSize = {}", moduleIndex, infoSize); return ORBIS_OK; } // Ngs2Geom s32 PS4_SYSV_ABI sceNgs2GeomResetListenerParam(OrbisNgs2GeomListenerParam* outListenerParam) { - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2GeomResetSourceParam(OrbisNgs2GeomSourceParam* outSourceParam) { - LOG_INFO(Lib_Ngs2, "called"); + LOG_ERROR(Lib_Ngs2, "called"); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2GeomCalcListener(const OrbisNgs2GeomListenerParam* param, OrbisNgs2GeomListenerWork* outWork, u32 flags) { - LOG_INFO(Lib_Ngs2, "flags = {}", flags); + LOG_ERROR(Lib_Ngs2, "flags = {}", flags); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2GeomApply(const OrbisNgs2GeomListenerWork* listener, const OrbisNgs2GeomSourceParam* source, OrbisNgs2GeomAttribute* outAttrib, u32 flags) { - LOG_INFO(Lib_Ngs2, "flags = {}", flags); + LOG_ERROR(Lib_Ngs2, "flags = {}", flags); return ORBIS_OK; } @@ -444,15 +380,15 @@ s32 PS4_SYSV_ABI sceNgs2GeomApply(const OrbisNgs2GeomListenerWork* listener, s32 PS4_SYSV_ABI sceNgs2PanInit(OrbisNgs2PanWork* work, const float* aSpeakerAngle, float unitAngle, u32 numSpeakers) { - LOG_INFO(Lib_Ngs2, "aSpeakerAngle = {}, unitAngle = {}, numSpeakers = {}", *aSpeakerAngle, - unitAngle, numSpeakers); + LOG_ERROR(Lib_Ngs2, "aSpeakerAngle = {}, unitAngle = {}, numSpeakers = {}", *aSpeakerAngle, + unitAngle, numSpeakers); return ORBIS_OK; } s32 PS4_SYSV_ABI sceNgs2PanGetVolumeMatrix(OrbisNgs2PanWork* work, const OrbisNgs2PanParam* aParam, u32 numParams, u32 matrixFormat, float* outVolumeMatrix) { - LOG_INFO(Lib_Ngs2, "numParams = {}, matrixFormat = {}", numParams, matrixFormat); + LOG_ERROR(Lib_Ngs2, "numParams = {}, matrixFormat = {}", numParams, matrixFormat); return ORBIS_OK; } diff --git a/src/core/libraries/ngs2/ngs2.h b/src/core/libraries/ngs2/ngs2.h index a34bf21d4..6c499d974 100644 --- a/src/core/libraries/ngs2/ngs2.h +++ b/src/core/libraries/ngs2/ngs2.h @@ -16,7 +16,8 @@ class SymbolsResolver; namespace Libraries::Ngs2 { -typedef s32 (*OrbisNgs2ParseReadHandler)(uintptr_t userData, u32 offset, void* data, size_t size); +using OrbisNgs2ParseReadHandler = s32 PS4_SYSV_ABI (*)(uintptr_t user_data, u32 offset, void* data, + size_t size); enum class OrbisNgs2HandleType : u32 { Invalid = 0, @@ -90,7 +91,7 @@ struct OrbisNgs2UserFxProcessContext { u32 sampleRate; }; -typedef s32 (*OrbisNgs2UserFxProcessHandler)(OrbisNgs2UserFxProcessContext* context); +using OrbisNgs2UserFxProcessHandler = s32 PS4_SYSV_ABI (*)(OrbisNgs2UserFxProcessContext* context); struct OrbisNgs2UserFx2SetupContext { void* common; @@ -102,7 +103,7 @@ struct OrbisNgs2UserFx2SetupContext { u64 reserved[4]; }; -typedef s32 (*OrbisNgs2UserFx2SetupHandler)(OrbisNgs2UserFx2SetupContext* context); +using OrbisNgs2UserFx2SetupHandler = s32 PS4_SYSV_ABI (*)(OrbisNgs2UserFx2SetupContext* context); struct OrbisNgs2UserFx2CleanupContext { void* common; @@ -114,7 +115,8 @@ struct OrbisNgs2UserFx2CleanupContext { u64 reserved[4]; }; -typedef s32 (*OrbisNgs2UserFx2CleanupHandler)(OrbisNgs2UserFx2CleanupContext* context); +using OrbisNgs2UserFx2CleanupHandler = + s32 PS4_SYSV_ABI (*)(OrbisNgs2UserFx2CleanupContext* context); struct OrbisNgs2UserFx2ControlContext { const void* data; @@ -125,7 +127,8 @@ struct OrbisNgs2UserFx2ControlContext { u64 reserved[4]; }; -typedef s32 (*OrbisNgs2UserFx2ControlHandler)(OrbisNgs2UserFx2ControlContext* context); +using OrbisNgs2UserFx2ControlHandler = + s32 PS4_SYSV_ABI (*)(OrbisNgs2UserFx2ControlContext* context); struct OrbisNgs2UserFx2ProcessContext { float** aChannelData; @@ -143,7 +146,8 @@ struct OrbisNgs2UserFx2ProcessContext { u64 reserved2[4]; }; -typedef s32 (*OrbisNgs2UserFx2ProcessHandler)(OrbisNgs2UserFx2ProcessContext* context); +using OrbisNgs2UserFx2ProcessHandler = + s32 PS4_SYSV_ABI (*)(OrbisNgs2UserFx2ProcessContext* context); struct OrbisNgs2BufferAllocator { OrbisNgs2BufferAllocHandler allocHandler; @@ -237,7 +241,7 @@ struct OrbisNgs2VoiceCallbackInfo { } param; }; -typedef void (*OrbisNgs2VoiceCallbackHandler)(const OrbisNgs2VoiceCallbackInfo* info); +using OrbisNgs2VoiceCallbackHandler = void PS4_SYSV_ABI (*)(const OrbisNgs2VoiceCallbackInfo* info); struct OrbisNgs2VoiceCallbackParam { OrbisNgs2VoiceParamHeader header; diff --git a/src/core/libraries/ngs2/ngs2_impl.h b/src/core/libraries/ngs2/ngs2_impl.h index 7be0f89cc..a433e84fd 100644 --- a/src/core/libraries/ngs2/ngs2_impl.h +++ b/src/core/libraries/ngs2/ngs2_impl.h @@ -30,8 +30,9 @@ struct OrbisNgs2SystemOption { u32 aReserved[6]; }; -typedef s32 (*OrbisNgs2BufferAllocHandler)(OrbisNgs2ContextBufferInfo* ioBufferInfo); -typedef s32 (*OrbisNgs2BufferFreeHandler)(OrbisNgs2ContextBufferInfo* ioBufferInfo); +using OrbisNgs2BufferAllocHandler = + s32 PS4_SYSV_ABI (*)(OrbisNgs2ContextBufferInfo* io_buffer_info); +using OrbisNgs2BufferFreeHandler = s32 PS4_SYSV_ABI (*)(OrbisNgs2ContextBufferInfo* io_buffer_info); struct OrbisNgs2SystemInfo { char name[ORBIS_NGS2_SYSTEM_NAME_LENGTH]; // 0 diff --git a/src/core/libraries/ngs2/ngs2_report.h b/src/core/libraries/ngs2/ngs2_report.h index 88f6d1df0..0f46f5081 100644 --- a/src/core/libraries/ngs2/ngs2_report.h +++ b/src/core/libraries/ngs2/ngs2_report.h @@ -18,7 +18,8 @@ struct OrbisNgs2ReportDataHeader { s32 result; }; -typedef void (*OrbisNgs2ReportHandler)(const OrbisNgs2ReportDataHeader* data, uintptr_t userData); +using OrbisNgs2ReportHandler = void PS4_SYSV_ABI (*)(const OrbisNgs2ReportDataHeader* data, + uintptr_t user_data); struct OrbisNgs2ReportMessageData { OrbisNgs2ReportDataHeader header; diff --git a/src/core/libraries/np_auth/np_auth.cpp b/src/core/libraries/np_auth/np_auth.cpp new file mode 100644 index 000000000..9ec986f3c --- /dev/null +++ b/src/core/libraries/np_auth/np_auth.cpp @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/np_auth/np_auth.h" + +namespace Libraries::NpAuth { + +s32 PS4_SYSV_ABI sceNpAuthGetAuthorizationCode() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthGetIdToken() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthAbortRequest() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthCreateAsyncRequest() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthCreateRequest() { + LOG_WARNING(Lib_NpAuth, "(DUMMY) called"); + return 1; +} + +s32 PS4_SYSV_ABI sceNpAuthDeleteRequest(s32 id) { + LOG_WARNING(Lib_NpAuth, "(DUMMY) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthGetAuthorizationCodeA() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthGetAuthorizationCodeV3() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthGetIdTokenA() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthGetIdTokenV3() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthPollAsync() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthSetTimeout() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNpAuthWaitAsync() { + LOG_ERROR(Lib_NpAuth, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceNpAuth(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("KxGkOrQJTqY", "libSceNpAuthCompat", 1, "libSceNpAuth", 1, 1, + sceNpAuthGetAuthorizationCode); + LIB_FUNCTION("uaB-LoJqHis", "libSceNpAuthCompat", 1, "libSceNpAuth", 1, 1, sceNpAuthGetIdToken); + LIB_FUNCTION("cE7wIsqXdZ8", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, sceNpAuthAbortRequest); + LIB_FUNCTION("N+mr7GjTvr8", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, + sceNpAuthCreateAsyncRequest); + LIB_FUNCTION("6bwFkosYRQg", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, sceNpAuthCreateRequest); + LIB_FUNCTION("H8wG9Bk-nPc", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, sceNpAuthDeleteRequest); + LIB_FUNCTION("KxGkOrQJTqY", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, + sceNpAuthGetAuthorizationCode); + LIB_FUNCTION("qAUXQ9GdWp8", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, + sceNpAuthGetAuthorizationCodeA); + LIB_FUNCTION("KI4dHLlTNl0", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, + sceNpAuthGetAuthorizationCodeV3); + LIB_FUNCTION("uaB-LoJqHis", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, sceNpAuthGetIdToken); + LIB_FUNCTION("CocbHVIKPE8", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, sceNpAuthGetIdTokenA); + LIB_FUNCTION("RdsFVsgSpZY", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, sceNpAuthGetIdTokenV3); + LIB_FUNCTION("gjSyfzSsDcE", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, sceNpAuthPollAsync); + LIB_FUNCTION("PM3IZCw-7m0", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, sceNpAuthSetTimeout); + LIB_FUNCTION("SK-S7daqJSE", "libSceNpAuth", 1, "libSceNpAuth", 1, 1, sceNpAuthWaitAsync); +}; + +} // namespace Libraries::NpAuth \ No newline at end of file diff --git a/src/core/libraries/np_auth/np_auth.h b/src/core/libraries/np_auth/np_auth.h new file mode 100644 index 000000000..a6a66b452 --- /dev/null +++ b/src/core/libraries/np_auth/np_auth.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::NpAuth { + +s32 PS4_SYSV_ABI sceNpAuthGetAuthorizationCode(); +s32 PS4_SYSV_ABI sceNpAuthGetIdToken(); +s32 PS4_SYSV_ABI sceNpAuthAbortRequest(); +s32 PS4_SYSV_ABI sceNpAuthCreateAsyncRequest(); +s32 PS4_SYSV_ABI sceNpAuthCreateRequest(); +s32 PS4_SYSV_ABI sceNpAuthDeleteRequest(s32 id); +s32 PS4_SYSV_ABI sceNpAuthGetAuthorizationCodeA(); +s32 PS4_SYSV_ABI sceNpAuthGetAuthorizationCodeV3(); +s32 PS4_SYSV_ABI sceNpAuthGetIdTokenA(); +s32 PS4_SYSV_ABI sceNpAuthGetIdTokenV3(); +s32 PS4_SYSV_ABI sceNpAuthPollAsync(); +s32 PS4_SYSV_ABI sceNpAuthSetTimeout(); +s32 PS4_SYSV_ABI sceNpAuthWaitAsync(); + +void RegisterlibSceNpAuth(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::NpAuth \ No newline at end of file diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 2e5973144..5dfc68e90 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -250,7 +250,6 @@ int PS4_SYSV_ABI scePadMbusTerm() { } int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenParam* pParam) { - LOG_INFO(Lib_Pad, "(DUMMY) called user_id = {} type = {} index = {}", userId, type, index); if (userId == -1) { return ORBIS_PAD_ERROR_DEVICE_NO_HANDLE; } @@ -261,6 +260,7 @@ int PS4_SYSV_ABI scePadOpen(s32 userId, s32 type, s32 index, const OrbisPadOpenP if (type != ORBIS_PAD_PORT_TYPE_STANDARD && type != ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL) return ORBIS_PAD_ERROR_DEVICE_NOT_CONNECTED; } + LOG_INFO(Lib_Pad, "(DUMMY) called user_id = {} type = {} index = {}", userId, type, index); scePadResetLightBar(1); return 1; // dummy } diff --git a/src/core/libraries/ulobjmgr/ulobjmgr.cpp b/src/core/libraries/ulobjmgr/ulobjmgr.cpp new file mode 100644 index 000000000..cad6feda9 --- /dev/null +++ b/src/core/libraries/ulobjmgr/ulobjmgr.cpp @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/posix_error.h" +#include "core/libraries/libs.h" +#include "core/libraries/ulobjmgr/ulobjmgr.h" + +namespace Libraries::Ulobjmgr { + +s32 PS4_SYSV_ABI Func_046DBA8411A2365C(u64 arg0, s32 arg1, u32* arg2) { + if (arg0 == 0 || arg1 == 0 || arg2 == nullptr) { + return POSIX_EINVAL; + } + *arg2 = 0; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1D9F50D9CFB8054E() { + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_4A67FE7D435B94F7(u32 arg0) { + if (arg0 >= 0x4000) { + return POSIX_EINVAL; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_4B07893BBB77A649(u64 arg0) { + if (arg0 == 0) { + return POSIX_EINVAL; + } + return ORBIS_OK; +} + +void RegisterlibSceUlobjmgr(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("BG26hBGiNlw", "ulobjmgr", 1, "ulobjmgr", 1, 1, Func_046DBA8411A2365C); + LIB_FUNCTION("HZ9Q2c+4BU4", "ulobjmgr", 1, "ulobjmgr", 1, 1, Func_1D9F50D9CFB8054E); + LIB_FUNCTION("Smf+fUNblPc", "ulobjmgr", 1, "ulobjmgr", 1, 1, Func_4A67FE7D435B94F7); + LIB_FUNCTION("SweJO7t3pkk", "ulobjmgr", 1, "ulobjmgr", 1, 1, Func_4B07893BBB77A649); +}; + +} // namespace Libraries::Ulobjmgr diff --git a/src/core/libraries/ulobjmgr/ulobjmgr.h b/src/core/libraries/ulobjmgr/ulobjmgr.h new file mode 100644 index 000000000..9a1440e5c --- /dev/null +++ b/src/core/libraries/ulobjmgr/ulobjmgr.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Ulobjmgr { +void RegisterlibSceUlobjmgr(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Ulobjmgr \ No newline at end of file diff --git a/src/core/libraries/usbd/usbd.cpp b/src/core/libraries/usbd/usbd.cpp index fdfa50b23..e5f6151eb 100644 --- a/src/core/libraries/usbd/usbd.cpp +++ b/src/core/libraries/usbd/usbd.cpp @@ -7,311 +7,455 @@ #include "core/libraries/libs.h" #include "usbd.h" +#include +#include + namespace Libraries::Usbd { -int PS4_SYSV_ABI sceUsbdAllocTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 libusb_to_orbis_error(int retVal) { + if (retVal == LIBUSB_ERROR_OTHER) + return 0x802400FF; + if (retVal < 0) { + LOG_ERROR(Lib_Usbd, "libusb returned: {}", libusb_error_name(retVal)); + return 0x80240000 - retVal; + } + + return retVal; } -int PS4_SYSV_ABI sceUsbdAttachKernelDriver() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +libusb_context* g_libusb_context; + +#if defined(_WIN32) +bool s_has_removed_driver = false; +#endif + +s32 PS4_SYSV_ABI sceUsbdInit() { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_init(&g_libusb_context)); } -int PS4_SYSV_ABI sceUsbdBulkTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdExit() { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_exit(g_libusb_context); } -int PS4_SYSV_ABI sceUsbdCancelTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s64 PS4_SYSV_ABI sceUsbdGetDeviceList(SceUsbdDevice*** list) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_get_device_list(g_libusb_context, list)); } -int PS4_SYSV_ABI sceUsbdCheckConnected() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdFreeDeviceList(SceUsbdDevice** list, s32 unref_devices) { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_free_device_list(list, unref_devices); } -int PS4_SYSV_ABI sceUsbdClaimInterface() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +SceUsbdDevice* PS4_SYSV_ABI sceUsbdRefDevice(SceUsbdDevice* device) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_ref_device(device); } -int PS4_SYSV_ABI sceUsbdClearHalt() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdUnrefDevice(SceUsbdDevice* device) { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_unref_device(device); } -int PS4_SYSV_ABI sceUsbdClose() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdGetConfiguration(SceUsbdDeviceHandle* dev_handle, s32* config) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_get_configuration(dev_handle, config)); } -int PS4_SYSV_ABI sceUsbdControlTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdGetDeviceDescriptor(SceUsbdDevice* device, SceUsbdDeviceDescriptor* desc) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_get_device_descriptor(device, desc)); } -int PS4_SYSV_ABI sceUsbdControlTransferGetData() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdGetActiveConfigDescriptor(SceUsbdDevice* device, + SceUsbdConfigDescriptor** config) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_get_active_config_descriptor(device, config)); } -int PS4_SYSV_ABI sceUsbdControlTransferGetSetup() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdGetConfigDescriptor(SceUsbdDevice* device, u8 config_index, + SceUsbdConfigDescriptor** config) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_get_config_descriptor(device, config_index, config)); } -int PS4_SYSV_ABI sceUsbdDetachKernelDriver() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdGetConfigDescriptorByValue(SceUsbdDevice* device, u8 bConfigurationValue, + SceUsbdConfigDescriptor** config) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error( + libusb_get_config_descriptor_by_value(device, bConfigurationValue, config)); } -int PS4_SYSV_ABI sceUsbdEventHandlerActive() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdFreeConfigDescriptor(SceUsbdConfigDescriptor* config) { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_free_config_descriptor(config); } -int PS4_SYSV_ABI sceUsbdEventHandlingOk() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +u8 PS4_SYSV_ABI sceUsbdGetBusNumber(SceUsbdDevice* device) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_get_bus_number(device); } -int PS4_SYSV_ABI sceUsbdExit() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +u8 PS4_SYSV_ABI sceUsbdGetDeviceAddress(SceUsbdDevice* device) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_get_device_address(device); } -int PS4_SYSV_ABI sceUsbdFillBulkTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +SceUsbdSpeed PS4_SYSV_ABI sceUsbdGetDeviceSpeed(SceUsbdDevice* device) { + LOG_DEBUG(Lib_Usbd, "called"); + + return static_cast(libusb_get_device_speed(device)); } -int PS4_SYSV_ABI sceUsbdFillControlSetup() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdGetMaxPacketSize(SceUsbdDevice* device, u8 endpoint) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_get_max_packet_size(device, endpoint)); } -int PS4_SYSV_ABI sceUsbdFillControlTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdGetMaxIsoPacketSize(SceUsbdDevice* device, u8 endpoint) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_get_max_iso_packet_size(device, endpoint)); } -int PS4_SYSV_ABI sceUsbdFillInterruptTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdOpen(SceUsbdDevice* device, SceUsbdDeviceHandle** dev_handle) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_open(device, dev_handle)); } -int PS4_SYSV_ABI sceUsbdFillIsoTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdClose(SceUsbdDeviceHandle* dev_handle) { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_close(dev_handle); } -int PS4_SYSV_ABI sceUsbdFreeConfigDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +SceUsbdDevice* PS4_SYSV_ABI sceUsbdGetDevice(SceUsbdDeviceHandle* dev_handle) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_get_device(dev_handle); } -int PS4_SYSV_ABI sceUsbdFreeDeviceList() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdSetConfiguration(SceUsbdDeviceHandle* dev_handle, s32 config) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_set_configuration(dev_handle, config)); } -int PS4_SYSV_ABI sceUsbdFreeTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdClaimInterface(SceUsbdDeviceHandle* dev_handle, s32 interface_number) { + LOG_DEBUG(Lib_Usbd, "called"); + + if (sceUsbdKernelDriverActive(dev_handle, interface_number)) { + sceUsbdDetachKernelDriver(dev_handle, interface_number); + } + + return libusb_to_orbis_error(libusb_claim_interface(dev_handle, interface_number)); } -int PS4_SYSV_ABI sceUsbdGetActiveConfigDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdReleaseInterface(SceUsbdDeviceHandle* dev_handle, s32 interface_number) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_release_interface(dev_handle, interface_number)); } -int PS4_SYSV_ABI sceUsbdGetBusNumber() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +SceUsbdDeviceHandle* PS4_SYSV_ABI sceUsbdOpenDeviceWithVidPid(u16 vendor_id, u16 product_id) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_open_device_with_vid_pid(g_libusb_context, vendor_id, product_id); } -int PS4_SYSV_ABI sceUsbdGetConfigDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdSetInterfaceAltSetting(SceUsbdDeviceHandle* dev_handle, + int interface_number, int alternate_setting) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error( + libusb_set_interface_alt_setting(dev_handle, interface_number, alternate_setting)); } -int PS4_SYSV_ABI sceUsbdGetConfigDescriptorByValue() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdClearHalt(SceUsbdDeviceHandle* dev_handle, uint8_t endpoint) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_clear_halt(dev_handle, endpoint)); } -int PS4_SYSV_ABI sceUsbdGetConfiguration() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdResetDevice(SceUsbdDeviceHandle* dev_handle) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_reset_device(dev_handle)); } -int PS4_SYSV_ABI sceUsbdGetDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdKernelDriverActive(SceUsbdDeviceHandle* dev_handle, int interface_number) { + LOG_DEBUG(Lib_Usbd, "called"); + +#if defined(_WIN32) + if (!s_has_removed_driver) + return 1; + else + return 0; +#endif + + return libusb_to_orbis_error(libusb_kernel_driver_active(dev_handle, interface_number)); } -int PS4_SYSV_ABI sceUsbdGetDevice() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdDetachKernelDriver(SceUsbdDeviceHandle* dev_handle, int interface_number) { + LOG_DEBUG(Lib_Usbd, "called"); + +#if defined(_WIN32) + s_has_removed_driver = true; + return 0; +#endif + + return libusb_to_orbis_error(libusb_detach_kernel_driver(dev_handle, interface_number)); } -int PS4_SYSV_ABI sceUsbdGetDeviceAddress() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdAttachKernelDriver(SceUsbdDeviceHandle* dev_handle, int interface_number) { + LOG_DEBUG(Lib_Usbd, "called"); + +#if defined(_WIN32) + s_has_removed_driver = false; + return 0; +#endif + + return libusb_to_orbis_error(libusb_attach_kernel_driver(dev_handle, interface_number)); } -int PS4_SYSV_ABI sceUsbdGetDeviceDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +u8* PS4_SYSV_ABI sceUsbdControlTransferGetData(SceUsbdTransfer* transfer) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_control_transfer_get_data(transfer); } -int PS4_SYSV_ABI sceUsbdGetDeviceList() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +SceUsbdControlSetup* PS4_SYSV_ABI sceUsbdControlTransferGetSetup(SceUsbdTransfer* transfer) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_control_transfer_get_setup(transfer); } -int PS4_SYSV_ABI sceUsbdGetDeviceSpeed() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdFillControlSetup(u8* buf, u8 bmRequestType, u8 bRequest, u16 wValue, + u16 wIndex, u16 wLength) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_fill_control_setup(buf, bmRequestType, bRequest, wValue, wIndex, wLength); } -int PS4_SYSV_ABI sceUsbdGetIsoPacketBuffer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +SceUsbdTransfer* PS4_SYSV_ABI sceUsbdAllocTransfer(int iso_packets) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_alloc_transfer(iso_packets); } -int PS4_SYSV_ABI sceUsbdGetMaxIsoPacketSize() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdSubmitTransfer(SceUsbdTransfer* transfer) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_submit_transfer(transfer)); } -int PS4_SYSV_ABI sceUsbdGetMaxPacketSize() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdCancelTransfer(SceUsbdTransfer* transfer) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_cancel_transfer(transfer)); } -int PS4_SYSV_ABI sceUsbdGetStringDescriptor() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdFreeTransfer(SceUsbdTransfer* transfer) { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_free_transfer(transfer); } -int PS4_SYSV_ABI sceUsbdGetStringDescriptorAscii() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdFillControlTransfer(SceUsbdTransfer* transfer, + SceUsbdDeviceHandle* dev_handle, u8* buffer, + SceUsbdTransferCallback callback, void* user_data, + u32 timeout) { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_fill_control_transfer(transfer, dev_handle, buffer, callback, user_data, timeout); } -int PS4_SYSV_ABI sceUsbdHandleEvents() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdFillBulkTransfer(SceUsbdTransfer* transfer, + SceUsbdDeviceHandle* dev_handle, uint8_t endpoint, + u8* buffer, s32 length, SceUsbdTransferCallback callback, + void* user_data, u32 timeout) { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, length, callback, user_data, + timeout); } -int PS4_SYSV_ABI sceUsbdHandleEventsLocked() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdFillInterruptTransfer(SceUsbdTransfer* transfer, + SceUsbdDeviceHandle* dev_handle, uint8_t endpoint, + u8* buffer, s32 length, + SceUsbdTransferCallback callback, void* user_data, + u32 timeout) { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_fill_interrupt_transfer(transfer, dev_handle, endpoint, buffer, length, callback, + user_data, timeout); } -int PS4_SYSV_ABI sceUsbdHandleEventsTimeout() { - LOG_DEBUG(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdFillIsoTransfer(SceUsbdTransfer* transfer, SceUsbdDeviceHandle* dev_handle, + uint8_t endpoint, u8* buffer, s32 length, + s32 num_iso_packets, SceUsbdTransferCallback callback, + void* user_data, u32 timeout) { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_fill_iso_transfer(transfer, dev_handle, endpoint, buffer, length, num_iso_packets, + callback, user_data, timeout); } -int PS4_SYSV_ABI sceUsbdInit() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return 0x80240005; // Skip +void PS4_SYSV_ABI sceUsbdSetIsoPacketLengths(SceUsbdTransfer* transfer, u32 length) { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_set_iso_packet_lengths(transfer, length); } -int PS4_SYSV_ABI sceUsbdInterruptTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +u8* PS4_SYSV_ABI sceUsbdGetIsoPacketBuffer(SceUsbdTransfer* transfer, u32 packet) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_get_iso_packet_buffer(transfer, packet); } -int PS4_SYSV_ABI sceUsbdKernelDriverActive() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdControlTransfer(SceUsbdDeviceHandle* dev_handle, u8 request_type, + u8 bRequest, u16 wValue, u16 wIndex, u8* data, s32 wLength, + u32 timeout) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_control_transfer(dev_handle, request_type, bRequest, wValue, + wIndex, data, wLength, timeout)); } -int PS4_SYSV_ABI sceUsbdLockEvents() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdBulkTransfer(SceUsbdDeviceHandle* dev_handle, u8 endpoint, u8* data, + s32 length, s32* actual_length, u32 timeout) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error( + libusb_bulk_transfer(dev_handle, endpoint, data, length, actual_length, timeout)); } -int PS4_SYSV_ABI sceUsbdLockEventWaiters() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdInterruptTransfer(SceUsbdDeviceHandle* dev_handle, u8 endpoint, u8* data, + s32 length, s32* actual_length, u32 timeout) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error( + libusb_interrupt_transfer(dev_handle, endpoint, data, length, actual_length, timeout)); } -int PS4_SYSV_ABI sceUsbdOpen() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdGetDescriptor(SceUsbdDeviceHandle* dev_handle, u8 descType, u8 descIndex, + u8* data, s32 length) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error( + libusb_get_descriptor(dev_handle, descType, descIndex, data, length)); } -int PS4_SYSV_ABI sceUsbdOpenDeviceWithVidPid() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdGetStringDescriptor(SceUsbdDeviceHandle* dev_handle, u8 desc_index, + u16 langid, u8* data, s32 length) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error( + libusb_get_string_descriptor(dev_handle, desc_index, langid, data, length)); } -int PS4_SYSV_ABI sceUsbdRefDevice() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdGetStringDescriptorAscii(SceUsbdDeviceHandle* dev_handle, u8 desc_index, + u8* data, s32 length) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error( + libusb_get_string_descriptor_ascii(dev_handle, desc_index, data, length)); } -int PS4_SYSV_ABI sceUsbdReleaseInterface() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdTryLockEvents() { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_try_lock_events(g_libusb_context); } -int PS4_SYSV_ABI sceUsbdResetDevice() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdLockEvents() { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_lock_events(g_libusb_context); } -int PS4_SYSV_ABI sceUsbdSetConfiguration() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdUnlockEvents() { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_unlock_events(g_libusb_context); } -int PS4_SYSV_ABI sceUsbdSetInterfaceAltSetting() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdEventHandlingOk() { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_event_handling_ok(g_libusb_context); } -int PS4_SYSV_ABI sceUsbdSetIsoPacketLengths() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdEventHandlerActive() { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_event_handler_active(g_libusb_context); } -int PS4_SYSV_ABI sceUsbdSubmitTransfer() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdLockEventWaiters() { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_lock_event_waiters(g_libusb_context); } -int PS4_SYSV_ABI sceUsbdTryLockEvents() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +void PS4_SYSV_ABI sceUsbdUnlockEventWaiters() { + LOG_DEBUG(Lib_Usbd, "called"); + + libusb_unlock_event_waiters(g_libusb_context); } -int PS4_SYSV_ABI sceUsbdUnlockEvents() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdWaitForEvent(timeval* tv) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_wait_for_event(g_libusb_context, tv)); } -int PS4_SYSV_ABI sceUsbdUnlockEventWaiters() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdHandleEventsTimeout(timeval* tv) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_handle_events_timeout(g_libusb_context, tv)); } -int PS4_SYSV_ABI sceUsbdUnrefDevice() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdHandleEvents() { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_handle_events(g_libusb_context)); } -int PS4_SYSV_ABI sceUsbdWaitForEvent() { - LOG_ERROR(Lib_Usbd, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceUsbdHandleEventsLocked(timeval* tv) { + LOG_DEBUG(Lib_Usbd, "called"); + + return libusb_to_orbis_error(libusb_handle_events_locked(g_libusb_context, tv)); +} + +s32 PS4_SYSV_ABI sceUsbdCheckConnected(SceUsbdDeviceHandle* dev_handle) { + LOG_DEBUG(Lib_Usbd, "called"); + + // There's no libusb version of this function. + // Simulate by querying data. + + int config; + int r = libusb_get_configuration(dev_handle, &config); + + return libusb_to_orbis_error(r); } int PS4_SYSV_ABI Func_65F6EF33E38FFF50() { @@ -406,4 +550,4 @@ void RegisterlibSceUsbd(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("1WtDBgcgseA", "libSceUsbd", 1, "libSceUsbd", 1, 1, Func_D56B43060720B1E0); }; -} // namespace Libraries::Usbd +} // namespace Libraries::Usbd \ No newline at end of file diff --git a/src/core/libraries/usbd/usbd.h b/src/core/libraries/usbd/usbd.h index 4ed153587..3bd8134a7 100644 --- a/src/core/libraries/usbd/usbd.h +++ b/src/core/libraries/usbd/usbd.h @@ -1,76 +1,150 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX - FileCopyrightText : Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "common/types.h" +extern "C" { +struct libusb_device; +struct libusb_device_handle; +struct libusb_device_descriptor; +struct libusb_config_descriptor; +struct libusb_transfer; +struct libusb_control_setup; +struct timeval; +} + namespace Core::Loader { class SymbolsResolver; } + namespace Libraries::Usbd { -int PS4_SYSV_ABI sceUsbdAllocTransfer(); -int PS4_SYSV_ABI sceUsbdAttachKernelDriver(); -int PS4_SYSV_ABI sceUsbdBulkTransfer(); -int PS4_SYSV_ABI sceUsbdCancelTransfer(); -int PS4_SYSV_ABI sceUsbdCheckConnected(); -int PS4_SYSV_ABI sceUsbdClaimInterface(); -int PS4_SYSV_ABI sceUsbdClearHalt(); -int PS4_SYSV_ABI sceUsbdClose(); -int PS4_SYSV_ABI sceUsbdControlTransfer(); -int PS4_SYSV_ABI sceUsbdControlTransferGetData(); -int PS4_SYSV_ABI sceUsbdControlTransferGetSetup(); -int PS4_SYSV_ABI sceUsbdDetachKernelDriver(); -int PS4_SYSV_ABI sceUsbdEventHandlerActive(); -int PS4_SYSV_ABI sceUsbdEventHandlingOk(); -int PS4_SYSV_ABI sceUsbdExit(); -int PS4_SYSV_ABI sceUsbdFillBulkTransfer(); -int PS4_SYSV_ABI sceUsbdFillControlSetup(); -int PS4_SYSV_ABI sceUsbdFillControlTransfer(); -int PS4_SYSV_ABI sceUsbdFillInterruptTransfer(); -int PS4_SYSV_ABI sceUsbdFillIsoTransfer(); -int PS4_SYSV_ABI sceUsbdFreeConfigDescriptor(); -int PS4_SYSV_ABI sceUsbdFreeDeviceList(); -int PS4_SYSV_ABI sceUsbdFreeTransfer(); -int PS4_SYSV_ABI sceUsbdGetActiveConfigDescriptor(); -int PS4_SYSV_ABI sceUsbdGetBusNumber(); -int PS4_SYSV_ABI sceUsbdGetConfigDescriptor(); -int PS4_SYSV_ABI sceUsbdGetConfigDescriptorByValue(); -int PS4_SYSV_ABI sceUsbdGetConfiguration(); -int PS4_SYSV_ABI sceUsbdGetDescriptor(); -int PS4_SYSV_ABI sceUsbdGetDevice(); -int PS4_SYSV_ABI sceUsbdGetDeviceAddress(); -int PS4_SYSV_ABI sceUsbdGetDeviceDescriptor(); -int PS4_SYSV_ABI sceUsbdGetDeviceList(); -int PS4_SYSV_ABI sceUsbdGetDeviceSpeed(); -int PS4_SYSV_ABI sceUsbdGetIsoPacketBuffer(); -int PS4_SYSV_ABI sceUsbdGetMaxIsoPacketSize(); -int PS4_SYSV_ABI sceUsbdGetMaxPacketSize(); -int PS4_SYSV_ABI sceUsbdGetStringDescriptor(); -int PS4_SYSV_ABI sceUsbdGetStringDescriptorAscii(); -int PS4_SYSV_ABI sceUsbdHandleEvents(); -int PS4_SYSV_ABI sceUsbdHandleEventsLocked(); -int PS4_SYSV_ABI sceUsbdHandleEventsTimeout(); -int PS4_SYSV_ABI sceUsbdInit(); -int PS4_SYSV_ABI sceUsbdInterruptTransfer(); -int PS4_SYSV_ABI sceUsbdKernelDriverActive(); -int PS4_SYSV_ABI sceUsbdLockEvents(); -int PS4_SYSV_ABI sceUsbdLockEventWaiters(); -int PS4_SYSV_ABI sceUsbdOpen(); -int PS4_SYSV_ABI sceUsbdOpenDeviceWithVidPid(); -int PS4_SYSV_ABI sceUsbdRefDevice(); -int PS4_SYSV_ABI sceUsbdReleaseInterface(); -int PS4_SYSV_ABI sceUsbdResetDevice(); -int PS4_SYSV_ABI sceUsbdSetConfiguration(); -int PS4_SYSV_ABI sceUsbdSetInterfaceAltSetting(); -int PS4_SYSV_ABI sceUsbdSetIsoPacketLengths(); -int PS4_SYSV_ABI sceUsbdSubmitTransfer(); -int PS4_SYSV_ABI sceUsbdTryLockEvents(); -int PS4_SYSV_ABI sceUsbdUnlockEvents(); -int PS4_SYSV_ABI sceUsbdUnlockEventWaiters(); -int PS4_SYSV_ABI sceUsbdUnrefDevice(); -int PS4_SYSV_ABI sceUsbdWaitForEvent(); +using SceUsbdDevice = libusb_device; +using SceUsbdDeviceHandle = libusb_device_handle; +using SceUsbdDeviceDescriptor = libusb_device_descriptor; +using SceUsbdConfigDescriptor = libusb_config_descriptor; +using SceUsbdTransfer = libusb_transfer; +using SceUsbdControlSetup = libusb_control_setup; +using SceUsbdTransferCallback = void (*)(SceUsbdTransfer* transfer); + +enum class SceUsbdSpeed : u32 { + UNKNOWN = 0, + LOW = 1, + FULL = 2, + HIGH = 3, + SUPER = 4, + SUPER_PLUS = 5 +}; + +s32 PS4_SYSV_ABI sceUsbdInit(); +void PS4_SYSV_ABI sceUsbdExit(); + +s64 PS4_SYSV_ABI sceUsbdGetDeviceList(SceUsbdDevice*** list); +void PS4_SYSV_ABI sceUsbdFreeDeviceList(SceUsbdDevice** list, s32 unref_devices); + +SceUsbdDevice* PS4_SYSV_ABI sceUsbdRefDevice(SceUsbdDevice* device); +void PS4_SYSV_ABI sceUsbdUnrefDevice(SceUsbdDevice* device); + +s32 PS4_SYSV_ABI sceUsbdGetConfiguration(SceUsbdDeviceHandle* dev_handle, s32* config); +s32 PS4_SYSV_ABI sceUsbdGetDeviceDescriptor(SceUsbdDevice* device, SceUsbdDeviceDescriptor* desc); +s32 PS4_SYSV_ABI sceUsbdGetActiveConfigDescriptor(SceUsbdDevice* device, + SceUsbdConfigDescriptor** config); +s32 PS4_SYSV_ABI sceUsbdGetConfigDescriptor(SceUsbdDevice* device, u8 config_index, + SceUsbdConfigDescriptor** config); +s32 PS4_SYSV_ABI sceUsbdGetConfigDescriptorByValue(SceUsbdDevice* device, u8 bConfigurationValue, + SceUsbdConfigDescriptor** config); +void PS4_SYSV_ABI sceUsbdFreeConfigDescriptor(SceUsbdConfigDescriptor* config); + +u8 PS4_SYSV_ABI sceUsbdGetBusNumber(SceUsbdDevice* device); +u8 PS4_SYSV_ABI sceUsbdGetDeviceAddress(SceUsbdDevice* device); + +SceUsbdSpeed PS4_SYSV_ABI sceUsbdGetDeviceSpeed(SceUsbdDevice* device); +s32 PS4_SYSV_ABI sceUsbdGetMaxPacketSize(SceUsbdDevice* device, u8 endpoint); +s32 PS4_SYSV_ABI sceUsbdGetMaxIsoPacketSize(SceUsbdDevice* device, u8 endpoint); + +s32 PS4_SYSV_ABI sceUsbdOpen(SceUsbdDevice* device, SceUsbdDeviceHandle** dev_handle); +void PS4_SYSV_ABI sceUsbdClose(SceUsbdDeviceHandle* dev_handle); +SceUsbdDevice* PS4_SYSV_ABI sceUsbdGetDevice(SceUsbdDeviceHandle* dev_handle); + +s32 PS4_SYSV_ABI sceUsbdSetConfiguration(SceUsbdDeviceHandle* dev_handle, s32 config); +s32 PS4_SYSV_ABI sceUsbdClaimInterface(SceUsbdDeviceHandle* dev_handle, s32 interface_number); +s32 PS4_SYSV_ABI sceUsbdReleaseInterface(SceUsbdDeviceHandle* dev_handle, s32 interface_number); + +SceUsbdDeviceHandle* PS4_SYSV_ABI sceUsbdOpenDeviceWithVidPid(u16 vendor_id, u16 product_id); + +s32 PS4_SYSV_ABI sceUsbdSetInterfaceAltSetting(SceUsbdDeviceHandle* dev_handle, + int interface_number, int alternate_setting); +s32 PS4_SYSV_ABI sceUsbdClearHalt(SceUsbdDeviceHandle* dev_handle, u8 endpoint); +s32 PS4_SYSV_ABI sceUsbdResetDevice(SceUsbdDeviceHandle* dev_handle); + +s32 PS4_SYSV_ABI sceUsbdKernelDriverActive(SceUsbdDeviceHandle* dev_handle, int interface_number); +s32 PS4_SYSV_ABI sceUsbdDetachKernelDriver(SceUsbdDeviceHandle* dev_handle, int interface_number); +s32 PS4_SYSV_ABI sceUsbdAttachKernelDriver(SceUsbdDeviceHandle* dev_handle, int interface_number); + +u8* PS4_SYSV_ABI sceUsbdControlTransferGetData(SceUsbdTransfer* transfer); +SceUsbdControlSetup* PS4_SYSV_ABI sceUsbdControlTransferGetSetup(SceUsbdTransfer* transfer); + +void PS4_SYSV_ABI sceUsbdFillControlSetup(u8* buf, u8 bmRequestType, u8 bRequest, u16 wValue, + u16 wIndex, u16 wLength); + +SceUsbdTransfer* PS4_SYSV_ABI sceUsbdAllocTransfer(int iso_packets); +s32 PS4_SYSV_ABI sceUsbdSubmitTransfer(SceUsbdTransfer* transfer); +s32 PS4_SYSV_ABI sceUsbdCancelTransfer(SceUsbdTransfer* transfer); +void PS4_SYSV_ABI sceUsbdFreeTransfer(SceUsbdTransfer* transfer); + +void PS4_SYSV_ABI sceUsbdFillControlTransfer(SceUsbdTransfer* transfer, + SceUsbdDeviceHandle* dev_handle, u8* buffer, + SceUsbdTransferCallback callback, void* user_data, + u32 timeout); +void PS4_SYSV_ABI sceUsbdFillBulkTransfer(SceUsbdTransfer* transfer, + SceUsbdDeviceHandle* dev_handle, u8 endpoint, u8* buffer, + s32 length, SceUsbdTransferCallback callback, + void* user_data, u32 timeout); +void PS4_SYSV_ABI sceUsbdFillInterruptTransfer(SceUsbdTransfer* transfer, + SceUsbdDeviceHandle* dev_handle, u8 endpoint, + u8* buffer, s32 length, + SceUsbdTransferCallback callback, void* user_data, + u32 timeout); +void PS4_SYSV_ABI sceUsbdFillIsoTransfer(SceUsbdTransfer* transfer, SceUsbdDeviceHandle* dev_handle, + u8 endpoint, u8* buffer, s32 length, s32 num_iso_packets, + SceUsbdTransferCallback callback, void* userData, + u32 timeout); + +void PS4_SYSV_ABI sceUsbdSetIsoPacketLengths(SceUsbdTransfer* transfer, u32 length); +u8* PS4_SYSV_ABI sceUsbdGetIsoPacketBuffer(SceUsbdTransfer* transfer, u32 packet); + +s32 PS4_SYSV_ABI sceUsbdControlTransfer(SceUsbdDeviceHandle* dev_handle, u8 request_type, + u8 bRequest, u16 wValue, u16 wIndex, u8* data, s32 wLength, + u32 timeout); +s32 PS4_SYSV_ABI sceUsbdBulkTransfer(SceUsbdDeviceHandle* dev_handle, u8 endpoint, u8* data, + s32 length, s32* actual_length, u32 timeout); +s32 PS4_SYSV_ABI sceUsbdInterruptTransfer(SceUsbdDeviceHandle* dev_handle, u8 endpoint, u8* data, + s32 length, s32* actual_length, u32 timeout); + +s32 PS4_SYSV_ABI sceUsbdGetDescriptor(SceUsbdDeviceHandle* dev_handle, u8 descType, u8 descIndex, + u8* data, s32 length); +s32 PS4_SYSV_ABI sceUsbdGetStringDescriptor(SceUsbdDeviceHandle* dev_handle, u8 desc_index, + u16 langid, u8* data, s32 length); +s32 PS4_SYSV_ABI sceUsbdGetStringDescriptorAscii(SceUsbdDeviceHandle* dev_handle, u8 desc_index, + u8* data, s32 length); + +s32 PS4_SYSV_ABI sceUsbdTryLockEvents(); +void PS4_SYSV_ABI sceUsbdLockEvents(); +void PS4_SYSV_ABI sceUsbdUnlockEvents(); +s32 PS4_SYSV_ABI sceUsbdEventHandlingOk(); +s32 PS4_SYSV_ABI sceUsbdEventHandlerActive(); +void PS4_SYSV_ABI sceUsbdLockEventWaiters(); +void PS4_SYSV_ABI sceUsbdUnlockEventWaiters(); +s32 PS4_SYSV_ABI sceUsbdWaitForEvent(timeval* tv); + +s32 PS4_SYSV_ABI sceUsbdHandleEventsTimeout(timeval* tv); +s32 PS4_SYSV_ABI sceUsbdHandleEvents(); +s32 PS4_SYSV_ABI sceUsbdHandleEventsLocked(timeval* tv); + +s32 PS4_SYSV_ABI sceUsbdCheckConnected(SceUsbdDeviceHandle* dev_handle); + int PS4_SYSV_ABI Func_65F6EF33E38FFF50(); int PS4_SYSV_ABI Func_97F056BAD90AADE7(); int PS4_SYSV_ABI Func_C55104A33B35B264(); diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 219d0886b..3c839dadd 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -104,6 +104,20 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl return ORBIS_OK; } +s32 PS4_SYSV_ABI sceVideoOutDeleteVblankEvent(Kernel::SceKernelEqueue eq, s32 handle) { + auto* port = driver->GetPort(handle); + if (port == nullptr) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE; + } + + if (eq == nullptr) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE; + } + eq->RemoveEvent(handle, Kernel::SceKernelEvent::Filter::VideoOut); + port->vblank_events.erase(find(port->vblank_events.begin(), port->vblank_events.end(), eq)); + return ORBIS_OK; +} + s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses, s32 bufferNum, const BufferAttribute* attribute) { if (!addresses || !attribute) { @@ -166,7 +180,7 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode return ORBIS_OK; } -int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) { +s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) { if (ev == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } @@ -194,7 +208,7 @@ int PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) { } } -int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64_t* data) { +s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* data) { if (ev == nullptr || data == nullptr) { return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } @@ -211,6 +225,17 @@ int PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, int64 return ORBIS_OK; } +s32 PS4_SYSV_ABI sceVideoOutGetEventCount(const Kernel::SceKernelEvent* ev) { + if (ev == nullptr) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; + } + if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT; + } + + return (ev->data >> 0xc) & 0xf; +} + s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status) { if (!status) { LOG_ERROR(Lib_VideoOut, "Flip status is null"); @@ -447,12 +472,16 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("U2JJtSqNKZI", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetEventId); LIB_FUNCTION("rWUTcKdkUzQ", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetEventData); + LIB_FUNCTION("Mt4QHHkxkOc", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, + sceVideoOutGetEventCount); LIB_FUNCTION("DYhhWbJSeRg", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutColorSettingsSetGamma); LIB_FUNCTION("pv9CI5VC+R0", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutAdjustColor); LIB_FUNCTION("-Ozn0F1AFRg", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutDeleteFlipEvent); + LIB_FUNCTION("oNOQn3knW6s", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, + sceVideoOutDeleteVblankEvent); LIB_FUNCTION("pjkDsgxli6c", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutModeSetAny_); LIB_FUNCTION("N1bEoJ4SRw4", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 4ccb9d943..69deb464f 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -108,10 +108,22 @@ void Linker::Execute(const std::vector args) { static constexpr s64 InternalMemorySize = 0x1000000; void* addr_out{reinterpret_cast(KernelAllocBase)}; - const s32 ret = Libraries::Kernel::sceKernelMapNamedFlexibleMemory( - &addr_out, InternalMemorySize, 3, 0, "SceKernelInternalMemory"); + s32 ret = Libraries::Kernel::sceKernelMapNamedFlexibleMemory(&addr_out, InternalMemorySize, 3, + 0, "SceKernelInternalMemory"); ASSERT_MSG(ret == 0, "Unable to perform sceKernelInternalMemory mapping"); + // Simulate libSceGnmDriver initialization, which maps a chunk of direct memory. + // Some games fail without accurately emulating this behavior. + s64 phys_addr{}; + ret = Libraries::Kernel::sceKernelAllocateDirectMemory( + 0, Libraries::Kernel::sceKernelGetDirectMemorySize(), 0x10000, 0x10000, 3, &phys_addr); + if (ret == 0) { + void* addr{reinterpret_cast(0xfe0000000)}; + ret = Libraries::Kernel::sceKernelMapNamedDirectMemory(&addr, 0x10000, 0x13, 0, phys_addr, + 0x10000, "SceGnmDriver"); + } + ASSERT_MSG(ret == 0, "Unable to emulate libSceGnmDriver initialization"); + main_thread.Run([this, module, args](std::stop_token) { Common::SetCurrentThreadName("GAME_MainThread"); LoadSharedLibraries(); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 8b108a654..cb80d6be4 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -139,35 +139,35 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, alignment = alignment > 0 ? alignment : 16_KB; auto dmem_area = FindDmemArea(search_start); + auto mapping_start = search_start > dmem_area->second.base + ? Common::AlignUp(search_start, alignment) + : Common::AlignUp(dmem_area->second.base, alignment); + auto mapping_end = Common::AlignUp(mapping_start + size, alignment); - const auto is_suitable = [&] { - if (dmem_area == dmem_map.end()) { - return false; - } - const auto aligned_base = Common::AlignUp(dmem_area->second.base, alignment); - const auto alignment_size = aligned_base - dmem_area->second.base; - const auto remaining_size = - dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0; - return dmem_area->second.is_free && remaining_size >= size; - }; - while (dmem_area != dmem_map.end() && !is_suitable() && - dmem_area->second.GetEnd() <= search_end) { - ++dmem_area; + // Find the first free, large enough dmem area in the range. + while ((!dmem_area->second.is_free || dmem_area->second.GetEnd() < mapping_end) && + dmem_area != dmem_map.end()) { + // The current dmem_area isn't suitable, move to the next one. + dmem_area++; + + // Update local variables based on the new dmem_area + mapping_start = search_start > dmem_area->second.base + ? Common::AlignUp(search_start, alignment) + : Common::AlignUp(dmem_area->second.base, alignment); + mapping_end = Common::AlignUp(mapping_start + size, alignment); } - if (!is_suitable()) { + + if (dmem_area == dmem_map.end()) { + // There are no suitable mappings in this range LOG_ERROR(Kernel_Vmm, "Unable to find free direct memory area: size = {:#x}", size); return -1; } - // Align free position - PAddr free_addr = dmem_area->second.base; - free_addr = Common::AlignUp(free_addr, alignment); - // Add the allocated region to the list and commit its pages. - auto& area = CarveDmemArea(free_addr, size)->second; + auto& area = CarveDmemArea(mapping_start, size)->second; area.memory_type = memory_type; area.is_free = false; - return free_addr; + return mapping_start; } void MemoryManager::Free(PAddr phys_addr, size_t size) { @@ -632,17 +632,34 @@ int MemoryManager::DirectQueryAvailable(PAddr search_start, PAddr search_end, si auto dmem_area = FindDmemArea(search_start); PAddr paddr{}; size_t max_size{}; - while (dmem_area != dmem_map.end() && dmem_area->second.GetEnd() <= search_end) { + + while (dmem_area != dmem_map.end()) { if (!dmem_area->second.is_free) { dmem_area++; continue; } - const auto aligned_base = alignment > 0 ? Common::AlignUp(dmem_area->second.base, alignment) - : dmem_area->second.base; + auto aligned_base = alignment > 0 ? Common::AlignUp(dmem_area->second.base, alignment) + : dmem_area->second.base; const auto alignment_size = aligned_base - dmem_area->second.base; - const auto remaining_size = + auto remaining_size = dmem_area->second.size >= alignment_size ? dmem_area->second.size - alignment_size : 0; + + if (dmem_area->second.base < search_start) { + // We need to trim remaining_size to ignore addresses before search_start + remaining_size = remaining_size > (search_start - dmem_area->second.base) + ? remaining_size - (search_start - dmem_area->second.base) + : 0; + aligned_base = alignment > 0 ? Common::AlignUp(search_start, alignment) : search_start; + } + + if (dmem_area->second.GetEnd() > search_end) { + // We need to trim remaining_size to ignore addresses beyond search_end + remaining_size = remaining_size > (dmem_area->second.GetEnd() - search_end) + ? remaining_size - (dmem_area->second.GetEnd() - search_end) + : 0; + } + if (remaining_size > max_size) { paddr = aligned_base; max_size = remaining_size; diff --git a/src/core/tls.cpp b/src/core/tls.cpp index 9b3178171..e13c683e1 100644 --- a/src/core/tls.cpp +++ b/src/core/tls.cpp @@ -5,7 +5,6 @@ #include "common/arch.h" #include "common/assert.h" #include "common/types.h" -#include "core/cpu_patches.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/tls.h" @@ -197,12 +196,7 @@ Tcb* GetTcbBase() { thread_local std::once_flag init_tls_flag; void EnsureThreadInitialized() { - std::call_once(init_tls_flag, [] { -#ifdef ARCH_X86_64 - InitializeThreadPatchStack(); -#endif - SetTcbBase(Libraries::Kernel::g_curthread->tcb); - }); + std::call_once(init_tls_flag, [] { SetTcbBase(Libraries::Kernel::g_curthread->tcb); }); } } // namespace Core diff --git a/src/emulator.cpp b/src/emulator.cpp index 5f94f008a..1a71b99cb 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -22,7 +22,6 @@ #include "common/polyfill_thread.h" #include "common/scm_rev.h" #include "common/singleton.h" -#include "common/version.h" #include "core/file_format/psf.h" #include "core/file_format/trp.h" #include "core/file_sys/fs.h" @@ -123,7 +122,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector", id, title, app_version); std::string window_title = ""; - if (Common::isRelease) { - window_title = fmt::format("shadPS4 v{} | {}", Common::VERSION, game_title); + if (Common::g_is_release) { + window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title); } else { std::string remote_url(Common::g_scm_remote_url); std::string remote_host; @@ -208,10 +207,10 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector + + + diff --git a/src/images/github.png b/src/images/github.png index 22b101798..7b61598cc 100644 Binary files a/src/images/github.png and b/src/images/github.png differ diff --git a/src/images/github.svg b/src/images/github.svg new file mode 100644 index 000000000..1872a00ce --- /dev/null +++ b/src/images/github.svg @@ -0,0 +1,22 @@ + + + + diff --git a/src/images/ko-fi.png b/src/images/ko-fi.png index d19991b5f..ec0c94773 100644 Binary files a/src/images/ko-fi.png and b/src/images/ko-fi.png differ diff --git a/src/images/ko-fi.svg b/src/images/ko-fi.svg new file mode 100644 index 000000000..b8fd694fd --- /dev/null +++ b/src/images/ko-fi.svg @@ -0,0 +1,22 @@ + + + + diff --git a/src/images/shadps4.ico b/src/images/shadps4.ico index bb50f9995..870569def 100644 Binary files a/src/images/shadps4.ico and b/src/images/shadps4.ico differ diff --git a/src/images/shadps4.png b/src/images/shadps4.png new file mode 100644 index 000000000..037732e3b Binary files /dev/null and b/src/images/shadps4.png differ diff --git a/src/images/shadps4.svg b/src/images/shadps4.svg new file mode 100644 index 000000000..0243f550b --- /dev/null +++ b/src/images/shadps4.svg @@ -0,0 +1,105 @@ + + diff --git a/src/images/website.png b/src/images/website.png index 9584f6b82..e872e60a2 100644 Binary files a/src/images/website.png and b/src/images/website.png differ diff --git a/src/images/website.svg b/src/images/website.svg new file mode 100644 index 000000000..eddc0e194 --- /dev/null +++ b/src/images/website.svg @@ -0,0 +1,22 @@ + + + + diff --git a/src/images/youtube.png b/src/images/youtube.png index 362ac5781..2a69760e6 100644 Binary files a/src/images/youtube.png and b/src/images/youtube.png differ diff --git a/src/images/youtube.svg b/src/images/youtube.svg new file mode 100644 index 000000000..977e2b7ff --- /dev/null +++ b/src/images/youtube.svg @@ -0,0 +1,28 @@ + + + + + + diff --git a/src/imgui/imgui_std.h b/src/imgui/imgui_std.h index cd7208064..743702657 100644 --- a/src/imgui/imgui_std.h +++ b/src/imgui/imgui_std.h @@ -50,14 +50,14 @@ inline void KeepWindowInside(ImVec2 display_size = GetIO().DisplaySize) { } inline void KeepNavHighlight() { - GetCurrentContext()->NavDisableHighlight = false; + GetCurrentContext()->NavCursorVisible = true; } inline void SetItemCurrentNavFocus(const ImGuiID id = -1) { const auto ctx = GetCurrentContext(); SetFocusID(id == -1 ? ctx->LastItemData.ID : id, ctx->CurrentWindow); ctx->NavInitResult.Clear(); - ctx->NavDisableHighlight = false; + ctx->NavCursorVisible = true; } inline void DrawPrettyBackground() { diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index 6e961043e..3e2d66a6b 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -22,7 +22,6 @@ #include "common/elf_info.h" #include "common/io_file.h" #include "common/path_util.h" -#include "common/version.h" #include "input/controller.h" #include "input/input_mouse.h" @@ -551,18 +550,18 @@ void ControllerOutput::FinalizeUpdate() { break; case Axis::TriggerLeft: ApplyDeadzone(new_param, lefttrigger_deadzone); - controller->Axis(0, c_axis, GetAxis(0x0, 0x80, *new_param)); + controller->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param)); controller->CheckButton(0, OrbisPadButtonDataOffset::L2, *new_param > 0x20); return; case Axis::TriggerRight: ApplyDeadzone(new_param, righttrigger_deadzone); - controller->Axis(0, c_axis, GetAxis(0x0, 0x80, *new_param)); + controller->Axis(0, c_axis, GetAxis(0x0, 0x7f, *new_param)); controller->CheckButton(0, OrbisPadButtonDataOffset::R2, *new_param > 0x20); return; default: break; } - controller->Axis(0, c_axis, GetAxis(-0x80, 0x80, *new_param * multiplier)); + controller->Axis(0, c_axis, GetAxis(-0x80, 0x7f, *new_param * multiplier)); } } diff --git a/src/input/input_mouse.cpp b/src/input/input_mouse.cpp index 11feaeebb..c84d14b3f 100644 --- a/src/input/input_mouse.cpp +++ b/src/input/input_mouse.cpp @@ -61,11 +61,11 @@ Uint32 MousePolling(void* param, Uint32 id, Uint32 interval) { float a_x = cos(angle) * output_speed, a_y = sin(angle) * output_speed; if (d_x != 0 && d_y != 0) { - controller->Axis(0, axis_x, GetAxis(-0x80, 0x80, a_x)); - controller->Axis(0, axis_y, GetAxis(-0x80, 0x80, a_y)); + controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, a_x)); + controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, a_y)); } else { - controller->Axis(0, axis_x, GetAxis(-0x80, 0x80, 0)); - controller->Axis(0, axis_y, GetAxis(-0x80, 0x80, 0)); + controller->Axis(0, axis_x, GetAxis(-0x80, 0x7f, 0)); + controller->Axis(0, axis_y, GetAxis(-0x80, 0x7f, 0)); } return interval; diff --git a/src/qt_gui/about_dialog.ui b/src/qt_gui/about_dialog.ui index 3842513a5..0e9ef222c 100644 --- a/src/qt_gui/about_dialog.ui +++ b/src/qt_gui/about_dialog.ui @@ -35,7 +35,7 @@ - :/images/shadps4.ico + :/images/shadps4.png true diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index e73a66a71..550fdddb5 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include "check_update.h" using namespace Common::FS; @@ -52,7 +51,7 @@ void CheckUpdate::CheckForUpdates(const bool showMessage) { url = QUrl("https://api.github.com/repos/shadps4-emu/shadPS4/releases/latest"); checkName = false; } else { - if (Common::isRelease) { + if (Common::g_is_release) { Config::setUpdateChannel("Release"); } else { Config::setUpdateChannel("Nightly"); @@ -162,7 +161,7 @@ tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached QString currentRev = (updateChannel == "Nightly") ? QString::fromStdString(Common::g_scm_rev) - : "v." + QString::fromStdString(Common::VERSION); + : "v." + QString::fromStdString(Common::g_version); QString currentDate = Common::g_scm_date; QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate); @@ -189,7 +188,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate, QHBoxLayout* titleLayout = new QHBoxLayout(); QLabel* imageLabel = new QLabel(this); - QPixmap pixmap(":/images/shadps4.ico"); + QPixmap pixmap(":/images/shadps4.png"); imageLabel->setPixmap(pixmap); imageLabel->setScaledContents(true); imageLabel->setFixedSize(50, 50); diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index b5732d0ca..2fd4588d2 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -13,7 +13,7 @@ #include "cheats_patches.h" #include "common/config.h" #include "common/path_util.h" -#include "common/version.h" +#include "common/scm_rev.h" #include "compatibility_info.h" #include "game_info.h" #include "trophy_viewer.h" @@ -115,14 +115,15 @@ public: compatibilityMenu->addAction(updateCompatibility); compatibilityMenu->addAction(viewCompatibilityReport); - if (Common::isRelease) { + if (Common::g_is_release) { compatibilityMenu->addAction(submitCompatibilityReport); } menu.addMenu(compatibilityMenu); compatibilityMenu->setEnabled(Config::getCompatibilityEnabled()); - viewCompatibilityReport->setEnabled(!m_games[itemID].compatibility.url.isEmpty()); + viewCompatibilityReport->setEnabled(m_games[itemID].compatibility.status != + CompatibilityStatus::Unknown); // Show menu. auto selected = menu.exec(global_pos); @@ -557,24 +558,36 @@ public: } if (selected == viewCompatibilityReport) { - if (!m_games[itemID].compatibility.url.isEmpty()) - QDesktopServices::openUrl(QUrl(m_games[itemID].compatibility.url)); + if (m_games[itemID].compatibility.issue_number != "") { + auto url_issues = + "https://github.com/shadps4-emu/shadps4-game-compatibility/issues/"; + QDesktopServices::openUrl( + QUrl(url_issues + m_games[itemID].compatibility.issue_number)); + } } if (selected == submitCompatibilityReport) { - QUrl url = QUrl("https://github.com/shadps4-emu/shadps4-game-compatibility/issues/new"); - QUrlQuery query; - query.addQueryItem("template", QString("game_compatibility.yml")); - query.addQueryItem( - "title", QString("%1 - %2").arg(QString::fromStdString(m_games[itemID].serial), - QString::fromStdString(m_games[itemID].name))); - query.addQueryItem("game-name", QString::fromStdString(m_games[itemID].name)); - query.addQueryItem("game-serial", QString::fromStdString(m_games[itemID].serial)); - query.addQueryItem("game-version", QString::fromStdString(m_games[itemID].version)); - query.addQueryItem("emulator-version", QString(Common::VERSION)); - url.setQuery(query); + if (m_games[itemID].compatibility.issue_number == "") { + QUrl url = + QUrl("https://github.com/shadps4-emu/shadps4-game-compatibility/issues/new"); + QUrlQuery query; + query.addQueryItem("template", QString("game_compatibility.yml")); + query.addQueryItem( + "title", QString("%1 - %2").arg(QString::fromStdString(m_games[itemID].serial), + QString::fromStdString(m_games[itemID].name))); + query.addQueryItem("game-name", QString::fromStdString(m_games[itemID].name)); + query.addQueryItem("game-serial", QString::fromStdString(m_games[itemID].serial)); + query.addQueryItem("game-version", QString::fromStdString(m_games[itemID].version)); + query.addQueryItem("emulator-version", QString(Common::g_version)); + url.setQuery(query); - QDesktopServices::openUrl(url); + QDesktopServices::openUrl(url); + } else { + auto url_issues = + "https://github.com/shadps4-emu/shadps4-game-compatibility/issues/"; + QDesktopServices::openUrl( + QUrl(url_issues + m_games[itemID].compatibility.issue_number)); + } } } diff --git a/src/qt_gui/kbm_gui.cpp b/src/qt_gui/kbm_gui.cpp index 8777dda95..2e1f6ddce 100644 --- a/src/qt_gui/kbm_gui.cpp +++ b/src/qt_gui/kbm_gui.cpp @@ -127,58 +127,9 @@ tr("Do you want to overwrite existing mappings with the mappings from the Common } void KBMSettings::ButtonConnects() { - connect(ui->CrossButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->CrossButton); }); - connect(ui->CircleButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->CircleButton); }); - connect(ui->TriangleButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->TriangleButton); }); - connect(ui->SquareButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->SquareButton); }); - - connect(ui->L1Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->L1Button); }); - connect(ui->L2Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->L2Button); }); - connect(ui->L3Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->L3Button); }); - connect(ui->R1Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->R1Button); }); - connect(ui->R2Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->R2Button); }); - connect(ui->R3Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->R3Button); }); - - connect(ui->TouchpadButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->TouchpadButton); }); - connect(ui->OptionsButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->OptionsButton); }); - - connect(ui->DpadUpButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->DpadUpButton); }); - connect(ui->DpadDownButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->DpadDownButton); }); - connect(ui->DpadLeftButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->DpadLeftButton); }); - connect(ui->DpadRightButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->DpadRightButton); }); - - connect(ui->LStickUpButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->LStickUpButton); }); - connect(ui->LStickDownButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->LStickDownButton); }); - connect(ui->LStickLeftButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->LStickLeftButton); }); - connect(ui->LStickRightButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->LStickRightButton); }); - - connect(ui->RStickUpButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->RStickUpButton); }); - connect(ui->RStickDownButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->RStickDownButton); }); - connect(ui->RStickLeftButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->RStickLeftButton); }); - connect(ui->RStickRightButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->RStickRightButton); }); - - connect(ui->LHalfButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->LHalfButton); }); - connect(ui->RHalfButton, &QPushButton::clicked, this, - [this]() { StartTimer(ui->RHalfButton); }); + for (auto& button : ButtonsList) { + connect(button, &QPushButton::clicked, this, [this, &button]() { StartTimer(button); }); + } } void KBMSettings::DisableMappingButtons() { diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 072ad70e5..36037fd4c 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -18,7 +18,6 @@ #include "common/path_util.h" #include "common/scm_rev.h" #include "common/string_util.h" -#include "common/version.h" #include "control_settings.h" #include "game_install_dialog.h" #include "kbm_gui.h" @@ -58,8 +57,8 @@ bool MainWindow::Init() { // show ui setMinimumSize(720, 405); std::string window_title = ""; - if (Common::isRelease) { - window_title = fmt::format("shadPS4 v{}", Common::VERSION); + if (Common::g_is_release) { + window_title = fmt::format("shadPS4 v{}", Common::g_version); } else { std::string remote_url(Common::g_scm_remote_url); std::string remote_host; @@ -69,10 +68,10 @@ bool MainWindow::Init() { remote_host = "unknown"; } if (remote_host == "shadps4-emu" || remote_url.length() == 0) { - window_title = fmt::format("shadPS4 v{} {} {}", Common::VERSION, Common::g_scm_branch, + window_title = fmt::format("shadPS4 v{} {} {}", Common::g_version, Common::g_scm_branch, Common::g_scm_desc); } else { - window_title = fmt::format("shadPS4 v{} {}/{} {}", Common::VERSION, remote_host, + window_title = fmt::format("shadPS4 v{} {}/{} {}", Common::g_version, remote_host, Common::g_scm_branch, Common::g_scm_desc); } } @@ -517,9 +516,11 @@ void MainWindow::CreateConnects() { Config::setIconSize(36); Config::setSliderPosition(0); } else { + m_game_grid_frame->icon_size = 69; ui->sizeSlider->setValue(0); // icone_size - 36 Config::setIconSizeGrid(69); Config::setSliderPositionGrid(0); + m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); } }); @@ -530,9 +531,11 @@ void MainWindow::CreateConnects() { Config::setIconSize(64); Config::setSliderPosition(28); } else { + m_game_grid_frame->icon_size = 97; ui->sizeSlider->setValue(28); Config::setIconSizeGrid(97); Config::setSliderPositionGrid(28); + m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); } }); @@ -543,9 +546,11 @@ void MainWindow::CreateConnects() { Config::setIconSize(128); Config::setSliderPosition(92); } else { + m_game_grid_frame->icon_size = 161; ui->sizeSlider->setValue(92); - Config::setIconSizeGrid(160); - Config::setSliderPositionGrid(91); + Config::setIconSizeGrid(161); + Config::setSliderPositionGrid(92); + m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); } }); @@ -556,9 +561,11 @@ void MainWindow::CreateConnects() { Config::setIconSize(256); Config::setSliderPosition(220); } else { + m_game_grid_frame->icon_size = 256; ui->sizeSlider->setValue(220); Config::setIconSizeGrid(256); Config::setSliderPositionGrid(220); + m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false); } }); // List @@ -578,6 +585,7 @@ void MainWindow::CreateConnects() { ui->sizeSlider->setEnabled(true); ui->sizeSlider->setSliderPosition(slider_pos); ui->mw_searchbar->setText(""); + SetLastIconSizeBullet(); }); // Grid connect(ui->setlistModeGridAct, &QAction::triggered, m_dock_widget.data(), [this]() { @@ -596,6 +604,7 @@ void MainWindow::CreateConnects() { ui->sizeSlider->setEnabled(true); ui->sizeSlider->setSliderPosition(slider_pos_grid); ui->mw_searchbar->setText(""); + SetLastIconSizeBullet(); }); // Elf Viewer connect(ui->setlistElfAct, &QAction::triggered, m_dock_widget.data(), [this]() { @@ -607,6 +616,7 @@ void MainWindow::CreateConnects() { isTableList = false; ui->sizeSlider->setDisabled(true); Config::setTableMode(2); + SetLastIconSizeBullet(); }); // Cheats/Patches Download. @@ -1032,19 +1042,37 @@ void MainWindow::SetLastUsedTheme() { void MainWindow::SetLastIconSizeBullet() { // set QAction bullet point if applicable int lastSize = Config::getIconSize(); - switch (lastSize) { - case 36: - ui->setIconSizeTinyAct->setChecked(true); - break; - case 64: - ui->setIconSizeSmallAct->setChecked(true); - break; - case 128: - ui->setIconSizeMediumAct->setChecked(true); - break; - case 256: - ui->setIconSizeLargeAct->setChecked(true); - break; + int lastSizeGrid = Config::getIconSizeGrid(); + if (isTableList) { + switch (lastSize) { + case 36: + ui->setIconSizeTinyAct->setChecked(true); + break; + case 64: + ui->setIconSizeSmallAct->setChecked(true); + break; + case 128: + ui->setIconSizeMediumAct->setChecked(true); + break; + case 256: + ui->setIconSizeLargeAct->setChecked(true); + break; + } + } else { + switch (lastSizeGrid) { + case 69: + ui->setIconSizeTinyAct->setChecked(true); + break; + case 97: + ui->setIconSizeSmallAct->setChecked(true); + break; + case 161: + ui->setIconSizeMediumAct->setChecked(true); + break; + case 256: + ui->setIconSizeLargeAct->setChecked(true); + break; + } } } diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 2c4d4480b..4d3481c07 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -106,8 +106,6 @@ public: toggleLabelsAct = new QAction(MainWindow); toggleLabelsAct->setObjectName("toggleLabelsAct"); - toggleLabelsAct->setText( - QCoreApplication::translate("MainWindow", "Show Labels Under Icons")); toggleLabelsAct->setCheckable(true); toggleLabelsAct->setChecked(Config::getShowLabelsUnderIcons()); @@ -413,6 +411,8 @@ public: setThemeTokyoNight->setText("Tokyo Night"); setThemeOled->setText("OLED"); toolBar->setWindowTitle(QCoreApplication::translate("MainWindow", "toolBar", nullptr)); + toggleLabelsAct->setText( + QCoreApplication::translate("MainWindow", "Show Labels Under Icons")); } // retranslateUi }; diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 25c27fef3..5ee802b0c 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -9,7 +9,7 @@ #include #include "common/config.h" -#include "common/version.h" +#include "common/scm_rev.h" #include "qt_gui/compatibility_info.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" @@ -491,7 +491,7 @@ void SettingsDialog::LoadValuesFromConfig() { QString updateChannel = QString::fromStdString(Config::getUpdateChannel()); ui->updateComboBox->setCurrentText( channelMap.key(updateChannel != "Release" && updateChannel != "Nightly" - ? (Common::isRelease ? "Release" : "Nightly") + ? (Common::g_is_release ? "Release" : "Nightly") : updateChannel)); #endif diff --git a/src/qt_gui/translations/ar_SA.ts b/src/qt_gui/translations/ar_SA.ts index c130a374c..e434b3259 100644 --- a/src/qt_gui/translations/ar_SA.ts +++ b/src/qt_gui/translations/ar_SA.ts @@ -7,34 +7,34 @@ AboutDialog About shadPS4 - حول shadPS4 + حول محاكي الظل PS4 shadPS4 is an experimental open-source emulator for the PlayStation 4. - shadPS4 هو محاكي تجريبي مفتوح المصدر لجهاز PlayStation 4. + محاكي الظل هو محاكي تجريبي مفتوح المصدر مخصص لـ PS4 This software should not be used to play games you have not legally obtained. - يجب عدم استخدام هذا البرنامج لتشغيل الألعاب التي لم تحصل عليها بشكل قانوني. + لا تستخدم هذا البرنامج لتشغيل الألعاب بطريقة غير قانونية. CheatsPatches Cheats / Patches for - الغِشّ / التصحيحات + شفرات / إصلاحات لـ Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n - الغش والتصحيحات هي ميزات تجريبية.\nاستخدمها بحذر.\n\nقم بتنزيل الغش بشكل فردي عن طريق اختيار المستودع والنقر على زر التنزيل.\nفي علامة تبويب التصحيحات، يمكنك تنزيل جميع التصحيحات دفعة واحدة، واختيار ما تريد استخدامه، وحفظ اختياراتك.\n\nنظرًا لأننا لا نقوم بتطوير الغش/التصحيحات،\nيرجى الإبلاغ عن أي مشاكل إلى مؤلف الغش.\n\nهل قمت بإنشاء غش جديد؟ قم بزيارة:\n + الشفرات والإصلاحات هي ميزات تجريبية.\nاستخدمها بحذر.\n\nقم بتنزيل الشفرات بشكل فردي عن طريق اختيار المستودع والضغط على تنزيل.\nفي علامة تبويب الإصلاحات، يمكنك تنزيل جميع الإصلاحات دفعة واحدة، واختيار ما تريد استخدامه، وحفظ اختياراتك.\n\nنظرًا لأننا لا نقوم بتطوير الشفرات والإصلاحات ،\nيرجى الإبلاغ عن أي مشاكل إلى مؤلف الشفرات.\n\nهل قمت بإنشاء شفرة جديد؟ قم بزيارة:\n No Image Available - لا تتوفر صورة + الصورة غير موجودة Serial: - الرقم التسلسلي: + الرَّقَم التسلسلي: Version: @@ -46,7 +46,7 @@ Select Cheat File: - اختر ملف الغش: + حدد مِلَفّ الشفرات: Repository: @@ -54,7 +54,7 @@ Download Cheats - تنزيل الغش + تحميل الشفرات Delete File @@ -66,19 +66,19 @@ You can delete the cheats you don't want after downloading them. - يمكنك حذف الغش الذي لا تريده بعد تنزيله. + يمكنك حذف الشفرات التي لا 'تريدها بعد تنزيلها. Do you want to delete the selected file?\n%1 - هل تريد حذف الملف المحدد؟\n%1 + هل ترغب في حذف الملف المحدد؟\n%1 Select Patch File: - اختر ملف التصحيح: + اختر مِلَف التصحيح: Download Patches - تنزيل التصحيحات + تحميل ملفات التصحيح Save @@ -86,7 +86,7 @@ Cheats - الغش + الشفرات Patches @@ -98,15 +98,15 @@ No patch selected. - لم يتم اختيار أي تصحيح. + لم يتم تحديد أي مِلَف تصحيح. Unable to open files.json for reading. - تعذر فتح files.json للقراءة. + تعذّر فتح مِلَف files.json للقراءة. No patch file found for the current serial. - لم يتم العثور على ملف تصحيح للرقم التسلسلي الحالي. + لم يتم العثور على مِلَف تصحيح للسيريال الحالي. Unable to open the file for reading. @@ -126,11 +126,11 @@ Options saved successfully. - تم حفظ الخيارات بنجاح. + تم حفظ الإعدادات. Invalid Source - مصدر غير صالح + المصدر غير صالح The selected source is invalid. @@ -138,11 +138,11 @@ File Exists - الملف موجود + المِلَف موجود مسبقًا File already exists. Do you want to replace it? - الملف موجود بالفعل. هل تريد استبداله؟ + المِلَف موجود مسبقًا. هل ترغب في استبداله؟ Failed to save file: @@ -154,19 +154,19 @@ Cheats Not Found - لم يتم العثور على الغش + لم يتم العثور على أي شفرات No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. - لم يتم العثور على غش لهذه اللعبة في هذا الإصدار من المستودع المحدد. حاول استخدام مستودع آخر أو إصدار آخر من اللعبة. + لم يتم العثور على شفرات لهذه اللعبة في هذا الإصدار من المستودع المحدد. جرّب مستودعًا آخر أو إصدارًا مختلفًا من اللعبة. Cheats Downloaded Successfully - تم تنزيل الغش بنجاح + تم تحميل الشفرات بنجاح You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. - لقد نجحت في تنزيل الغش لهذا الإصدار من اللعبة من المستودع المحدد. يمكنك محاولة التنزيل من مستودع آخر. إذا كان متاحًا، يمكنك اختياره عن طريق تحديد الملف من القائمة. + تم تحميل الشفرات بنجاح لهذه النسخة من اللعبة من المستودع الذي اخترته. إذا أردت، يمكنك تجربة مستودع آخر، وإذا كان متاحًا، يمكنك استخدام الشفرات عن طريق اختيار الملف من القائمة. Failed to save: @@ -182,7 +182,7 @@ 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. - تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات لجميع الألعاب، ولا داعي لتنزيلها بشكل فردي لكل لعبة كما هو الحال مع الغش. إذا لم يظهر التحديث، قد يكون السبب أنه غير متوفر للإصدار وسيريال اللعبة المحدد. + تم تنزيل التصحيحات بنجاح! تم تنزيل جميع التصحيحات المتوفرة لجميع الألعاب، ولا حاجة إلى تنزيلها بشكل فردي لكل لعبة كما هو الحال مع الشفرات. إذا لم يظهر التصحيح، فقد لا يكون متوفرًا للسيريال أو الإصدار المحدد من اللعبة. Failed to parse JSON data from HTML. @@ -190,15 +190,15 @@ Failed to retrieve HTML page. - .HTML فشل في استرجاع صفحة + فشل في جلب صفحة HTML. The game is in version: %1 - اللعبة في الإصدار: %1 + إصدار اللعبة الحالي: %1 The downloaded patch only works on version: %1 - الباتش الذي تم تنزيله يعمل فقط على الإصدار: %1 + التصحيح الذي تم تنزيله يعمل فقط مع الإصدار:%1 You may need to update your game. @@ -206,7 +206,7 @@ Incompatibility Notice - إشعار عدم التوافق + إشعار بعدم التوافق Failed to open file: @@ -238,7 +238,7 @@ Can't apply cheats before the game is started - لا يمكن تطبيق الغش قبل بدء اللعبة. + لا 'يمكن تطبيق الشفرات قبل بَدْء اللعبة Close @@ -249,7 +249,7 @@ CheckUpdate Auto Updater - محدث تلقائي + التحديثات التلقائية Error @@ -261,7 +261,7 @@ The Auto Updater allows up to 60 update checks per hour.\nYou have reached this limit. Please try again later. - يتيح التحديث التلقائي ما يصل إلى 60 عملية تحقق من التحديث في الساعة.\nلقد وصلت إلى هذا الحد. الرجاء المحاولة مرة أخرى لاحقًا. + تسمح التحديثات التلقائية بـ 60 عملية تحقق من التحديث في الساعة.\nلقد وصلت إلى الحد المسموح به. الرجاء المحاولة لاحقًا. Failed to parse update information. @@ -329,7 +329,7 @@ Network error occurred while trying to access the URL - حدث خطأ في الشبكة أثناء محاولة الوصول إلى عنوان URL + حدث خطأ في الشبكة عند محاولة الوصول إلى الموقع Download Complete @@ -388,11 +388,11 @@ Boots - أحذية + إقلاع Menus - قوائم + القائمة Ingame @@ -407,7 +407,7 @@ ControlSettings Configure Controls - تعديل عناصر التحكم + إعدادات التحكم D-Pad @@ -431,11 +431,11 @@ Left Stick Deadzone (def:2 max:127) - مدى تسجيل الإدخال للعصا اليسرى (التلقائي:2 حد أقصى:127) + النقطة العمياء للعصا اليسرى (الافتراضي: 2، الحد الأقصى: 127) Left Deadzone - إعدادات مدى تسجيل الإدخال لعصا التحكم اليسرى + النقطة العمياء لليسار Left Stick @@ -507,11 +507,11 @@ Right Stick Deadzone (def:2, max:127) - مدى تسجيل الإدخال للعصا اليمنى (التلقائي:2 حد أقصى:127) + النقطة العمياء للعصا اليمنى (الافتراضي: 2، الحد الأقصى: 127) Right Deadzone - إعدادات مدى تسجيل الإدخال لعصا التحكم اليمنى + النقطة العمياء اليمنى Right Stick @@ -617,7 +617,7 @@ ElfViewer Open Folder - فتح المجلد + افتح مجلد @@ -647,7 +647,7 @@ Browse - تصفح + استعراض Error @@ -718,15 +718,15 @@ Compatibility is untested - التوافق غير مختبر + لم يتم اختبار التوافق Game does not initialize properly / crashes the emulator - اللعبة لا تهيئ بشكل صحيح / تعطل المحاكي + اللعبة لا تبدأ بشكل صحيح / تتسبب في انهيار المحاكي Game boots, but only displays a blank screen - اللعبة تبدأ بالعمل، ولكن فقط تعرض شاشة فارغة + اللعبة تبدأ بالإقلاع، لكنها تعرض شاشة فارغة فقط Game displays an image but does not go past the menu @@ -734,15 +734,15 @@ Game has game-breaking glitches or unplayable performance - اللعبة بها قلتشات أو أداء غير قابل للتشغيل + اللعبة بها أخطاء جسيمة أو أداء يجعلها غير قابلة للعب Game can be completed with playable performance and no major glitches - يمكن الانتهاء من اللعبة مع الأداء القابل للتشغيل و لا توجد قلتشات كبيرة + يمكن إنهاء اللعبة بأداء جيد وبدون أعطال رئيسية Click to see details on github - انقر لرؤية التفاصيل على GitHub + اضغط لعرض التفاصيل على GitHub Last updated @@ -780,15 +780,15 @@ Cheats / Patches - الغش / التصحيحات + الشفرات / التصحيحات SFO Viewer - عارض SFO + عارض معلومات اللعبة (SFO) Trophy Viewer - عارض الجوائز + عارض الكؤوس Open Folder... @@ -820,11 +820,11 @@ Copy Version - إصدار النسخة + نسخ الإصدار Copy Size - حجم النسخة + نسخ حجم Copy All @@ -864,7 +864,7 @@ Submit a report - إرسال بلاغ + إرسال تقرير Shortcut creation @@ -888,7 +888,7 @@ This game has no update to delete! - لا تحتوي اللعبة على تحديث لحذفه! + لا يوجد تحديث لهذه اللعبة لحذفه! Update @@ -896,7 +896,7 @@ This game has no DLC to delete! - لا تحتوي اللعبة على DLC لحذفه! + لا توجد محتويات إضافية (DLC) لهذه اللعبة لحذفها! DLC @@ -916,11 +916,11 @@ Delete Save Data - حذف التخزينه + حذف بيانات الحفظ This game has no update folder to open! - لا تحتوي اللعبة على تحديث لفتحه! + لا يوجد مجلد تحديث لهذه اللعبة لفتحه! No log file found for this game! @@ -932,15 +932,15 @@ This game has no save data to delete! - هذه اللعبة لا تحتوي على أي تخزينات لحذفها! + لا توجد بيانات حفظ لهذه اللعبة لحذفها! This game has no saved trophies to delete! - هذه اللعبة ليس لديها كؤوس محفوظة للحذف! + لا توجد كؤوس محفوظة لهذه اللعبة لحذفها! Save Data - حفظ البيانات + بيانات الحفظ Trophy @@ -955,7 +955,7 @@ HelpDialog Quickstart - التشغيل السريع + البدء السريع FAQ @@ -963,22 +963,22 @@ Syntax - الصّيغة + البنية Special Bindings - إدخالات خاصة + الارتباطات الخاصة Keybindings - أزرار التحكم + تعيين الأزرار KBMSettings Configure Controls - تعديل عناصر التحكم + إعدادات جهاز التحكم D-Pad @@ -1026,7 +1026,7 @@ Use per-game configs - استخدام إعدادات كل لُعْبَة + استخدام إعدادات خاصة لكل لعبه L1 @@ -1038,7 +1038,7 @@ Text Editor - محرر النص + محرّر النصوص Help @@ -1058,15 +1058,15 @@ Touchpad Click - النقر على لوحة اللمس + زر لوحة اللمس Mouse to Joystick - الفأرة إلى عصا التحكم + تحويل الماوس إلى عصا التحكم *press F7 ingame to activate - * اضغط على F7 للتفعيل + * اضغط F7 داخل اللعبة للتفعيل R3 @@ -1078,15 +1078,15 @@ Mouse Movement Parameters - معطيات حركة الفأرة + إعدادات حركة الماوس note: click Help Button/Special Keybindings for more information - ملاحظة: انقر فوق زر المساعدة/روابط المفاتيح الخاصة للحصول على مزيد من المعلومات + ملاحظة: لمزيد من المعلومات عن التعيينات الخاصة، اضغط على زر المساعدة Face Buttons - أزرار الوجه + الأزرار الأمامية Triangle @@ -1106,11 +1106,11 @@ Right Analog Halfmode - تقليل سرعة عصا التحكم اليمنى للنصف + وضع النصف للعصا اليمنى (نصف الحساسية أو نصف الحركة) hold to move right stick at half-speed - الضغط باستمرار لتحريك العصا اليمنى بنصف السرعة + اضغط مع الاستمرار لتحريك العصا اليمنى بسرعة منخفضة Right Stick @@ -1118,15 +1118,15 @@ Speed Offset (def 0.125): - إزاحة السرعة (تلقائي 0.125): + تعويض السرعة (الافتراضي 0.125): Copy from Common Config - نسخ من الإعدادات الشائعة + نسخ من الإعدادات العامة Deadzone Offset (def 0.50): - : + تعويض منطقة الخمول (الافتراضي 0.50): Speed Multiplier (def 1.0): @@ -1134,39 +1134,39 @@ Common Config Selected - الإعدادات الشائعة محدده + تم اختيار الإعدادات العامة This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. - هذا الزر يقوم بنسخ تعيينات الأزرار من إعدادات المستخدم العامة لإعدادات المستخدم المحددة حالياً، ولا يمكن استعماله عندما تكون الإعدادات المستخدمة هي الإعدادات العامة. + يقوم هذا الزر بنسخ التعيينات من الإعدادات العامة إلى الملف الشخصي المحدد حاليًا، ولا يمكن استخدامه إذا كانت الإعدادات العامة هي الملف المحدد. Copy values from Common Config - نسخ من الإعدادات الشائعة + نسخ القيم من الإعدادات العامة Do you want to overwrite existing mappings with the mappings from the Common Config? - هل تريد استبدال التعيينات الحالية بالتعيينات العامة؟ + هل تريد استبدال التعيينات الحالية بالتعيينات من الإعدادات العامة؟ Unable to Save - غير قادر على الحفظ + تعذّر الحفظ Cannot bind any unique input more than once - لا يمكن ربط أي إدخال فريد أكثر من مرة + لا يمكن تعيين نفس الزر لأكثر من وظيفة Press a key - اضغط على مفتاح + اضغط زرًا Cannot set mapping - لا يمكن تعيين الأزرار + تعذّر تعيين الزر Mousewheel cannot be mapped to stick outputs - عجلة الفأرة لا يمكن تعيينها لعصا التحكم + لا يمكن تعيين عجلة الفأرة لمخرجات العصا Save @@ -1189,7 +1189,7 @@ MainWindow Open/Add Elf Folder - Elf فتح/إضافة مجلد + فتح / إضافة مجلد Elf Boot Game @@ -1225,11 +1225,11 @@ Exit the application. - الخروج من التطبيق. + إغلاق التطبيق Show Game List - إظهار قائمة الألعاب + عرض قائمة الألعاب Game List Refresh @@ -1281,7 +1281,7 @@ No games found. Please add your games to your library first. - لم يتم العثور على ألعاب. الرجاء إضافة ألعابك إلى مكتبتك أولاً. + لم يتم العثور على ألعاب. يرجى إضافة ألعابك إلى المكتبة أولاً. Search... @@ -1345,7 +1345,7 @@ Game List - ققائمة الألعاب + قائمة الألعاب * Unsupported Vulkan Version @@ -1353,7 +1353,7 @@ Download Cheats For All Installed Games - تنزيل الغش لجميع الألعاب المثبتة + تحميل الشفرات لجميع الألعاب المثبتة Download Patches For All Games @@ -1365,7 +1365,7 @@ You have downloaded cheats for all the games you have installed. - لقد قمت بتنزيل الغش لجميع الألعاب التي قمت بتثبيتها. + تم تحميل الشفرات لجميع الألعاب المثبتة. Patches Downloaded Successfully! @@ -1409,11 +1409,11 @@ Play - أبدأ اللعب + تشغيل Pause - توقف مؤقت + إيقاف مؤقت Stop @@ -1425,11 +1425,11 @@ Full Screen - وضع ملء الشاشة + الشاشة الكاملة Controllers - أذرعة التحكم + وحدات التحكم Keyboard @@ -1445,7 +1445,7 @@ Show Labels Under Icons - إظهار العلامات أسفل الأيقونات + إظهار التسميات تحت الأيقونات @@ -1464,7 +1464,7 @@ Console Language - لغة وحدة التحكم + لغة النظام Emulator Language @@ -1476,11 +1476,11 @@ Default tab when opening settings - علامة التبويب الافتراضية عند فتح الإعدادات + التبويب الافتراضي عند فتح الإعدادات Show Game Size In List - عرض حجم اللعبة في القائمة + إظهار حجم اللعبة في القائمة Show Splash @@ -1496,7 +1496,7 @@ Trophy Key - زر الميداليات + مفتاح الكأس Trophy @@ -1504,11 +1504,11 @@ Open the custom trophy images/sounds folder - افتح مجلد تخصيص اصوات/صور الميداليات + فتح مجلد الصور/الأصوات المخصصة للكؤوس Logger - المسجل + سجل الأحداث Log Type @@ -1520,23 +1520,23 @@ Open Log Location - افتح موقع السجل + فتح موقع السجل Input - إدخال + إعدادات المدخلات Cursor - مؤشر + مؤشر الماوس Hide Cursor - إخفاء المؤشر + إخفاء مؤشر الماوس Hide Cursor Idle Timeout - مهلة إخفاء المؤشر عند الخمول + إخفاء المؤشر بعد فترة الخمول s @@ -1552,7 +1552,7 @@ Graphics - الرسومات + الرسوميات GUI @@ -1564,7 +1564,7 @@ Graphics Device - جهاز الرسومات + جهاز الرسوميات Vblank Divider @@ -1572,15 +1572,15 @@ Advanced - متقدم + الإعدادات المتقدمة Enable Shaders Dumping - تمكين تفريغ الشيدرات + تمكين تصدير الشيدرات Enable NULL GPU - تمكين وحدة معالجة الرسومات الفارغة + تمكين GPU فارغ Enable HDR @@ -1604,23 +1604,23 @@ Debug - تصحيح الأخطاء + التصحيح Enable Debug Dumping - تمكين تفريغ التصحيح + تمكين تصدير بيانات التصحيح Enable Vulkan Validation Layers - Vulkan تمكين طبقات التحقق من + تمكين طبقات التحقق لـ Vulkan Enable Vulkan Synchronization Validation - Vulkan تمكين التحقق من تزامن + تمكين التحقق من تزامن Vulkan Enable RenderDoc Debugging - RenderDoc تمكين تصحيح أخطاء + تمكين التصحيح باستخدام RenderDoc Enable Crash Diagnostics @@ -1628,19 +1628,19 @@ Collect Shaders - اجمع برامج التظليل + جمع الشيدرات Copy GPU Buffers - انسخ التخزين المؤقت لوحدة معالجة الرُسوم + نسخ مخازن الرسوميات Host Debug Markers - استضافة علامات التصحيح + علامات التصحيح على المضيف Guest Debug Markers - ضيف علامات التصحيح + علامات التصحيح على الضيف Update @@ -1648,11 +1648,11 @@ Check for Updates at Startup - تحقق من التحديثات عند بدء التشغيل + التحقق من التحديثات عند بدء التشغيل Always Show Changelog - اظهر سجل التغيرات دائماً + عرض سجل التغييرات دائمًا Update Channel @@ -1672,7 +1672,7 @@ Disable Trophy Notification - إغلاق إشعارات الميداليات + تعطيل إشعار التروفي Background Image @@ -1684,7 +1684,7 @@ Opacity - درجة السواد + الشفافية Play title music @@ -1692,7 +1692,7 @@ Update Compatibility Database On Startup - تحديث قاعدة بيانات التوافق عند التشغيل + تحديث بيانات التوافق تلقائيًا عند التشغيل Game Compatibility @@ -1700,11 +1700,11 @@ Display Compatibility Data - إظهار معلومات التوافق + عرض بيانات التوافق Update Compatibility Database - Update Compatibility Database + تحديث قاعدة بيانات التوافق Volume @@ -1728,7 +1728,7 @@ Point your mouse at an option to display its description. - وجّه الماوس نحو خيار لعرض وصفه. + ✅ مرّر مؤشر الماوس على أي خِيار لعرض وصفه. Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. @@ -1736,11 +1736,11 @@ Emulator Language:\nSets the language of the emulator's user interface. - لغة المحاكي:\nتحدد لغة واجهة المستخدم الخاصة بالمحاكي. + لغة المحاكي:\nتحدد اللغة المستخدمة في واجهة المحاكي. Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. - إظهار شاشة البداية:\nيعرض شاشة البداية الخاصة باللعبة (صورة خاصة) أثناء بدء التشغيل. + عرض شاشة البداية:\nيعرض شاشة البداية الخاصة باللعبة (صورة مميزة) أثناء بدء التشغيل. Enable Discord Rich Presence:\nDisplays the emulator icon and relevant information on your Discord profile. @@ -1748,59 +1748,60 @@ Username:\nSets the PS4's account username, which may be displayed by some games. - اسم المستخدم:\nيضبط اسم حساب PS4، الذي قد يتم عرضه في بعض الألعاب. + اسم المستخدم:\nيحدد اسم حساب'PS4، وقد يظهر في بعض الألعاب. Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - مفتاح الميداليات:\nمفتاح يستخدم لفتح تشفير الميداليات. يجب أن يكون من جهاز مكسور الحماية.\nيجي أن يحتوي على أحرف نظام العد السداسي. + مفتاح الكؤوس:\nيُستخدم لفك تشفير الجوائز داخل الألعاب. يُستخرج من جهاز معدل.\nيجب أن يُكتب باستخدام رموز ست عشرية فقط. Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. - نوع السجل:\nيضبط ما إذا كان سيتم مزامنة مخرجات نافذة السجل للأداء. قد يؤثر سلبًا على المحاكاة. + نوع السجل:\nييحدد ما إذا كان سيتم مزامنة مخرجات نافذة السجل لتحسين الأداء. قد يؤدي ذلك إلى تأثيرات سلبية على المحاكاة. Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. - فلتر السجل:\nيقوم بتصفية السجل لطباعة معلومات محددة فقط.\nأمثلة: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" المستويات: Trace, Debug, Info, Warning, Error, Critical - بالترتيب، مستوى محدد يخفي جميع المستويات التي تسبقه ويعرض جميع المستويات بعده. + تصفية السجل:\nيمكنك تحديد نوع المعلومات التي سيتم عرضها في السجل فقط.\nأمثلة: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" االمستويات حسب الترتيب:: Trace, Debug, Info, Warning, Error, Critical - عند اختيار مستوى معين، يتم تجاهل كل ما قبله، وتسجيل كل ما بعده. Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. - تحديث: Release: إصدارات رسمية تصدر شهريًا، قد تكون قديمة بعض الشيء، لكنها أكثر استقرارًا واختبارًا. Nightly: إصدارات تطوير تحتوي على أحدث الميزات والإصلاحات، لكنها قد تحتوي على أخطاء وأقل استقرارًا. + تحديث: Release: نُسخ شهرية مستقرة وخضعت للاختبار، لكنها قد تكون قديمة. +Nightly: نُسخ تحتوي على أحدث الميزات، لكنها أقل استقرارًا وقد تحتوي على أخطاء. Background Image:\nControl the opacity of the game background image. - صورة الخلفية:\nيتحكم في درجة سواد صورة خلفية اللعبة. + صورة الخلفية:\nتحكم في شفافية صورة خلفية اللعبة. Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. - تشغيل موسيقى العنوان:\nإذا كانت اللعبة تدعم ذلك، قم بتمكين تشغيل موسيقى خاصة عند اختيار اللعبة في واجهة المستخدم. + تشغيل موسيقى العنوان:\nفي حال كانت اللعبة تدعم ذلك، يتم تشغيل موسيقى خاصة عند تحديدها في الواجهة. Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - إغلاق نوافذ الميداليات المنبثقة:\n إغلاق إشعارات الميداليات داخل اللعبة. تقدم الميداليات يمكن تتبعه باستخدام عارض الميداليات (قم بالضغط على زر الفأرة الأيمن داخل النافذة الرئيسية). + تعطيل إشعارات التروفي:\nيمنع ظهور إشعارات الجوائز أثناء اللعب، بينما يظل بإمكانك متابعة التقدم من خلال "عارض التروفي" (انقر بزر الفأرة الأيمن على اللعبة في النافذة الرئيسية). Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. - إخفاء المؤشر:\nاختر متى سيختفي المؤشر:\nأبداً: سترى الفأرة دائماً.\nعاطل: حدد وقتاً لاختفائه بعد أن يكون غير مستخدم.\nدائماً: لن ترى الفأرة أبداً. + إخفاء المؤشر:\nاختر متى يتم إخفاء مؤشر الماوس:\nأبدًا: يظهر دائمًا.\nعند الخمول: يختفي بعد فترة من عدم الحركة.\nدائمًا: لا يظهر إطلاقًا. Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. - حدد وقتاً لاختفاء الفأرة بعد أن تكون غير مستخدم. + مدة إخفاء المؤشر عند الخمول:\nالوقت (بالثواني) الذي ينتظره المؤشر قبل أن يختفي تلقائيًا عند عدم استخدامه. Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. - سلوك زر العودة:\nيضبط زر العودة في وحدة التحكم ليحاكي الضغط على الموضع المحدد على لوحة اللمس في PS4. + سلوك زر الرجوع:\nيحدد وظيفة زر' الرجوع في وحدة التحكم لمحاكاة اللمس في موقع معيّن على لوحة اللمس الخاصة بـ PS4. Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + عرض بيانات التوافق:\nيعرض معلومات توافق الألعاب في عرض جدولي. فعّل ""تحديث التوافق عند بدء التشغيل"" للحصول على أحدث المعلومات. Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + تحديث التوافق عند بدء التشغيل:\nيتم تحديث قاعدة بيانات التوافق تلقائيًا عند بدء تشغيل shadPS4. Update Compatibility Database:\nImmediately update the compatibility database. - Update Compatibility Database:\nImmediately update the compatibility database. + تحديث قاعدة بيانات التوافق:\nتحديث فوري لقاعدة بيانات التوافق. Never @@ -1816,15 +1817,15 @@ Touchpad Left - لوحة اللمس اليسرى + الجانب الأيسر من لوحة اللمس Touchpad Right - لوحة اللمس اليمنى + الجانب الأيمن من لوحة اللمس Touchpad Center - وسط لوحة اللمس + مركز لوحة اللمس None @@ -1832,11 +1833,11 @@ Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it. - جهاز الرسومات:\nعلى الأنظمة متعددة وحدات معالجة الرسومات، اختر وحدة معالجة الرسومات التي سيستخدمها المحاكي من قائمة منسدلة،\nأو اختر "Auto Select" لتحديدها تلقائيًا. + جهاز الرسوميات:\nفي الأنظمة التي تحتوي على أكثر من معالج رسومي، اختر وحدة المعالجة الرسومية GPU التي سيستخدمها المحاكي من القائمة المنسدلة،\nأو اختر ""تحديد تلقائي"" ليتم اختيارها تلقائيًا. Width/Height:\nSets the size of the emulator window at launch, which can be resized during gameplay.\nThis is different from the in-game resolution. - العرض / الارتفاع:\nيضبط حجم نافذة المحاكي عند التشغيل، والذي يمكن تغيير حجمه أثناء اللعب.\nهذا يختلف عن دقة اللعبة نفسها. + العرض/الارتفاع:\nتحدد حجم نافذة المحاكي عند التشغيل، ويمكن تغيير حجمها أثناء اللعب.\nيختلف هذا عن دقة اللعبة داخل اللعبة. Vblank Divider:\nThe frame rate at which the emulator refreshes at is multiplied by this number. Changing this may have adverse effects, such as increasing the game speed, or breaking critical game functionality that does not expect this to change! @@ -1844,19 +1845,19 @@ Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. - تمكين تفريغ الـ Shaders:\nلأغراض تصحيح الأخطاء التقنية، يحفظ الـ Shaders الخاصة باللعبة في مجلد أثناء التشغيل. + تمكين تفريغ الشيدرات:\nلأغراض تصحيح الأخطاء التقنية، يقوم بحفظ شيدرات اللعبة في مجلد أثناء عرضها. Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. - تمكين GPU الافتراضية:\nلأغراض تصحيح الأخطاء التقنية، يقوم بتعطيل عرض اللعبة كما لو لم يكن هناك بطاقة رسومات. + تمكين GPU فارغ:\nلأغراض تصحيح الأخطاء التقنية، يتم تعطيل عرض اللعبة كما لو لم يكن هناك كرت شاشة. Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + تشغيل HDR:\n يقوم بتشغيل HDR في الألعاب المدعومة.\nيجب أن تدعم شاشتك أطياف ألوان BT2020 PQ و صيغة تنسيق المبادلة RGB10A2. Game Folders:\nThe list of folders to check for installed games. - مجلدات اللعبة:\nقائمة بالمجلدات للتحقق من الألعاب المثبتة. + مجلدات اللعبة:\nقائمة المجلدات التي يتم فحصها للبحث عن الألعاب المثبتة. Add:\nAdd a folder to the list. @@ -1868,7 +1869,7 @@ Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. - تمكين تفريغ التصحيح:\nيحفظ رموز الاستيراد والتصدير ومعلومات رأس الملف للبرنامج الحالي لجهاز PS4 إلى دليل. + تمكين تفريغ التصحيح:\nيقوم بحفظ الرموز المستوردة والمصدرة، بالإضافة إلى معلومات رأس الملف للبرنامج الجاري تشغيله على PS4 في مجلد. Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation. @@ -1884,51 +1885,51 @@ Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). - Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + تجميع برامج التظليل:\n يجب أن تقوم بتشغيل هذا لتعديل برامج التظليل باستخدام قائمة تصحيح الأخطاء (Ctrl + F10). Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + تشخيص الأعطال:\nيقوم بإنشاء ملف بصيغة .yaml يحتوي على معلومات عن حالة Vulkan في وقت حدوث العطل.\nمفيد لتصحيح أخطاء 'فصل الجهاز'. إذا قمت بتشغيل هذا من الأفضل أن تقوم بتشغيل "استضافة علامات تصحيح الأخطاء" و "ضيف علامات تصحيح الأخطاء".\nلا يعمل على وحدة معالجة رسوم إنتل.\nتحتاج لتشغيل التحقق من طبقات Vulkan و مجموعة تطوير البرامج الخاصة بـVulkan من أجل أن يعمل هذا. Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. - Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + نسخ مخازن الذاكرة الخاصة بالـ GPU:\nيتجاوز مشكلة التزامن المتعلقة بتقديمات GPU.\n قد يساعد أو لا يساعد في حل أعطال PM4 من النوع 0. Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + علامات تصحيح الأخطاء للمضيف:\nقوم بإدراج معلومات في المحاكي مثل علامات للأوامر AMDGPU المرتبطة بأوامر فولكن، إضافةً إلى تخصيص أسماء لتصحيح الأخطاء للموارد.\nمن الأفضل تفعيل تشخيص الأعطال عند تفعيل هذه الخاصية.\nمفيد لبرامج مثل أداة تصحيح الأخطاء الرسومية. Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + علامات تصحيح الضيف:\nيُدخل أي علامات تصحيح أضافتها اللعبة بنفسها إلى ذاكرة أوامر الرسوميات.\nإذا تم التفعيل الأفضل تمكين "تشخيص الأعطال".\nمفيد لبرامج مثل دوك. Save Data Path:\nThe folder where game save data will be saved. - Save Data Path:\nThe folder where game save data will be saved. + مسار حفظ البيانات:\nالمجلد الذي سيتم فيه حفظ بيانات تخزين اللعبة. Browse:\nBrowse for a folder to set as the save data path. - Browse:\nBrowse for a folder to set as the save data path. + تصفح:\nاختر مجلدًا لتحديده كمكان لحفظ بيانات التخزين. Release - Release + إصدار Nightly - Nightly + إصدار ليلي Set the volume of the background music. - Set the volume of the background music. + ضبط صوت الموسيقى في الخلفية. Enable Motion Controls - Enable Motion Controls + تفعيل التحكم بالحركة Save Data Path - Save Data Path + مسار بيانات الحفظ Browse @@ -1936,15 +1937,15 @@ async - async + غير متزامن sync - sync + متزامن Auto Select - Auto Select + تلقائي Directory to install games @@ -1952,11 +1953,11 @@ Directory to save data - Directory to save data + مسار الحفظ لبيانات الألعاب Video - Video + الفيديو Display Mode @@ -1980,11 +1981,11 @@ W: - W: + العرض: H: - H: + الارتفاع: Separate Log Files @@ -1992,7 +1993,7 @@ Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + ملفات السجل المنفصلة:\nيتم كتابة سجل منفصل لكل لعبه. Trophy Notification Position @@ -2028,7 +2029,7 @@ Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. - Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + مجلد المستخدم القابل للنقل:\nتخزين إعدادات وبيانات المحاكي التي ستُطبق فقط على هذا الإصدار في المجلد الحالي. بعد إنشاء مجلد المستخدم القابل للنقل، يجب إعادة تشغيل التطبيق للبدء في استخدامه. Cannot create portable user folder @@ -2040,7 +2041,7 @@ Portable user folder created - Portable user folder created + تم إنشاء مجلد مستخدم محمول %1 successfully created. @@ -2048,14 +2049,14 @@ Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + افتح مجلد الصور/الأصوات الخاصة بالجوائز المخصصة:\nيمكنك إضافة صور مخصصة للجوائز وصوت مرفق.\nأضف الملفات إلى مجلد custom_trophy بالأسماء التالية:\ntrophy.wav أو trophy.mp3، bronze.png، gold.png، platinum.png، silver.png\nملاحظة: الصوت سيعمل فقط في الإصدارات التي تستخدم QT. TrophyViewer Trophy Viewer - عارض الجوائز + عارض التروفي Select Game: diff --git a/src/qt_gui/translations/de_DE.ts b/src/qt_gui/translations/de_DE.ts index 9642cd500..c7a18dd99 100644 --- a/src/qt_gui/translations/de_DE.ts +++ b/src/qt_gui/translations/de_DE.ts @@ -1277,11 +1277,11 @@ Trophy Viewer - Trophy Viewer + Trophäenansicht No games found. Please add your games to your library first. - No games found. Please add your games to your library first. + Keine Spiele gefunden. Bitte fügen Sie zuerst Ihre Spiele zu Ihrer Bibliothek hinzu. Search... @@ -1409,43 +1409,43 @@ Play - Play + Spielen Pause - Pause + Pause Stop - Stop + Stopp Restart - Restart + Neustarten Full Screen - Full Screen + Vollbild Controllers - Controllers + Controller Keyboard - Keyboard + Tastatur Refresh List - Refresh List + Liste aktualisieren Resume - Resume + Fortsetzen Show Labels Under Icons - Show Labels Under Icons + Etiketten unter Symbolen anzeigen @@ -2048,7 +2048,11 @@ Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Öffnen Sie den Ordner für benutzerdefinierte Trophäenbilder/-sounds:\n +Sie können benutzerdefinierte Bilder zu den Trophäen hinzufügen und einen Ton hinzufügen.\n +Fügen Sie die Dateien dem Ordner custom_trophy mit folgenden Namen hinzu:\n +trophy.wav ODER trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\n +Hinweis: Der Sound funktioniert nur in Qt-Versionen. @@ -2059,23 +2063,23 @@ Select Game: - Select Game: + Spiel auswählen: Progress - Progress + Fortschritt Show Earned Trophies - Show Earned Trophies + Verdiente Trophäen anzeigen Show Not Earned Trophies - Show Not Earned Trophies + Nicht verdiente Trophäen anzeigen Show Hidden Trophies - Show Hidden Trophies + Verborgene Trophäen anzeigen diff --git a/src/qt_gui/translations/it_IT.ts b/src/qt_gui/translations/it_IT.ts index 908013004..8c9e53611 100644 --- a/src/qt_gui/translations/it_IT.ts +++ b/src/qt_gui/translations/it_IT.ts @@ -138,7 +138,7 @@ File Exists - Il file è presente + Il file esiste già File already exists. Do you want to replace it? diff --git a/src/qt_gui/translations/nl_NL.ts b/src/qt_gui/translations/nl_NL.ts index 66872455e..2d75b74eb 100644 --- a/src/qt_gui/translations/nl_NL.ts +++ b/src/qt_gui/translations/nl_NL.ts @@ -7,7 +7,7 @@ AboutDialog About shadPS4 - About shadPS4 + Over shadPS4 shadPS4 is an experimental open-source emulator for the PlayStation 4. @@ -582,7 +582,7 @@ Could not open the file for reading - Could not open the file for reading + Could not open the file for writing diff --git a/src/qt_gui/translations/sl_SI.ts b/src/qt_gui/translations/sl_SI.ts new file mode 100644 index 000000000..ab61a5d3a --- /dev/null +++ b/src/qt_gui/translations/sl_SI.ts @@ -0,0 +1,2081 @@ + + + + + + AboutDialog + + About shadPS4 + About shadPS4 + + + shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 is an experimental open-source emulator for the PlayStation 4. + + + This software should not be used to play games you have not legally obtained. + This software should not be used to play games you have not legally obtained. + + + + CheatsPatches + + Cheats / Patches for + Cheats / Patches for + + + Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n + Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n + + + No Image Available + No Image Available + + + Serial: + Serial: + + + Version: + Version: + + + Size: + Size: + + + Select Cheat File: + Select Cheat File: + + + Repository: + Repository: + + + Download Cheats + Download Cheats + + + Delete File + Delete File + + + No files selected. + No files selected. + + + You can delete the cheats you don't want after downloading them. + You can delete the cheats you don't want after downloading them. + + + Do you want to delete the selected file?\n%1 + Do you want to delete the selected file?\n%1 + + + Select Patch File: + Select Patch File: + + + Download Patches + Download Patches + + + Save + Save + + + Cheats + Cheats + + + Patches + Patches + + + Error + Error + + + No patch selected. + No patch selected. + + + Unable to open files.json for reading. + Unable to open files.json for reading. + + + No patch file found for the current serial. + No patch file found for the current serial. + + + Unable to open the file for reading. + Unable to open the file for reading. + + + Unable to open the file for writing. + Unable to open the file for writing. + + + Failed to parse XML: + Failed to parse XML: + + + Success + Success + + + Options saved successfully. + Options saved successfully. + + + Invalid Source + Invalid Source + + + The selected source is invalid. + The selected source is invalid. + + + File Exists + File Exists + + + File already exists. Do you want to replace it? + File already exists. Do you want to replace it? + + + Failed to save file: + Failed to save file: + + + Failed to download file: + Failed to download file: + + + Cheats Not Found + Cheats Not Found + + + No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. + No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. + + + Cheats Downloaded Successfully + Cheats Downloaded Successfully + + + You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. + You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. + + + Failed to save: + Failed to save: + + + Failed to download: + Failed to download: + + + Download Complete + Download Complete + + + 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. + 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. + + + Failed to parse JSON data from HTML. + Failed to parse JSON data from HTML. + + + Failed to retrieve HTML page. + Failed to retrieve HTML page. + + + The game is in version: %1 + The game is in version: %1 + + + The downloaded patch only works on version: %1 + The downloaded patch only works on version: %1 + + + You may need to update your game. + You may need to update your game. + + + Incompatibility Notice + Incompatibility Notice + + + Failed to open file: + Failed to open file: + + + XML ERROR: + XML ERROR: + + + Failed to open files.json for writing + Failed to open files.json for writing + + + Author: + Author: + + + Directory does not exist: + Directory does not exist: + + + Failed to open files.json for reading. + Failed to open files.json for reading. + + + Name: + Name: + + + Can't apply cheats before the game is started + Can't apply cheats before the game is started + + + Close + Close + + + + CheckUpdate + + Auto Updater + Auto Updater + + + Error + Error + + + Network error: + Network error: + + + The Auto Updater allows up to 60 update checks per hour.\nYou have reached this limit. Please try again later. + The Auto Updater allows up to 60 update checks per hour.\nYou have reached this limit. Please try again later. + + + Failed to parse update information. + Failed to parse update information. + + + No pre-releases found. + No pre-releases found. + + + Invalid release data. + Invalid release data. + + + No download URL found for the specified asset. + No download URL found for the specified asset. + + + Your version is already up to date! + Your version is already up to date! + + + Update Available + Update Available + + + Update Channel + Update Channel + + + Current Version + Current Version + + + Latest Version + Latest Version + + + Do you want to update? + Do you want to update? + + + Show Changelog + Show Changelog + + + Check for Updates at Startup + Check for Updates at Startup + + + Update + Update + + + No + No + + + Hide Changelog + Hide Changelog + + + Changes + Changes + + + Network error occurred while trying to access the URL + Network error occurred while trying to access the URL + + + Download Complete + Download Complete + + + The update has been downloaded, press OK to install. + The update has been downloaded, press OK to install. + + + Failed to save the update file at + Failed to save the update file at + + + Starting Update... + Starting Update... + + + Failed to create the update script file + Failed to create the update script file + + + + CompatibilityInfoClass + + Fetching compatibility data, please wait + Fetching compatibility data, please wait + + + Cancel + Cancel + + + Loading... + Loading... + + + Error + Error + + + Unable to update compatibility data! Try again later. + Unable to update compatibility data! Try again later. + + + Unable to open compatibility_data.json for writing. + Unable to open compatibility_data.json for writing. + + + Unknown + Unknown + + + Nothing + Nothing + + + Boots + Boots + + + Menus + Menus + + + Ingame + Ingame + + + Playable + Playable + + + + ControlSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Stick Deadzone (def:2 max:127) + Left Stick Deadzone (def:2 max:127) + + + Left Deadzone + Left Deadzone + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 / LB + L1 / LB + + + L2 / LT + L2 / LT + + + Back + Back + + + R1 / RB + R1 / RB + + + R2 / RT + R2 / RT + + + L3 + L3 + + + Options / Start + Options / Start + + + R3 + R3 + + + Face Buttons + Face Buttons + + + Triangle / Y + Triangle / Y + + + Square / X + Square / X + + + Circle / B + Circle / B + + + Cross / A + Cross / A + + + Right Stick Deadzone (def:2, max:127) + Right Stick Deadzone (def:2, max:127) + + + Right Deadzone + Right Deadzone + + + Right Stick + Right Stick + + + Color Adjustment + Color Adjustment + + + R: + R: + + + G: + G: + + + B: + B: + + + Override Lightbar Color + Override Lightbar Color + + + Override Color + Override Color + + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + + + + ElfViewer + + Open Folder + Open Folder + + + + GameInfoClass + + Loading game list, please wait :3 + Loading game list, please wait :3 + + + Cancel + Cancel + + + Loading... + Loading... + + + + GameInstallDialog + + shadPS4 - Choose directory + shadPS4 - Choose directory + + + Directory to install games + Directory to install games + + + Browse + Browse + + + Error + Error + + + Directory to install DLC + Directory to install DLC + + + + GameListFrame + + Icon + Icon + + + Name + Name + + + Serial + Serial + + + Compatibility + Compatibility + + + Region + Region + + + Firmware + Firmware + + + Size + Size + + + Version + Version + + + Path + Path + + + Play Time + Play Time + + + Never Played + Never Played + + + h + h + + + m + m + + + s + s + + + Compatibility is untested + Compatibility is untested + + + Game does not initialize properly / crashes the emulator + Game does not initialize properly / crashes the emulator + + + Game boots, but only displays a blank screen + Game boots, but only displays a blank screen + + + Game displays an image but does not go past the menu + Game displays an image but does not go past the menu + + + Game has game-breaking glitches or unplayable performance + Game has game-breaking glitches or unplayable performance + + + Game can be completed with playable performance and no major glitches + Game can be completed with playable performance and no major glitches + + + Click to see details on github + Click to see details on github + + + Last updated + Last updated + + + + GameListUtils + + B + B + + + KB + KB + + + MB + MB + + + GB + GB + + + TB + TB + + + + GuiContextMenus + + Create Shortcut + Create Shortcut + + + Cheats / Patches + Cheats / Patches + + + SFO Viewer + SFO Viewer + + + Trophy Viewer + Trophy Viewer + + + Open Folder... + Open Folder... + + + Open Game Folder + Open Game Folder + + + Open Save Data Folder + Open Save Data Folder + + + Open Log Folder + Open Log Folder + + + Copy info... + Copy info... + + + Copy Name + Copy Name + + + Copy Serial + Copy Serial + + + Copy Version + Copy Version + + + Copy Size + Copy Size + + + Copy All + Copy All + + + Delete... + Delete... + + + Delete Game + Delete Game + + + Delete Update + Delete Update + + + Delete DLC + Delete DLC + + + Delete Trophy + Delete Trophy + + + Compatibility... + Compatibility... + + + Update database + Update database + + + View report + View report + + + Submit a report + Submit a report + + + Shortcut creation + Shortcut creation + + + Shortcut created successfully! + Shortcut created successfully! + + + Error + Error + + + Error creating shortcut! + Error creating shortcut! + + + Game + Game + + + This game has no update to delete! + This game has no update to delete! + + + Update + Update + + + This game has no DLC to delete! + This game has no DLC to delete! + + + DLC + DLC + + + Delete %1 + Delete %1 + + + Are you sure you want to delete %1's %2 directory? + Are you sure you want to delete %1's %2 directory? + + + Open Update Folder + Open Update Folder + + + Delete Save Data + Delete Save Data + + + This game has no update folder to open! + This game has no update folder to open! + + + No log file found for this game! + No log file found for this game! + + + Failed to convert icon. + Failed to convert icon. + + + This game has no save data to delete! + This game has no save data to delete! + + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + + + Save Data + Save Data + + + Trophy + Trophy + + + SFO Viewer for + SFO Viewer for + + + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + MainWindow + + Open/Add Elf Folder + Open/Add Elf Folder + + + Boot Game + Boot Game + + + Check for Updates + Check for Updates + + + About shadPS4 + About shadPS4 + + + Configure... + Configure... + + + Recent Games + Recent Games + + + Open shadPS4 Folder + Open shadPS4 Folder + + + Exit + Exit + + + Exit shadPS4 + Exit shadPS4 + + + Exit the application. + Exit the application. + + + Show Game List + Show Game List + + + Game List Refresh + Game List Refresh + + + Tiny + Tiny + + + Small + Small + + + Medium + Medium + + + Large + Large + + + List View + List View + + + Grid View + Grid View + + + Elf Viewer + Elf Viewer + + + Game Install Directory + Game Install Directory + + + Download Cheats/Patches + Download Cheats/Patches + + + Dump Game List + Dump Game List + + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + + + Search... + Search... + + + File + File + + + View + View + + + Game List Icons + Game List Icons + + + Game List Mode + Game List Mode + + + Settings + Settings + + + Utils + Utils + + + Themes + Themes + + + Help + Help + + + Dark + Dark + + + Light + Light + + + Green + Green + + + Blue + Blue + + + Violet + Violet + + + toolBar + toolBar + + + Game List + Game List + + + * Unsupported Vulkan Version + * Unsupported Vulkan Version + + + Download Cheats For All Installed Games + Download Cheats For All Installed Games + + + Download Patches For All Games + Download Patches For All Games + + + Download Complete + Download Complete + + + You have downloaded cheats for all the games you have installed. + You have downloaded cheats for all the games you have installed. + + + Patches Downloaded Successfully! + Patches Downloaded Successfully! + + + All Patches available for all games have been downloaded. + All Patches available for all games have been downloaded. + + + Games: + Games: + + + ELF files (*.bin *.elf *.oelf) + ELF files (*.bin *.elf *.oelf) + + + Game Boot + Game Boot + + + Only one file can be selected! + Only one file can be selected! + + + Run Game + Run Game + + + Eboot.bin file not found + Eboot.bin file not found + + + Game is already running! + Game is already running! + + + shadPS4 + shadPS4 + + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + + + + SettingsDialog + + Settings + Settings + + + General + General + + + System + System + + + Console Language + Console Language + + + Emulator Language + Emulator Language + + + Emulator + Emulator + + + Default tab when opening settings + Default tab when opening settings + + + Show Game Size In List + Show Game Size In List + + + Show Splash + Show Splash + + + Enable Discord Rich Presence + Enable Discord Rich Presence + + + Username + Username + + + Trophy Key + Trophy Key + + + Trophy + Trophy + + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + + + Logger + Logger + + + Log Type + Log Type + + + Log Filter + Log Filter + + + Open Log Location + Open Log Location + + + Input + Input + + + Cursor + Cursor + + + Hide Cursor + Hide Cursor + + + Hide Cursor Idle Timeout + Hide Cursor Idle Timeout + + + s + s + + + Controller + Controller + + + Back Button Behavior + Back Button Behavior + + + Graphics + Graphics + + + GUI + GUI + + + User + User + + + Graphics Device + Graphics Device + + + Vblank Divider + Vblank Divider + + + Advanced + Advanced + + + Enable Shaders Dumping + Enable Shaders Dumping + + + Enable NULL GPU + Enable NULL GPU + + + Enable HDR + Enable HDR + + + Paths + Paths + + + Game Folders + Game Folders + + + Add... + Add... + + + Remove + Remove + + + Debug + Debug + + + Enable Debug Dumping + Enable Debug Dumping + + + Enable Vulkan Validation Layers + Enable Vulkan Validation Layers + + + Enable Vulkan Synchronization Validation + Enable Vulkan Synchronization Validation + + + Enable RenderDoc Debugging + Enable RenderDoc Debugging + + + Enable Crash Diagnostics + Enable Crash Diagnostics + + + Collect Shaders + Collect Shaders + + + Copy GPU Buffers + Copy GPU Buffers + + + Host Debug Markers + Host Debug Markers + + + Guest Debug Markers + Guest Debug Markers + + + Update + Update + + + Check for Updates at Startup + Check for Updates at Startup + + + Always Show Changelog + Always Show Changelog + + + Update Channel + Update Channel + + + Check for Updates + Check for Updates + + + GUI Settings + GUI Settings + + + Title Music + Title Music + + + Disable Trophy Notification + Disable Trophy Notification + + + Background Image + Background Image + + + Show Background Image + Show Background Image + + + Opacity + Opacity + + + Play title music + Play title music + + + Update Compatibility Database On Startup + Update Compatibility Database On Startup + + + Game Compatibility + Game Compatibility + + + Display Compatibility Data + Display Compatibility Data + + + Update Compatibility Database + Update Compatibility Database + + + Volume + Volume + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Close + Close + + + Point your mouse at an option to display its description. + Point your mouse at an option to display its description. + + + Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. + Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. + + + Emulator Language:\nSets the language of the emulator's user interface. + Emulator Language:\nSets the language of the emulator's user interface. + + + Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. + Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. + + + Enable Discord Rich Presence:\nDisplays the emulator icon and relevant information on your Discord profile. + Enable Discord Rich Presence:\nDisplays the emulator icon and relevant information on your Discord profile. + + + Username:\nSets the PS4's account username, which may be displayed by some games. + Username:\nSets the PS4's account username, which may be displayed by some games. + + + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + + + Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. + Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. + + + Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. + Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. + + + Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. + Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. + + + Background Image:\nControl the opacity of the game background image. + Background Image:\nControl the opacity of the game background image. + + + Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. + Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. + + + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + + + Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. + Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. + + + Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. + Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. + + + Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. + Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. + + + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + + + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + + + Update Compatibility Database:\nImmediately update the compatibility database. + Update Compatibility Database:\nImmediately update the compatibility database. + + + Never + Never + + + Idle + Idle + + + Always + Always + + + Touchpad Left + Touchpad Left + + + Touchpad Right + Touchpad Right + + + Touchpad Center + Touchpad Center + + + None + None + + + Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it. + Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it. + + + Width/Height:\nSets the size of the emulator window at launch, which can be resized during gameplay.\nThis is different from the in-game resolution. + Width/Height:\nSets the size of the emulator window at launch, which can be resized during gameplay.\nThis is different from the in-game resolution. + + + Vblank Divider:\nThe frame rate at which the emulator refreshes at is multiplied by this number. Changing this may have adverse effects, such as increasing the game speed, or breaking critical game functionality that does not expect this to change! + Vblank Divider:\nThe frame rate at which the emulator refreshes at is multiplied by this number. Changing this may have adverse effects, such as increasing the game speed, or breaking critical game functionality that does not expect this to change! + + + Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. + Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. + + + Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. + Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. + + + Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + + + Game Folders:\nThe list of folders to check for installed games. + Game Folders:\nThe list of folders to check for installed games. + + + Add:\nAdd a folder to the list. + Add:\nAdd a folder to the list. + + + Remove:\nRemove a folder from the list. + Remove:\nRemove a folder from the list. + + + Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. + Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. + + + Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation. + Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation. + + + Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks.\nThis will reduce performance and likely change the behavior of emulation. + Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks.\nThis will reduce performance and likely change the behavior of emulation. + + + Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. + Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. + + + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + + + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + + + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + + + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + + + Save Data Path:\nThe folder where game save data will be saved. + Save Data Path:\nThe folder where game save data will be saved. + + + Browse:\nBrowse for a folder to set as the save data path. + Browse:\nBrowse for a folder to set as the save data path. + + + Release + Release + + + Nightly + Nightly + + + Set the volume of the background music. + Set the volume of the background music. + + + Enable Motion Controls + Enable Motion Controls + + + Save Data Path + Save Data Path + + + Browse + Browse + + + async + async + + + sync + sync + + + Auto Select + Auto Select + + + Directory to install games + Directory to install games + + + Directory to save data + Directory to save data + + + Video + Video + + + Display Mode + Display Mode + + + Windowed + Windowed + + + Fullscreen + Fullscreen + + + Fullscreen (Borderless) + Fullscreen (Borderless) + + + Window Size + Window Size + + + W: + W: + + + H: + H: + + + Separate Log Files + Separate Log Files + + + Separate Log Files:\nWrites a separate logfile for each game. + Separate Log Files:\nWrites a separate logfile for each game. + + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + + + + TrophyViewer + + Trophy Viewer + Trophy Viewer + + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + + + diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index 311633a99..50314a9b2 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -1409,43 +1409,43 @@ Play - Play + Luaj Pause - Pause + Pezullo Stop - Stop + Ndalo Restart - Restart + Rinis Full Screen - Full Screen + Ekran i Plotë Controllers - Controllers + Dorezat Keyboard - Keyboard + Tastiera Refresh List - Refresh List + Rifresko Listën Resume - Resume + Rifillo Show Labels Under Icons - Show Labels Under Icons + Shfaq Etiketat Poshtë Ikonave diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 077455558..bd051651d 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -7,26 +7,26 @@ AboutDialog About shadPS4 - About shadPS4 + 關於 shadPS4 shadPS4 is an experimental open-source emulator for the PlayStation 4. - shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 是一款試驗性質的 PlayStation 4 開源模擬器軟體。 This software should not be used to play games you have not legally obtained. - This software should not be used to play games you have not legally obtained. + 不得用此軟體來遊玩你未曾合法獲取的遊戲。 CheatsPatches Cheats / Patches for - Cheats / Patches for + 金手指/修補程式: Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n - 作弊/補丁為實驗性功能。\n請小心使用。\n\n透過選擇儲存庫並點擊下載按鈕來單獨下載作弊程式。\n在“補丁”標籤頁中,您可以一次下載所有補丁,選擇要使用的補丁並保存您的選擇。\n\n由於我們不開發作弊/補丁,\n請將問題報告給作弊程式的作者。\n\n創建了新的作弊程式?請訪問:\n + 金手指/修補檔屬於試驗性質功能。\n請小心使用。\n\n透過選取儲存庫並點擊下載按鈕來單獨下載作弊程式。\n在“修補檔”標籤頁中,你可以一次下載所有修補檔,選擇要使用的補丁並保存您的選擇。\n\n由於我們不負責開發金手指/修補檔,\n請將問題匯報給作弊程式的作者。\n\n建立了新的作弊程式?請瀏覽:\n No Image Available @@ -34,27 +34,27 @@ Serial: - 序號: + 序號: Version: - 版本: + 版本: Size: - 大小: + 大小: Select Cheat File: - 選擇作弊檔案: + 選取金手指檔案: Repository: - 儲存庫: + 儲存庫: Download Cheats - 下載作弊碼 + 下載金手指 Delete File @@ -62,19 +62,19 @@ No files selected. - 沒有選擇檔案。 + 未選取檔案。 You can delete the cheats you don't want after downloading them. - 您可以在下載後刪除不需要的作弊碼。 + 下載後可以刪除不需要的金手指。 Do you want to delete the selected file?\n%1 - 您是否要刪除選定的檔案?\n%1 + 確定要刪除選取的檔案?\n%1 Select Patch File: - 選擇修補檔案: + 選取修補檔案: Download Patches @@ -86,7 +86,7 @@ Cheats - 作弊碼 + 金手指 Patches @@ -98,27 +98,27 @@ No patch selected. - 未選擇修補檔。 + 未選取修補檔。 Unable to open files.json for reading. - 無法打開 files.json 進行讀取。 + 無法開啟 files.json 進行讀取。 No patch file found for the current serial. - 找不到當前序號的修補檔。 + 找不到目前序號的修補檔。 Unable to open the file for reading. - 無法打開檔案進行讀取。 + 無法開啟檔案進行讀取。 Unable to open the file for writing. - 無法打開檔案進行寫入。 + 無法開啟檔案進行寫入。 Failed to parse XML: - 解析 XML 失敗: + 解析 XML 失敗: Success @@ -126,7 +126,7 @@ Options saved successfully. - 選項已成功儲存。 + 已成功儲存選項。 Invalid Source @@ -134,7 +134,7 @@ The selected source is invalid. - 選擇的來源無效。 + 選取的來源無效。 File Exists @@ -142,39 +142,39 @@ File already exists. Do you want to replace it? - 檔案已存在。您是否希望替換它? + 檔案已存在,您要取代嗎? Failed to save file: - 無法儲存檔案: + 無法儲存檔案: Failed to download file: - 無法下載檔案: + 無法下載檔案: Cheats Not Found - 未找到作弊碼 + 找不到金手指 No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. - 在此版本的儲存庫中未找到該遊戲的作弊碼,請嘗試另一個儲存庫或不同版本的遊戲。 + 在此版本的儲存庫中未找到該遊戲的金手指,請嘗試另一個儲存庫或不同版本的遊戲。 Cheats Downloaded Successfully - 作弊碼下載成功 + 全手指下載成功 You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list. - 您已成功下載該遊戲版本的作弊碼 從選定的儲存庫中。 您可以嘗試從其他儲存庫下載,如果可用,您也可以選擇從列表中選擇檔案來使用它。 + 你已成功從選取的儲存庫中下載該遊戲版本的金手指。 可用的話,你可以嘗試從其他儲存庫下載,你也可以從列表中選取檔案來使用它。 Failed to save: - 儲存失敗: + 儲存失敗: Failed to download: - 下載失敗: + 下載失敗: Download Complete @@ -182,15 +182,15 @@ 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. - 修補檔下載成功!所有遊戲的修補檔已下載完成,無需像作弊碼那樣為每個遊戲單獨下載。如果補丁未顯示,可能是該補丁不適用於特定的序號和遊戲版本。 + 修補檔下載成功!所有遊戲的修補檔已下載完成,無需像金手指那樣為每個遊戲單獨下載。如果修補檔仍未顯示,可能該修補檔是不適用於特定的序號和遊戲版本。 Failed to parse JSON data from HTML. - 無法從 HTML 解析 JSON 數據。 + 無法從 HTML 分析 JSON 資料。 Failed to retrieve HTML page. - 無法檢索 HTML 頁面。 + 無法接收 HTML 頁面。 The game is in version: %1 @@ -198,11 +198,11 @@ The downloaded patch only works on version: %1 - 下載的補丁僅適用於版本: %1 + 下載的修補檔僅適用於版本: %1 You may need to update your game. - 您可能需要更新遊戲。 + 你可能需要更新你的遊戲。 Incompatibility Notice @@ -210,35 +210,35 @@ Failed to open file: - 無法打開檔案: + 無法開啟檔案: XML ERROR: - XML 錯誤: + XML 錯誤: Failed to open files.json for writing - 無法打開 files.json 進行寫入 + 無法開啟 files.json 進行寫入 Author: - 作者: + 作者: Directory does not exist: - 目錄不存在: + 目錄不存在: Failed to open files.json for reading. - 無法打開 files.json 進行讀取。 + 無法開啟 files.json 進行讀取。 Name: - 名稱: + 名稱: Can't apply cheats before the game is started - 在遊戲開始之前無法應用作弊。 + 在遊戲'開始之前無法套用金手指 Close @@ -265,19 +265,19 @@ Failed to parse update information. - 無法解析更新資訊。 + 無法分析更新資訊。 No pre-releases found. - 未找到預發布版本。 + 未找到預發佈版本。 Invalid release data. - 無效的發行數據。 + 無效的發佈資料。 No download URL found for the specified asset. - 未找到指定資產的下載 URL。 + 未找到指定資源的下載 URL。 Your version is already up to date! @@ -293,7 +293,7 @@ Current Version - 當前版本 + 目前版本 Latest Version @@ -301,7 +301,7 @@ Do you want to update? - 您想要更新嗎? + 你要更新嗎? Show Changelog @@ -321,7 +321,7 @@ Hide Changelog - 隱藏變更日誌 + 隱藏變更日誌紀錄 Changes @@ -329,7 +329,7 @@ Network error occurred while trying to access the URL - 嘗試訪問 URL 時發生網路錯誤 + 嘗試存取 URL 時發生了網路錯誤 Download Complete @@ -337,11 +337,11 @@ The update has been downloaded, press OK to install. - 更新已下載,按 OK 安裝。 + 已下載更新,按 OK 安裝。 Failed to save the update file at - 無法將更新文件保存到 + 無法將更新檔案儲存到 Starting Update... @@ -349,7 +349,7 @@ Failed to create the update script file - 無法創建更新腳本文件 + 無法建立更新程式碼檔案 @@ -364,7 +364,7 @@ Loading... - 載入中... + 正在載入… Error @@ -388,134 +388,134 @@ Boots - 靴子 + 可啟動 Menus - 選單 + 可顯示標題畫面 Ingame - 遊戲內 + 可進入遊戲 Playable - 可玩 + 可如常遊玩 ControlSettings Configure Controls - 操控設定 + 操控組態 D-Pad - D-Pad + 十字方向鍵 Up - Up + Left - Left + Right - Right + Down - Down + Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + 左搖桿無效區域(預設:2 最大:127) Left Deadzone - Left Deadzone + 左無效區域 Left Stick - Left Stick + 左搖桿 Config Selection - Config Selection + 選取組態 Common Config - Common Config + 通用組態 Use per-game configs - Use per-game configs + 使用個別遊戲組態 L1 / LB - L1 / LB + L1 / LB L2 / LT - L2 / LT + L2 / LT Back - Back + Back R1 / RB - R1 / RB + R1 / RB R2 / RT - R2 / RT + R2 / RT L3 - L3 + L3 Options / Start - Options / Start + Options / Start R3 - R3 + R3 Face Buttons - Face Buttons + 功能鍵(動作按鈕) Triangle / Y - Triangle / Y + 三角 / Y Square / X - Square / X + 正方 / X Circle / B - Circle / B + 圖形 / B Cross / A - Cross / A + 交叉 / A Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + 右搖桿無效區域(預設:2 最大:127) Right Deadzone - Right Deadzone + 右無效區域 Right Stick - Right Stick + 右搖桿 Color Adjustment @@ -523,35 +523,35 @@ R: - R: + 紅: G: - G: + 綠: B: - B: + 藍: Override Lightbar Color - Override Lightbar Color + 覆寫燈條顏色 Override Color - Override Color + 覆寫顏色 Unable to Save - 無法保存 + 無法儲存 Cannot bind axis values more than once - Cannot bind axis values more than once + 不能同時連結多個方向軸設定值 Save - 保存 + 儲存 Apply @@ -559,7 +559,7 @@ Restore Defaults - Restore Defaults + 還原成預設值 Cancel @@ -570,11 +570,11 @@ EditorDialog Edit Keyboard + Mouse and Controller input bindings - Edit Keyboard + Mouse and Controller input bindings + 編輯鍵盤滑鼠和控制器按鈕連結 Use Per-Game configs - Use Per-Game configs + 使用個別遊戲組態 Error @@ -582,11 +582,11 @@ Could not open the file for reading - Could not open the file for reading + 無法開啟檔案進行讀取 Could not open the file for writing - Could not open the file for writing + 無法開啟檔案進行寫入 Save Changes @@ -594,68 +594,68 @@ Do you want to save changes? - Do you want to save changes? + 你要儲存變更嗎? Help - Help + 幫助 Do you want to reset your custom default config to the original default config? - Do you want to reset your custom default config to the original default config? + 你要將自訂預設組態還原成原始預設組態嗎? Do you want to reset this config to your custom default config? - Do you want to reset this config to your custom default config? + 你要將此組態還原成自訂預設組態嗎? Reset to Default - Reset to Default + 還原成預設值 ElfViewer Open Folder - Open Folder + 開啟資料夾 GameInfoClass Loading game list, please wait :3 - Loading game list, please wait :3 + 正在載入遊戲列表,請稍候 :3 Cancel - Cancel + 取消 Loading... - Loading... + 正在載入… GameInstallDialog shadPS4 - Choose directory - shadPS4 - Choose directory + shadPS4 - 選取檔案目錄 Directory to install games - Directory to install games + 要安裝遊戲的目錄 Browse - Browse + 瀏覽 Error - Error + 錯誤 Directory to install DLC - Directory to install DLC + 要安裝DLC的目錄 @@ -674,7 +674,7 @@ Compatibility - Compatibility + 相容性 Region @@ -682,7 +682,7 @@ Firmware - 固件 + 韌體 Size @@ -702,43 +702,43 @@ Never Played - Never Played + 從未玩過 h - h + 小時 m - m + 分鐘 s - s + Compatibility is untested - Compatibility is untested + 未曾測試相容性 Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + 無法正確初始化遊戲/模擬器當機 Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + 遊戲可啟動,但只會顯示空白畫面 Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + 可以顯示遊戲畫面,但是在標題畫面無法繼續 Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + 遊戲有嚴重的BUG或者效能過低而無法遊玩 Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + 遊戲能在可以合理遊玩的效能下破關也沒有重大BUG Click to see details on github @@ -753,447 +753,447 @@ GameListUtils B - B + B KB - KB + KB MB - MB + MB GB - GB + GB TB - TB + TB GuiContextMenus Create Shortcut - Create Shortcut + 建立捷徑 Cheats / Patches - Zuòbì / Xiūbǔ chéngshì + 金手指/修補程式 SFO Viewer - SFO Viewer + SFO 檢視器 Trophy Viewer - Trophy Viewer + 獎盃檢視器 Open Folder... - 打開資料夾... + 開啟資料夾… Open Game Folder - 打開遊戲資料夾 + 開啟遊戲資料夾 Open Save Data Folder - 打開存檔資料夾 + 開啟遊戲存檔資料夾 Open Log Folder - 打開日誌資料夾 + 開啟日誌紀錄資料夾 Copy info... - Copy info... + 複製資訊... Copy Name - Copy Name + 複製名稱 Copy Serial - Copy Serial + 複製序號 Copy Version - Copy Version + 複製版本資訊 Copy Size - Copy Size + 複製大小 Copy All - Copy All + 全部複製 Delete... - Delete... + 刪除… Delete Game - Delete Game + 刪除遊戲 Delete Update - Delete Update + 刪除更新 Delete DLC - Delete DLC + 刪除DLC Delete Trophy - Delete Trophy + 刪除獎盃 Compatibility... - Compatibility... + 相容性… Update database - Update database + 更新資料庫 View report - View report + 檢視報告 Submit a report - Submit a report + 提交報告 Shortcut creation - Shortcut creation + 建立捷徑 Shortcut created successfully! - Shortcut created successfully! + 成功建立捷徑! Error - Error + 錯誤 Error creating shortcut! - Error creating shortcut! + 建立捷徑出錯! Game - Game + 遊戲 This game has no update to delete! - This game has no update to delete! + 此遊戲沒有可刪除的更新! Update - Update + 更新 This game has no DLC to delete! - This game has no DLC to delete! + 此遊戲沒有可刪除的DLC! DLC - DLC + DLC Delete %1 - Delete %1 + 刪除 %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + 確定要刪除 %1' 的%2目錄? Open Update Folder - Open Update Folder + 開啟更新資料夾 Delete Save Data - Delete Save Data + 刪除遊戲存檔 This game has no update folder to open! - This game has no update folder to open! + 此遊戲沒有可開啟的更新資料夾! No log file found for this game! - No log file found for this game! + 找不到此遊戲的日誌紀錄檔! Failed to convert icon. - Failed to convert icon. + 轉換圖示失敗。 This game has no save data to delete! - This game has no save data to delete! + 此遊戲沒有可刪除的遊戲存檔! This game has no saved trophies to delete! - This game has no saved trophies to delete! + 此遊戲沒有可刪除的已儲存獎盃! Save Data - Save Data + 遊戲存檔 Trophy - Trophy + 獎盃 SFO Viewer for - SFO Viewer for + SFO 檢視器: HelpDialog Quickstart - Quickstart + 快速入門 FAQ - FAQ + 常見問答 Syntax - Syntax + 語法 Special Bindings - Special Bindings + 特殊連結 Keybindings - Keybindings + 按鍵連結 KBMSettings Configure Controls - Configure Controls + 操控組態 D-Pad - D-Pad + 十字方向鍵 Up - Up + unmapped - unmapped + 鍵位未有對應 Left - Left + Right - Right + Down - Down + Left Analog Halfmode - Left Analog Halfmode + 左類比搖桿半速模式 hold to move left stick at half-speed - hold to move left stick at half-speed + 按住可半速移動左搖桿 Left Stick - Left Stick + 左搖桿 Config Selection - Config Selection + 選取組態 Common Config - Common Config + 通用組態 Use per-game configs - Use per-game configs + 使用個別遊戲組態 L1 - L1 + L1 L2 - L2 + L2 Text Editor - Text Editor + 文字編輯器 Help - Help + 幫助 R1 - R1 + R1 R2 - R2 + R2 L3 - L3 + L3 Touchpad Click - Touchpad Click + 觸控板點擊 Mouse to Joystick - Mouse to Joystick + 滑鼠操控操縱桿 *press F7 ingame to activate - *press F7 ingame to activate + *在遊戲中按F7啟動 R3 - R3 + R3 Options - Options + Options Mouse Movement Parameters - Mouse Movement Parameters + 滑鼠指標移動參數 note: click Help Button/Special Keybindings for more information - note: click Help Button/Special Keybindings for more information + 注意:點擊 幫助 按鈕/特殊按鍵連結以取得更多資訊 Face Buttons - Face Buttons + 功能鍵(動作按鈕) Triangle - Triangle + 三角 Square - Square + 正方 Circle - Circle + 圓形 Cross - Cross + 交叉 Right Analog Halfmode - Right Analog Halfmode + 右類比搖桿半速模式 hold to move right stick at half-speed - hold to move right stick at half-speed + 按住可半速移動右搖桿 Right Stick - Right Stick + 右搖桿 Speed Offset (def 0.125): - Speed Offset (def 0.125): + 速度偏移量(預設為 0.125): Copy from Common Config - Copy from Common Config + 從通用組態中複製設定值 Deadzone Offset (def 0.50): - Deadzone Offset (def 0.50): + 無效區偏移量(預設為 0.50): Speed Multiplier (def 1.0): - Speed Multiplier (def 1.0): + 速度倍數(預設為 1.0): Common Config Selected - Common Config Selected + 已選取通用組態 This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. - This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + 此按鈕會將通用組態中的鍵位對應複製到目前選取的組態檔案,選取通用組態時無法使用此按鈕。 Copy values from Common Config - Copy values from Common Config + 從通用組態中複製設定值 Do you want to overwrite existing mappings with the mappings from the Common Config? - Do you want to overwrite existing mappings with the mappings from the Common Config? + 你要將通用組態中的鍵位對應覆寫到現有的鍵位對應嗎? Unable to Save - Unable to Save + 無法儲存 Cannot bind any unique input more than once - Cannot bind any unique input more than once + 任何唯一的鍵位都不能重複連結 Press a key - Press a key + 按下按鍵 Cannot set mapping - Cannot set mapping + 無法設定鍵位對應 Mousewheel cannot be mapped to stick outputs - Mousewheel cannot be mapped to stick outputs + 滑鼠滾輪無法對應到搖桿輸出 Save - Save + 儲存 Apply - Apply + 套用 Restore Defaults - Restore Defaults + 還原成預設值 Cancel - Cancel + 取消 MainWindow Open/Add Elf Folder - Open/Add Elf Folder + 開啟/新增 Elf 資料夾 Boot Game - Boot Game + 啟動遊戲 Check for Updates @@ -1201,119 +1201,119 @@ About shadPS4 - About shadPS4 + 關於 shadPS4 Configure... - Configure... + 組態… Recent Games - Recent Games + 最近遊玩的遊戲 Open shadPS4 Folder - Open shadPS4 Folder + 開啟 shadPS4 資料夾 Exit - Exit + 退出 Exit shadPS4 - Exit shadPS4 + 退出 shadPS4 Exit the application. - Exit the application. + 退出應用程式。 Show Game List - Show Game List + 顯示遊戲列表 Game List Refresh - Game List Refresh + 重新整理遊戲列表 Tiny - Tiny + 微小 Small - Small + Medium - Medium + Large - Large + List View - List View + 以列表顯示 Grid View - Grid View + 以網格顯視 Elf Viewer - Elf Viewer + Elf 檢視器 Game Install Directory - Game Install Directory + 遊戲安裝目錄 Download Cheats/Patches - Xiàzài Zuòbì / Xiūbǔ chéngshì + 下載金手指/修補檔 Dump Game List - Dump Game List + 偵印遊戲列表 Trophy Viewer - Trophy Viewer + 獎盃檢視器 No games found. Please add your games to your library first. - No games found. Please add your games to your library first. + 找不到遊戲,請先將你的遊戲加入遊戲庫中。 Search... - Search... + 搜尋… File - File + 檔案 View - View + 檢視 Game List Icons - Game List Icons + 遊戲列表圖示 Game List Mode - Game List Mode + 遊戲列表模式 Settings - Settings + 設定 Utils - Utils + 工具 Themes - Themes + 主題 Help @@ -1321,27 +1321,27 @@ Dark - Dark + 深色 Light - Light + 淺色 Green - Green + 綠色 Blue - Blue + 藍色 Violet - Violet + 紫羅蘭色 toolBar - toolBar + 工具列 Game List @@ -1353,7 +1353,7 @@ Download Cheats For All Installed Games - 下載所有已安裝遊戲的作弊碼 + 下載所有已安裝遊戲的金手指 Download Patches For All Games @@ -1365,7 +1365,7 @@ You have downloaded cheats for all the games you have installed. - 您已經下載了所有已安裝遊戲的作弊碼。 + 你已下載所有已安裝遊戲的金手指。 Patches Downloaded Successfully! @@ -1377,7 +1377,7 @@ Games: - 遊戲: + 遊戲: ELF files (*.bin *.elf *.oelf) @@ -1389,102 +1389,102 @@ Only one file can be selected! - 只能選擇一個檔案! + 只能選取一個檔案! Run Game - Run Game + 執行遊戲 Eboot.bin file not found - Eboot.bin file not found + 找不到 Eboot.bin 檔案 Game is already running! - Game is already running! + 已在執行遊戲! shadPS4 - shadPS4 + shadPS4 Play - Play + 遊玩 Pause - Pause + 暫停 Stop - Stop + 停止 Restart - Restart + 重新啟動 Full Screen - Full Screen + 全螢幕 Controllers - Controllers + 控制器 Keyboard - Keyboard + 鍵盤 Refresh List - Refresh List + 重新整理列表 Resume - Resume + 繼續 Show Labels Under Icons - Show Labels Under Icons + 在圖示下方顯示標籤 SettingsDialog Settings - Settings + 設定 General - General + 一般 System - System + 系統 Console Language - Console Language + 主機語言 Emulator Language - Emulator Language + 模擬器語言 Emulator - Emulator + 模擬器 Default tab when opening settings - 打開設置時的默認選項卡 + 開啟設定時的預設選項頁面 Show Game Size In List - 顯示遊戲大小在列表中 + 在列表中顯示遊戲大小 Show Splash - Show Splash + 顯示啟動畫面 Enable Discord Rich Presence @@ -1492,35 +1492,35 @@ Username - Username + 使用者名稱 Trophy Key - Trophy Key + 獎盃金鑰 Trophy - Trophy + 獎盃 Open the custom trophy images/sounds folder - Open the custom trophy images/sounds folder + 開啟自訂獎盃圖像/音效資料夾 Logger - Logger + 日誌紀錄器 Log Type - Log Type + 日誌記錄類型 Log Filter - Log Filter + 日誌紀錄過濾器 Open Log Location - 開啟日誌位置 + 開啟日誌記錄位置 Input @@ -1528,19 +1528,19 @@ Cursor - 游標 + 滑鼠遊標 Hide Cursor - 隱藏游標 + 隱藏滑鼠指標 Hide Cursor Idle Timeout - 游標空閒超時隱藏 + 空閒滑鼠指標逾時隱藏 s - s + Controller @@ -1552,7 +1552,7 @@ Graphics - Graphics + 圖形 GUI @@ -1564,27 +1564,27 @@ Graphics Device - Graphics Device + 圖形裝置 Vblank Divider - Vblank Divider + Vblank 分隔符 Advanced - Advanced + 進階 Enable Shaders Dumping - Enable Shaders Dumping + 啟用著色器傾印 Enable NULL GPU - Enable NULL GPU + 啟用空值GPU Enable HDR - Enable HDR + 啟用 HDR Paths @@ -1596,7 +1596,7 @@ Add... - 添加... + 新增… Remove @@ -1604,43 +1604,43 @@ Debug - Debug + 偵錯 Enable Debug Dumping - Enable Debug Dumping + 啟用偵錯傾印 Enable Vulkan Validation Layers - Enable Vulkan Validation Layers + 啟用 Vulkan 驗證層 Enable Vulkan Synchronization Validation - Enable Vulkan Synchronization Validation + 啟用 Vulkan 同步驗證 Enable RenderDoc Debugging - Enable RenderDoc Debugging + 啟用 RenderDoc 偵錯 Enable Crash Diagnostics - Enable Crash Diagnostics + 啟用當機診斷 Collect Shaders - Collect Shaders + 收集著色器 Copy GPU Buffers - Copy GPU Buffers + 複製 GPU 緩衝區 Host Debug Markers - Host Debug Markers + Host 偵錯標記 Guest Debug Markers - Guest Debug Markers + Guest 偵錯標記 Update @@ -1664,27 +1664,27 @@ GUI Settings - 介面設置 + 介面設定 Title Music - Title Music + 標題音樂 Disable Trophy Notification - Disable Trophy Notification + 停用獎盃通知 Background Image - Background Image + 背景圖像 Show Background Image - Show Background Image + 顯示背景影像 Opacity - Opacity + 透明度 Play title music @@ -1692,19 +1692,19 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + 啟動時更新相容性資料庫 Game Compatibility - Game Compatibility + 遊戲相容性 Display Compatibility Data - Display Compatibility Data + 顯示相容性資料 Update Compatibility Database - Update Compatibility Database + 更新相容性資料庫 Volume @@ -1720,7 +1720,7 @@ Restore Defaults - 還原預設值 + 還原成預設值 Close @@ -1728,79 +1728,79 @@ Point your mouse at an option to display its description. - 將鼠標指向選項以顯示其描述。 + 將滑鼠指標指向選項就會顯示其描述。 Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. - 主機語言:\n設定PS4遊戲使用的語言。\n建議將其設置為遊戲支持的語言,這會因地區而異。 + 主機語言:\n設定 PS4 遊戲使用的語言。 \n建議將其設定為遊戲'支援的語言,該語言因地區而異。 Emulator Language:\nSets the language of the emulator's user interface. - 模擬器語言:\n設定模擬器的用戶介面的語言。 + 模擬器語言:\n設定模擬器'的使用者介面語言。 Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. - 顯示啟動畫面:\n在遊戲啟動時顯示遊戲的啟動畫面(特殊圖片)。 + 顯示啟動畫面:\n遊戲'開始時顯示遊戲的啟動畫面(特殊圖像)。 Enable Discord Rich Presence:\nDisplays the emulator icon and relevant information on your Discord profile. - 啟用 Discord Rich Presence:\n在您的 Discord 個人檔案上顯示模擬器圖標和相關信息。 + 啟用 Discord Rich Presence:\n在你的 Discord 個人檔案上顯示模擬器圖示和相關資訊。 Username:\nSets the PS4's account username, which may be displayed by some games. - 用戶名:\n設定PS4帳號的用戶名,某些遊戲中可能會顯示。 + 使用者名稱:\n設定 PS4 '的帳號使用者名稱,某些遊戲可能會顯示該使用者名稱。 Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + 獎盃金鑰:\n用於解密獎盃的金鑰,必須從你的越獄主機中獲取。 \n僅包含十六進制字元。 Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. - 日誌類型:\n設定是否同步日誌窗口的輸出以提高性能。可能對模擬產生不良影響。 + 日誌記錄類型:\n設定是否同步日誌記錄視窗的輸出以提高效能。可能會對模擬產生負面影響。 Log Filter:\nFilters the log to only print specific information.\nExamples: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nLevels: Trace, Debug, Info, Warning, Error, Critical - in this order, a specific level silences all levels preceding it in the list and logs every level after it. - 日誌過濾器:\n過濾日誌以僅打印特定信息。\n範例:"Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" 等級: Trace, Debug, Info, Warning, Error, Critical - 以此順序,特定級別靜音所有前面的級別,並記錄其後的每個級別。 + 日誌記錄過濾器:\n過濾日誌記錄以僅列出特定資訊。 \n範例:{[="-=]}Core:Trace{[="-=]} {[="-=]}Lib.Pad:Debug Common.Filesystem:Error{[="-=]} {[="-=]}*:Critical{[="-=]}\n等級:Trace、Debug、Info、Warning、Error、Critical - 按照此順序,指定等級之後會不列出列表中等級低於其的所有資訊,並記錄列出等級高於其/同級的資訊。 Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable. - 更新:\nRelease: 每月發布的官方版本,可能非常舊,但更可靠且經過測試。\nNightly: 開發版本,擁有所有最新的功能和修復,但可能包含錯誤,穩定性較差。 + 更新:\n正式版:每月發布的正式版本可能會過時,但更可靠而且有經過測試。 \n預覽版:具有所有最新功能和修復的開發版本,但可能含有一些錯誤而且不太穩定。 Background Image:\nControl the opacity of the game background image. - Background Image:\nControl the opacity of the game background image. + 背景圖像:\n控制遊戲背​​景圖像的能見度。 Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. - 播放標題音樂:\n如果遊戲支持,啟用在GUI中選擇遊戲時播放特殊音樂。 + 播放標題音樂:\n如果遊戲支援的話,則啟用在 GUI 中選取遊戲時播放特殊音樂。 Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + 停用獎盃彈出視窗:\n停用遊戲內獎盃通知,仍然可以使用獎盃檢視器(在主視窗中右鍵單擊遊戲)追蹤獎盃進度。 Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. - 隱藏游標:\n選擇游標何時消失:\n從不: 您將始終看到滑鼠。\n閒置: 設定在閒置後消失的時間。\n始終: 您將永遠看不到滑鼠。 + 隱藏滑鼠指標:\n選擇滑鼠指標何時消失:\n從不:你始終會看到滑鼠指標。 \n閒置:設定滑鼠指標空閒多久後消失的時間。 \n始終:你始終看不到滑鼠指標。 Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. - 設定滑鼠在閒置後消失的時間。 + 空閒滑鼠指標隱藏逾時:\n閒置滑鼠指標隱藏自身之前的持續顯示時間(秒)。 Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. - 返回按鈕行為:\n設定控制器的返回按鈕模擬在 PS4 觸控板上指定位置的觸碰。 + 返回按鈕行為:\n設定控制器'的返回按鈕以模擬點擊 PS4 控制器觸控板上的指定位置。 Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + 顯示相容性資料:\n在表格顯視模式中顯示遊戲相容性資訊。啟用「啟動"時更新相容性」以取得"最新資訊。 Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + 啟動時更新相容性:\n在 shadPS4 啟動時自動更新相容性資料庫。 Update Compatibility Database:\nImmediately update the compatibility database. - Update Compatibility Database:\nImmediately update the compatibility database. + 更新相容性資料庫:\n立即更新相容性資料庫。 Never @@ -1832,250 +1832,250 @@ Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it. - 圖形設備:\n在多GPU系統中,從下拉列表中選擇模擬器將使用的GPU,\n或選擇「自動選擇」以自動確定。 + 圖形裝置:\n在多GPU系統中,從下拉列表中選取模擬器將使用的GPU,\n或選取「自動選取」以自動選用適合的GPU。 Width/Height:\nSets the size of the emulator window at launch, which can be resized during gameplay.\nThis is different from the in-game resolution. - 寬度/高度:\n設定模擬器啟動時的窗口大小,可以在遊戲過程中調整。\n這與遊戲內解析度不同。 + 寬度/高度:\n設定模擬器啟動時的視窗大小,可以在遊戲過程中調整。\n這與遊戲內部解析度不同。 Vblank Divider:\nThe frame rate at which the emulator refreshes at is multiplied by this number. Changing this may have adverse effects, such as increasing the game speed, or breaking critical game functionality that does not expect this to change! - Vblank分隔符:\n模擬器的幀速率將乘以這個數字。更改此數字可能會有不良影響,例如增加遊戲速度,或破壞不預期此變化的關鍵遊戲功能! + Vblank分隔符:\n模擬器的畫格速率將乘以這個數字。變更此數字可能會有不良影響,例如增加遊戲速度,或可能會不似預期地破壞關鍵的遊戲功能! Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. - 啟用著色器轉儲:\n為了技術調試,將遊戲的著色器在渲染時保存到文件夾中。 + 啟用著色器傾印:\n作為技術偵錯用途,將遊戲的著色器在渲染時儲存到資料夾中。 Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. - 啟用空GPU:\n為了技術調試,禁用遊戲渲染,彷彿沒有顯示卡。 + 啟用空值GPU:\n作為技術偵錯用途,停用遊戲渲染,彷如沒有顯示卡。 Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + 啟用 HDR:\n在支援 HDR 的遊戲中啟用 HDR。 \n您的顯示器必須支援 BT2020 PQ 色彩空間和 RGB10A2 交換鏈格式。 Game Folders:\nThe list of folders to check for installed games. - 遊戲資料夾:\n檢查已安裝遊戲的資料夾列表。 + 遊戲資料夾:\n檢查已安裝遊戲的資料夾列表。 Add:\nAdd a folder to the list. - 添加:\n將資料夾添加到列表。 + 新增:\n將資料夾新增到列表中。 Remove:\nRemove a folder from the list. - 移除:\n從列表中移除資料夾。 + 移除:\n從列表中移除資料夾。 Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. - 啟用調試轉儲:\n將當前運行的PS4程序的輸入和輸出符號及文件頭信息保存到目錄中。 + 啟用偵錯傾印:\n將目前執行的PS4程式的輸入和輸出符號以及檔案頭資訊儲存到目錄中。 Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation. - 啟用Vulkan驗證層:\n啟用一個系統來驗證Vulkan渲染器的狀態並記錄其內部狀態的信息。這將降低性能並可能改變模擬行為。 + 啟用Vulkan驗證層:\n啟用一個系統來驗證Vulkan渲染器的狀態並記錄其內部狀態的資訊。這樣將會降低效能並可能改變模擬行為。 Enable Vulkan Synchronization Validation:\nEnables a system that validates the timing of Vulkan rendering tasks.\nThis will reduce performance and likely change the behavior of emulation. - 啟用Vulkan同步驗證:\n啟用一個系統來驗證Vulkan渲染任務的時間。這將降低性能並可能改變模擬行為。 + 啟用Vulkan同步驗證:\n啟用一個系統來驗證Vulkan渲染工作的時間。\n這樣將會降低效能並可能改變模擬行為。 Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame. - 啟用RenderDoc調試:\n如果啟用,模擬器將提供與Renderdoc的兼容性,以允許捕獲和分析當前渲染的幀。 + 啟用RenderDoc偵錯:\n如果啟用的話,模擬器將會提供與Renderdoc的相容性,以便允許擷取和分析目前渲染的畫格。 Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). - Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + 收集著色器:\n您需要啟用此功能才能使用偵錯功能表 (Ctrl + F10) 編輯著色器。 Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + 當機診斷:\n會建立一個 .yaml 檔案,其中包含有關當機時 Vulkan 狀態的資訊。 \n對於偵錯「'裝置遺失」的'錯誤很有用。如果啟用了此功能,則應啟用主機和來賓偵錯標記。 \n不適用於 Intel GPU。 \n你需要啟用 Vulkan 驗證層和 Vulkan SDK 才能使此功能如常運作。 Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. - Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + 複製 GPU 緩衝區:\n解決涉及 GPU 提交的競爭條件。 \n說不定能幫助解決 PM4 類型 0 的當機。 Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Host 偵錯標記:\n插入模擬器端資訊,例如圍繞 Vulkan 指令的特定 AMDGPU 指令的標記,以及提供資源偵錯名稱。 \n如果啟用此功能,則應啟用當機診斷。 \n對於 RenderDoc 等程式很有用。 Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Guest 偵錯標記:\n插入遊戲本身已新增至指令緩衝區的任何偵錯標記。 \n如果啟用此功能,則應啟用當機診斷。 \n對於 RenderDoc 等程式很有用。 Save Data Path:\nThe folder where game save data will be saved. - Save Data Path:\nThe folder where game save data will be saved. + 遊戲存檔路徑:\n存放遊戲存檔資料的資料夾。 Browse:\nBrowse for a folder to set as the save data path. - Browse:\nBrowse for a folder to set as the save data path. + 瀏覽:\n選取要設定為存放遊戲存檔路徑的資料夾。 Release - Release + 正式版 Nightly - Nightly + 預覽版 Set the volume of the background music. - Set the volume of the background music. + 設定背景音樂的音量。 Enable Motion Controls - Enable Motion Controls + 啟用動態感應控制 Save Data Path - Save Data Path + 遊戲存檔路徑 Browse - Browse + 瀏覽 async - async + 異步 sync - sync + 同步 Auto Select - Auto Select + 自動選取 Directory to install games - Directory to install games + 要安裝遊戲的目錄 Directory to save data - Directory to save data + 遊戲存檔的目錄 Video - Video + 影像 Display Mode - Display Mode + 顯示模式 Windowed - Windowed + 視窗 Fullscreen - Fullscreen + 全螢幕 Fullscreen (Borderless) - Fullscreen (Borderless) + 全螢幕(無邊框) Window Size - Window Size + 視窗大小 W: - W: + 寬: H: - H: + 高: Separate Log Files - Separate Log Files + 單獨的日誌紀錄檔 Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + 單獨的日誌紀錄檔:\n為個別遊戲寫入單獨的日誌記錄檔。 Trophy Notification Position - Trophy Notification Position + 獎盃通知位置 Left - Left + 左側 Right - Right + 右側 Top - Top + 頂部 Bottom - Bottom + 底部 Notification Duration - Notification Duration + 通知顯示持續時間 Portable User Folder - Portable User Folder + 可移植的使用者資料夾 Create Portable User Folder from Common User Folder - Create Portable User Folder from Common User Folder + 從通用使用者資料夾建立可移植的使用者資料夾 Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. - Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + 可移植的使用者資料夾:\n儲存僅套用於目前資料夾中的 shadPS4 版本的 shadPS4 設定和資料。建立可移植的使用者資料夾後重新啟動應用程式以便開始使用它。 Cannot create portable user folder - Cannot create portable user folder + 無法建立可移植的使用者資料夾 %1 already exists - %1 already exists + %1 已存在 Portable user folder created - Portable user folder created + 已建立可移植的使用者資料夾 %1 successfully created. - %1 successfully created. + 已成功建立 %1。 Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + 開啟自訂獎盃影像/聲音資料夾:\n您可以將自訂影像新增至獎盃和音訊。 \n將檔案加入 custom_tropy,名稱如下:\ntropy.wav OR trophy.mp3、bronze.png、gold.png、platinum.png、silver.png\n注意:聲音僅在 QT 版本中有效。 TrophyViewer Trophy Viewer - Trophy Viewer + 獎盃檢視器 Select Game: - Select Game: + 選取遊戲: Progress - Progress + 進度 Show Earned Trophies - Show Earned Trophies + 顯示已獲得的獎盃 Show Not Earned Trophies - Show Not Earned Trophies + 顯示未獲得的獎盃 Show Hidden Trophies - Show Hidden Trophies + 顯示隱藏的獎盃 diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index fcdde7240..e369240c6 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -10,7 +10,6 @@ #include "common/assert.h" #include "common/config.h" #include "common/elf_info.h" -#include "common/version.h" #include "core/debug_state.h" #include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index 4faa99fe8..c6ec65606 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -21,6 +21,15 @@ Id SharedAtomicU32(EmitContext& ctx, Id offset, Id value, return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value); } +Id SharedAtomicU32_IncDec(EmitContext& ctx, Id offset, + Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id)) { + const Id shift_id{ctx.ConstU32(2U)}; + const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; + const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)}; + const auto [scope, semantics]{AtomicArgs(ctx)}; + return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics); +} + Id BufferAtomicU32BoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto emit_func) { if (Sirit::ValidId(buffer_size)) { // Bounds checking enabled, wrap in a conditional branch to make sure that @@ -99,6 +108,18 @@ Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value) { return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicXor); } +Id EmitSharedAtomicISub32(EmitContext& ctx, Id offset, Id value) { + return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicISub); +} + +Id EmitSharedAtomicIIncrement32(EmitContext& ctx, Id offset) { + return SharedAtomicU32_IncDec(ctx, offset, &Sirit::Module::OpAtomicIIncrement); +} + +Id EmitSharedAtomicIDecrement32(EmitContext& ctx, Id offset) { + return SharedAtomicU32_IncDec(ctx, offset, &Sirit::Module::OpAtomicIDecrement); +} + Id EmitBufferAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) { return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicIAdd); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp index a63be87e2..347c4cb0a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp @@ -75,6 +75,28 @@ Id EmitFPMin64(EmitContext& ctx, Id a, Id b) { return ctx.OpFMin(ctx.F64[1], a, b); } +Id EmitFPMinTri32(EmitContext& ctx, Id a, Id b, Id c) { + if (ctx.profile.supports_trinary_minmax) { + return ctx.OpFMin3AMD(ctx.F32[1], a, b, c); + } + return ctx.OpFMin(ctx.F32[1], a, ctx.OpFMin(ctx.F32[1], b, c)); +} + +Id EmitFPMaxTri32(EmitContext& ctx, Id a, Id b, Id c) { + if (ctx.profile.supports_trinary_minmax) { + return ctx.OpFMax3AMD(ctx.F32[1], a, b, c); + } + return ctx.OpFMax(ctx.F32[1], a, ctx.OpFMax(ctx.F32[1], b, c)); +} + +Id EmitFPMedTri32(EmitContext& ctx, Id a, Id b, Id c) { + if (ctx.profile.supports_trinary_minmax) { + return ctx.OpFMid3AMD(ctx.F32[1], a, b, c); + } + const Id mmx{ctx.OpFMin(ctx.F32[1], ctx.OpFMax(ctx.F32[1], a, b), c)}; + return ctx.OpFMax(ctx.F32[1], ctx.OpFMin(ctx.F32[1], a, b), mmx); +} + Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b)); } @@ -245,12 +267,12 @@ Id EmitFPFrexpSig64(EmitContext& ctx, Id value) { Id EmitFPFrexpExp32(EmitContext& ctx, Id value) { const auto frexp = ctx.OpFrexpStruct(ctx.frexp_result_f32, value); - return ctx.OpCompositeExtract(ctx.U32[1], frexp, 1); + return ctx.OpBitcast(ctx.U32[1], ctx.OpCompositeExtract(ctx.S32[1], frexp, 1)); } Id EmitFPFrexpExp64(EmitContext& ctx, Id value) { const auto frexp = ctx.OpFrexpStruct(ctx.frexp_result_f64, value); - return ctx.OpCompositeExtract(ctx.U32[1], frexp, 1); + return ctx.OpBitcast(ctx.U32[1], ctx.OpCompositeExtract(ctx.S32[1], frexp, 1)); } Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index aaa2bb526..9b7528be8 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -130,6 +130,10 @@ Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value); Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value); +Id EmitSharedAtomicIIncrement32(EmitContext& ctx, Id offset); +Id EmitSharedAtomicIDecrement32(EmitContext& ctx, Id offset); +Id EmitSharedAtomicISub32(EmitContext& ctx, Id offset, Id value); + Id EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2); Id EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3); Id EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3, Id e4); @@ -247,6 +251,9 @@ Id EmitFPMax32(EmitContext& ctx, Id a, Id b, bool is_legacy = false); Id EmitFPMax64(EmitContext& ctx, Id a, Id b); Id EmitFPMin32(EmitContext& ctx, Id a, Id b, bool is_legacy = false); Id EmitFPMin64(EmitContext& ctx, Id a, Id b); +Id EmitFPMinTri32(EmitContext& ctx, Id a, Id b, Id c); +Id EmitFPMaxTri32(EmitContext& ctx, Id a, Id b, Id c); +Id EmitFPMedTri32(EmitContext& ctx, Id a, Id b, Id c); Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b); Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b); @@ -335,8 +342,8 @@ Id EmitIAdd64(EmitContext& ctx, Id a, Id b); Id EmitIAddCary32(EmitContext& ctx, Id a, Id b); Id EmitISub32(EmitContext& ctx, Id a, Id b); Id EmitISub64(EmitContext& ctx, Id a, Id b); -Id EmitSMulExt(EmitContext& ctx, Id a, Id b); -Id EmitUMulExt(EmitContext& ctx, Id a, Id b); +Id EmitSMulHi(EmitContext& ctx, Id a, Id b); +Id EmitUMulHi(EmitContext& ctx, Id a, Id b); Id EmitIMul32(EmitContext& ctx, Id a, Id b); Id EmitIMul64(EmitContext& ctx, Id a, Id b); Id EmitSDiv32(EmitContext& ctx, Id a, Id b); @@ -372,6 +379,12 @@ Id EmitSMin32(EmitContext& ctx, Id a, Id b); Id EmitUMin32(EmitContext& ctx, Id a, Id b); Id EmitSMax32(EmitContext& ctx, Id a, Id b); Id EmitUMax32(EmitContext& ctx, Id a, Id b); +Id EmitSMinTri32(EmitContext& ctx, Id a, Id b, Id c); +Id EmitUMinTri32(EmitContext& ctx, Id a, Id b, Id c); +Id EmitSMaxTri32(EmitContext& ctx, Id a, Id b, Id c); +Id EmitUMaxTri32(EmitContext& ctx, Id a, Id b, Id c); +Id EmitSMedTri32(EmitContext& ctx, Id a, Id b, Id c); +Id EmitUMedTri32(EmitContext& ctx, Id a, Id b, Id c); Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max); Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max); Id EmitSLessThan32(EmitContext& ctx, Id lhs, Id rhs); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index 9f8784797..10bfbb2ab 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp @@ -72,12 +72,17 @@ Id EmitISub64(EmitContext& ctx, Id a, Id b) { return ctx.OpISub(ctx.U64, a, b); } -Id EmitSMulExt(EmitContext& ctx, Id a, Id b) { - return ctx.OpSMulExtended(ctx.full_result_i32x2, a, b); +Id EmitSMulHi(EmitContext& ctx, Id a, Id b) { + const auto signed_a{ctx.OpBitcast(ctx.S32[1], a)}; + const auto signed_b{ctx.OpBitcast(ctx.S32[1], b)}; + const auto mul_ext{ctx.OpSMulExtended(ctx.full_result_i32x2, signed_a, signed_b)}; + const auto signed_hi{ctx.OpCompositeExtract(ctx.S32[1], mul_ext, 1)}; + return ctx.OpBitcast(ctx.U32[1], signed_hi); } -Id EmitUMulExt(EmitContext& ctx, Id a, Id b) { - return ctx.OpUMulExtended(ctx.full_result_u32x2, a, b); +Id EmitUMulHi(EmitContext& ctx, Id a, Id b) { + const auto mul_ext{ctx.OpUMulExtended(ctx.full_result_u32x2, a, b)}; + return ctx.OpCompositeExtract(ctx.U32[1], mul_ext, 1); } Id EmitIMul32(EmitContext& ctx, Id a, Id b) { @@ -256,6 +261,50 @@ Id EmitUMax32(EmitContext& ctx, Id a, Id b) { return ctx.OpUMax(ctx.U32[1], a, b); } +Id EmitSMinTri32(EmitContext& ctx, Id a, Id b, Id c) { + if (ctx.profile.supports_trinary_minmax) { + return ctx.OpSMin3AMD(ctx.U32[1], a, b, c); + } + return ctx.OpSMin(ctx.U32[1], a, ctx.OpSMin(ctx.U32[1], b, c)); +} + +Id EmitUMinTri32(EmitContext& ctx, Id a, Id b, Id c) { + if (ctx.profile.supports_trinary_minmax) { + return ctx.OpUMin3AMD(ctx.U32[1], a, b, c); + } + return ctx.OpUMin(ctx.U32[1], a, ctx.OpUMin(ctx.U32[1], b, c)); +} + +Id EmitSMaxTri32(EmitContext& ctx, Id a, Id b, Id c) { + if (ctx.profile.supports_trinary_minmax) { + return ctx.OpSMax3AMD(ctx.U32[1], a, b, c); + } + return ctx.OpSMax(ctx.U32[1], a, ctx.OpSMax(ctx.U32[1], b, c)); +} + +Id EmitUMaxTri32(EmitContext& ctx, Id a, Id b, Id c) { + if (ctx.profile.supports_trinary_minmax) { + return ctx.OpUMax3AMD(ctx.U32[1], a, b, c); + } + return ctx.OpUMax(ctx.U32[1], a, ctx.OpUMax(ctx.U32[1], b, c)); +} + +Id EmitSMedTri32(EmitContext& ctx, Id a, Id b, Id c) { + if (ctx.profile.supports_trinary_minmax) { + return ctx.OpSMid3AMD(ctx.U32[1], a, b, c); + } + const Id mmx{ctx.OpSMin(ctx.U32[1], ctx.OpSMax(ctx.U32[1], a, b), c)}; + return ctx.OpSMax(ctx.U32[1], ctx.OpSMin(ctx.U32[1], a, b), mmx); +} + +Id EmitUMedTri32(EmitContext& ctx, Id a, Id b, Id c) { + if (ctx.profile.supports_trinary_minmax) { + return ctx.OpUMid3AMD(ctx.U32[1], a, b, c); + } + const Id mmx{ctx.OpUMin(ctx.U32[1], ctx.OpUMax(ctx.U32[1], a, b), c)}; + return ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], a, b), mmx); +} + Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) { Id result{}; if (ctx.profile.has_broken_spirv_clamp) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index e20cfeae2..8433251ff 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -153,9 +153,9 @@ void EmitContext::DefineArithmeticTypes() { full_result_i32x2 = Name(TypeStruct(S32[1], S32[1]), "full_result_i32x2"); full_result_u32x2 = Name(TypeStruct(U32[1], U32[1]), "full_result_u32x2"); - frexp_result_f32 = Name(TypeStruct(F32[1], U32[1]), "frexp_result_f32"); + frexp_result_f32 = Name(TypeStruct(F32[1], S32[1]), "frexp_result_f32"); if (info.uses_fp64) { - frexp_result_f64 = Name(TypeStruct(F64[1], U32[1]), "frexp_result_f64"); + frexp_result_f64 = Name(TypeStruct(F64[1], S32[1]), "frexp_result_f64"); } } diff --git a/src/shader_recompiler/frontend/format.cpp b/src/shader_recompiler/frontend/format.cpp index 76b1cc818..52c8c733e 100644 --- a/src/shader_recompiler/frontend/format.cpp +++ b/src/shader_recompiler/frontend/format.cpp @@ -2784,8 +2784,7 @@ constexpr std::array InstructionFormatDS = {{ // 62 = DS_APPEND {InstClass::DsAppendCon, InstCategory::DataShare, 3, 1, ScalarType::Uint32, ScalarType::Uint32}, // 63 = DS_ORDERED_COUNT - {InstClass::GdsOrdCnt, InstCategory::DataShare, 3, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::GdsOrdCnt, InstCategory::DataShare, 3, 1, ScalarType::Uint32, ScalarType::Uint32}, // 64 = DS_ADD_U64 {InstClass::DsAtomicArith64, InstCategory::DataShare, 3, 1, ScalarType::Uint64, ScalarType::Uint64}, @@ -3570,19 +3569,19 @@ constexpr std::array InstructionFormatMIMG = {{ {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, ScalarType::Float32}, // 65 = IMAGE_GATHER4_CL - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, {}, {}, // 68 = IMAGE_GATHER4_L - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 69 = IMAGE_GATHER4_B - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 70 = IMAGE_GATHER4_B_CL - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 71 = IMAGE_GATHER4_LZ {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, ScalarType::Float32}, @@ -3590,19 +3589,19 @@ constexpr std::array InstructionFormatMIMG = {{ {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, ScalarType::Float32}, // 73 = IMAGE_GATHER4_C_CL - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, {}, {}, // 76 = IMAGE_GATHER4_C_L - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 77 = IMAGE_GATHER4_C_B - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 78 = IMAGE_GATHER4_C_B_CL - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 79 = IMAGE_GATHER4_C_LZ {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, ScalarType::Float32}, @@ -3610,19 +3609,19 @@ constexpr std::array InstructionFormatMIMG = {{ {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, ScalarType::Float32}, // 81 = IMAGE_GATHER4_CL_O - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, {}, {}, // 84 = IMAGE_GATHER4_L_O - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 85 = IMAGE_GATHER4_B_O - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 86 = IMAGE_GATHER4_B_CL_O - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 87 = IMAGE_GATHER4_LZ_O {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, ScalarType::Float32}, @@ -3630,19 +3629,19 @@ constexpr std::array InstructionFormatMIMG = {{ {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, ScalarType::Float32}, // 89 = IMAGE_GATHER4_C_CL_O - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, {}, {}, // 92 = IMAGE_GATHER4_C_L_O - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 93 = IMAGE_GATHER4_C_B_O - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 94 = IMAGE_GATHER4_C_B_CL_O - {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Undefined, - ScalarType::Undefined}, + {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, + ScalarType::Float32}, // 95 = IMAGE_GATHER4_C_LZ_O {InstClass::VectorMemImgSmp, InstCategory::VectorMemory, 4, 1, ScalarType::Uint32, ScalarType::Float32}, diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index 22f5b8644..c29497ada 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -13,6 +13,12 @@ void Translator::EmitDataShare(const GcnInst& inst) { // DS case Opcode::DS_ADD_U32: return DS_ADD_U32(inst, false); + case Opcode::DS_SUB_U32: + return DS_SUB_U32(inst, false); + case Opcode::DS_INC_U32: + return DS_INC_U32(inst, false); + case Opcode::DS_DEC_U32: + return DS_DEC_U32(inst, false); case Opcode::DS_MIN_I32: return DS_MIN_U32(inst, true, false); case Opcode::DS_MAX_I32: @@ -35,6 +41,8 @@ void Translator::EmitDataShare(const GcnInst& inst) { return DS_WRITE(32, false, true, true, inst); case Opcode::DS_ADD_RTN_U32: return DS_ADD_U32(inst, true); + case Opcode::DS_SUB_RTN_U32: + return DS_SUB_U32(inst, true); case Opcode::DS_MIN_RTN_U32: return DS_MIN_U32(inst, false, true); case Opcode::DS_MAX_RTN_U32: @@ -67,6 +75,8 @@ void Translator::EmitDataShare(const GcnInst& inst) { return DS_READ(64, false, false, false, inst); case Opcode::DS_READ2_B64: return DS_READ(64, false, true, false, inst); + case Opcode::DS_READ2ST64_B64: + return DS_READ(64, false, true, true, inst); default: LogMissingOpcode(inst); } @@ -226,6 +236,40 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) { SetDst(inst.dst[0], ir.QuadShuffle(src, index)); } +void Translator::DS_INC_U32(const GcnInst& inst, bool rtn) { + const IR::U32 addr{GetSrc(inst.src[0])}; + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); + const IR::U32 addr_offset = ir.IAdd(addr, offset); + const IR::Value original_val = ir.SharedAtomicIIncrement(addr_offset); + if (rtn) { + SetDst(inst.dst[0], IR::U32{original_val}); + } +} + +void Translator::DS_DEC_U32(const GcnInst& inst, bool rtn) { + const IR::U32 addr{GetSrc(inst.src[0])}; + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); + const IR::U32 addr_offset = ir.IAdd(addr, offset); + const IR::Value original_val = ir.SharedAtomicIDecrement(addr_offset); + if (rtn) { + SetDst(inst.dst[0], IR::U32{original_val}); + } +} + +void Translator::DS_SUB_U32(const GcnInst& inst, bool rtn) { + const IR::U32 addr{GetSrc(inst.src[0])}; + const IR::U32 data{GetSrc(inst.src[1])}; + const IR::U32 offset = + ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)); + const IR::U32 addr_offset = ir.IAdd(addr, offset); + const IR::Value original_val = ir.SharedAtomicISub(addr_offset, data); + if (rtn) { + SetDst(inst.dst[0], IR::U32{original_val}); + } +} + void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst) { const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))}; diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index 39f972848..3a8e894ae 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -30,6 +30,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_SUB_I32(inst); case Opcode::S_ADDC_U32: return S_ADDC_U32(inst); + case Opcode::S_SUBB_U32: + return S_SUBB_U32(inst); case Opcode::S_MIN_I32: return S_MIN_U32(true, inst); case Opcode::S_MIN_U32: @@ -110,6 +112,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) { return S_FF1_I32_B32(inst); case Opcode::S_FF1_I32_B64: return S_FF1_I32_B64(inst); + case Opcode::S_FLBIT_I32_B32: + return S_FLBIT_I32_B32(inst); case Opcode::S_BITSET0_B32: return S_BITSET_B32(inst, 0); case Opcode::S_BITSET1_B32: @@ -236,6 +240,17 @@ void Translator::S_SUB_U32(const GcnInst& inst) { ir.SetScc(ir.IGreaterThan(src1, src0, false)); } +void Translator::S_SUBB_U32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 borrow{ir.Select(ir.GetScc(), ir.Imm32(1U), ir.Imm32(0U))}; + const IR::U32 result{ir.ISub(ir.ISub(src0, src1), borrow)}; + SetDst(inst.dst[0], result); + + const IR::U32 sum_with_borrow{ir.IAdd(src1, borrow)}; + ir.SetScc(ir.ILessThan(src0, sum_with_borrow, false)); +} + void Translator::S_ADD_I32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; @@ -660,6 +675,17 @@ void Translator::S_FF1_I32_B64(const GcnInst& inst) { SetDst(inst.dst[0], result); } +void Translator::S_FLBIT_I32_B32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + // Gcn wants the MSB position counting from the left, but SPIR-V counts from the rightmost (LSB) + // position + const IR::U32 msb_pos = ir.FindUMsb(src0); + const IR::U32 pos_from_left = ir.ISub(ir.Imm32(31), msb_pos); + // Select 0xFFFFFFFF if src0 was 0 + const IR::U1 cond = ir.INotEqual(src0, ir.Imm32(0)); + SetDst(inst.dst[0], IR::U32{ir.Select(cond, pos_from_left, ir.Imm32(~0U))}); +} + void Translator::S_BITSET_B32(const GcnInst& inst, u32 bit_value) { const IR::U32 old_value{GetSrc(inst.dst[0])}; const IR::U32 offset{ir.BitFieldExtract(GetSrc(inst.src[0]), ir.Imm32(0U), ir.Imm32(5U))}; diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 2fd48a051..68d5e8dc8 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -80,6 +80,7 @@ public: // SOP2 void S_ADD_U32(const GcnInst& inst); void S_SUB_U32(const GcnInst& inst); + void S_SUBB_U32(const GcnInst& inst); void S_ADD_I32(const GcnInst& inst); void S_SUB_I32(const GcnInst& inst); void S_ADDC_U32(const GcnInst& inst); @@ -119,6 +120,7 @@ public: void S_BCNT1_I32_B64(const GcnInst& inst); void S_FF1_I32_B32(const GcnInst& inst); void S_FF1_I32_B64(const GcnInst& inst); + void S_FLBIT_I32_B32(const GcnInst& inst); void S_BITSET_B32(const GcnInst& inst, u32 bit_value); void S_GETPC_B64(u32 pc, const GcnInst& inst); void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst); @@ -238,13 +240,11 @@ public: void V_FMA_F32(const GcnInst& inst); void V_FMA_F64(const GcnInst& inst); void V_MIN3_F32(const GcnInst& inst); - void V_MIN3_I32(const GcnInst& inst); - void V_MIN3_U32(const GcnInst& inst); + void V_MIN3_U32(bool is_signed, const GcnInst& inst); void V_MAX3_F32(const GcnInst& inst); void V_MAX3_U32(bool is_signed, const GcnInst& inst); void V_MED3_F32(const GcnInst& inst); - void V_MED3_I32(const GcnInst& inst); - void V_MED3_U32(const GcnInst& inst); + void V_MED3_U32(bool is_signed, const GcnInst& inst); void V_SAD(const GcnInst& inst); void V_SAD_U32(const GcnInst& inst); void V_CVT_PK_U16_U32(const GcnInst& inst); @@ -275,6 +275,9 @@ public: void DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst); void DS_APPEND(const GcnInst& inst); void DS_CONSUME(const GcnInst& inst); + void DS_SUB_U32(const GcnInst& inst, bool rtn); + void DS_INC_U32(const GcnInst& inst, bool rtn); + void DS_DEC_U32(const GcnInst& inst, bool rtn); // Buffer Memory // MUBUF / MTBUF diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 56e903052..787cf6ad3 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -359,9 +359,9 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { case Opcode::V_MIN3_F32: return V_MIN3_F32(inst); case Opcode::V_MIN3_I32: - return V_MIN3_I32(inst); + return V_MIN3_U32(true, inst); case Opcode::V_MIN3_U32: - return V_MIN3_U32(inst); + return V_MIN3_U32(false, inst); case Opcode::V_MAX3_F32: return V_MAX3_F32(inst); case Opcode::V_MAX3_I32: @@ -371,9 +371,9 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { case Opcode::V_MED3_F32: return V_MED3_F32(inst); case Opcode::V_MED3_I32: - return V_MED3_I32(inst); + return V_MED3_U32(true, inst); case Opcode::V_MED3_U32: - return V_MED3_U32(inst); + return V_MED3_U32(false, inst); case Opcode::V_SAD_U32: return V_SAD_U32(inst); case Opcode::V_CVT_PK_U16_U32: @@ -394,6 +394,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_MUL_HI_U32(false, inst); case Opcode::V_MUL_LO_I32: return V_MUL_LO_U32(inst); + case Opcode::V_MUL_HI_I32: + return V_MUL_HI_U32(true, inst); case Opcode::V_MAD_U64_U32: return V_MAD_U64_U32(inst); case Opcode::V_NOP: @@ -1010,8 +1012,10 @@ void Translator::V_CMP_CLASS_F32(const GcnInst& inst) { value = ir.FPIsNan(src0); } else if ((class_mask & IR::FloatClassFunc::Infinity) == IR::FloatClassFunc::Infinity) { value = ir.FPIsInf(src0); + } else if ((class_mask & IR::FloatClassFunc::Negative) == IR::FloatClassFunc::Negative) { + value = ir.FPLessThanEqual(src0, ir.Imm32(-0.f)); } else { - UNREACHABLE(); + UNREACHABLE_MSG("Unsupported float class mask: {:#x}", static_cast(class_mask)); } } else { // We don't know the type yet, delay its resolution. @@ -1166,59 +1170,42 @@ void Translator::V_MIN3_F32(const GcnInst& inst) { const IR::F32 src0{GetSrc(inst.src[0])}; const IR::F32 src1{GetSrc(inst.src[1])}; const IR::F32 src2{GetSrc(inst.src[2])}; - SetDst(inst.dst[0], ir.FPMin(src0, ir.FPMin(src1, src2))); + SetDst(inst.dst[0], ir.FPMinTri(src0, src1, src2)); } -void Translator::V_MIN3_I32(const GcnInst& inst) { +void Translator::V_MIN3_U32(bool is_signed, const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; const IR::U32 src2{GetSrc(inst.src[2])}; - SetDst(inst.dst[0], ir.SMin(src0, ir.SMin(src1, src2))); -} - -void Translator::V_MIN3_U32(const GcnInst& inst) { - const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{GetSrc(inst.src[1])}; - const IR::U32 src2{GetSrc(inst.src[2])}; - SetDst(inst.dst[0], ir.UMin(src0, ir.UMin(src1, src2))); + SetDst(inst.dst[0], ir.IMinTri(src0, src1, src2, is_signed)); } void Translator::V_MAX3_F32(const GcnInst& inst) { const IR::F32 src0{GetSrc(inst.src[0])}; const IR::F32 src1{GetSrc(inst.src[1])}; const IR::F32 src2{GetSrc(inst.src[2])}; - SetDst(inst.dst[0], ir.FPMax(src0, ir.FPMax(src1, src2))); + SetDst(inst.dst[0], ir.FPMaxTri(src0, src1, src2)); } void Translator::V_MAX3_U32(bool is_signed, const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; const IR::U32 src2{GetSrc(inst.src[2])}; - SetDst(inst.dst[0], ir.IMax(src0, ir.IMax(src1, src2, is_signed), is_signed)); + SetDst(inst.dst[0], ir.IMaxTri(src0, src1, src2, is_signed)); } void Translator::V_MED3_F32(const GcnInst& inst) { const IR::F32 src0{GetSrc(inst.src[0])}; const IR::F32 src1{GetSrc(inst.src[1])}; const IR::F32 src2{GetSrc(inst.src[2])}; - const IR::F32 mmx = ir.FPMin(ir.FPMax(src0, src1), src2); - SetDst(inst.dst[0], ir.FPMax(ir.FPMin(src0, src1), mmx)); + SetDst(inst.dst[0], ir.FPMedTri(src0, src1, src2)); } -void Translator::V_MED3_I32(const GcnInst& inst) { +void Translator::V_MED3_U32(bool is_signed, const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; const IR::U32 src2{GetSrc(inst.src[2])}; - const IR::U32 mmx = ir.SMin(ir.SMax(src0, src1), src2); - SetDst(inst.dst[0], ir.SMax(ir.SMin(src0, src1), mmx)); -} - -void Translator::V_MED3_U32(const GcnInst& inst) { - const IR::U32 src0{GetSrc(inst.src[0])}; - const IR::U32 src1{GetSrc(inst.src[1])}; - const IR::U32 src2{GetSrc(inst.src[2])}; - const IR::U32 mmx = ir.UMin(ir.UMax(src0, src1), src2); - SetDst(inst.dst[0], ir.UMax(ir.UMin(src0, src1), mmx)); + SetDst(inst.dst[0], ir.IMedTri(src0, src1, src2, is_signed)); } void Translator::V_SAD(const GcnInst& inst) { @@ -1294,7 +1281,7 @@ void Translator::V_MUL_LO_U32(const GcnInst& inst) { void Translator::V_MUL_HI_U32(bool is_signed, const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; - const IR::U32 hi{ir.CompositeExtract(ir.IMulExt(src0, src1, is_signed), 1)}; + const IR::U32 hi{ir.IMulHi(src0, src1, is_signed)}; SetDst(inst.dst[0], hi); } diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 3615e8cbb..e8836bb4c 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -357,6 +357,18 @@ U32 IREmitter::SharedAtomicXor(const U32& address, const U32& data) { return Inst(Opcode::SharedAtomicXor32, address, data); } +U32 IREmitter::SharedAtomicIIncrement(const U32& address) { + return Inst(Opcode::SharedAtomicIIncrement32, address); +} + +U32 IREmitter::SharedAtomicIDecrement(const U32& address) { + return Inst(Opcode::SharedAtomicIDecrement32, address); +} + +U32 IREmitter::SharedAtomicISub(const U32& address, const U32& data) { + return Inst(Opcode::SharedAtomicISub32, address, data); +} + U32 IREmitter::ReadConst(const Value& base, const U32& offset) { return Inst(Opcode::ReadConst, base, offset); } @@ -1336,6 +1348,18 @@ F32F64 IREmitter::FPMin(const F32F64& lhs, const F32F64& rhs, bool is_legacy) { } } +F32F64 IREmitter::FPMinTri(const F32F64& a, const F32F64& b, const F32F64& c) { + return Inst(Opcode::FPMinTri32, a, b, c); +} + +F32F64 IREmitter::FPMaxTri(const F32F64& a, const F32F64& b, const F32F64& c) { + return Inst(Opcode::FPMaxTri32, a, b, c); +} + +F32F64 IREmitter::FPMedTri(const F32F64& a, const F32F64& b, const F32F64& c) { + return Inst(Opcode::FPMedTri32, a, b, c); +} + U32U64 IREmitter::IAdd(const U32U64& a, const U32U64& b) { if (a.Type() != b.Type()) { UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type()); @@ -1376,8 +1400,8 @@ U32U64 IREmitter::ISub(const U32U64& a, const U32U64& b) { } } -IR::Value IREmitter::IMulExt(const U32& a, const U32& b, bool is_signed) { - return Inst(is_signed ? Opcode::SMulExt : Opcode::UMulExt, a, b); +U32 IREmitter::IMulHi(const U32& a, const U32& b, bool is_signed) { + return Inst(is_signed ? Opcode::SMulHi : Opcode::UMulHi, a, b); } U32U64 IREmitter::IMul(const U32U64& a, const U32U64& b) { @@ -1567,6 +1591,42 @@ U32 IREmitter::IMax(const U32& a, const U32& b, bool is_signed) { return is_signed ? SMax(a, b) : UMax(a, b); } +U32 IREmitter::SMinTri(const U32& a, const U32& b, const U32& c) { + return Inst(Opcode::SMinTri32, a, b, c); +} + +U32 IREmitter::UMinTri(const U32& a, const U32& b, const U32& c) { + return Inst(Opcode::UMinTri32, a, b, c); +} + +U32 IREmitter::IMinTri(const U32& a, const U32& b, const U32& c, bool is_signed) { + return is_signed ? SMinTri(a, b, c) : UMinTri(a, b, c); +} + +U32 IREmitter::SMaxTri(const U32& a, const U32& b, const U32& c) { + return Inst(Opcode::SMaxTri32, a, b, c); +} + +U32 IREmitter::UMaxTri(const U32& a, const U32& b, const U32& c) { + return Inst(Opcode::UMaxTri32, a, b, c); +} + +U32 IREmitter::IMaxTri(const U32& a, const U32& b, const U32& c, bool is_signed) { + return is_signed ? SMaxTri(a, b, c) : UMaxTri(a, b, c); +} + +U32 IREmitter::SMedTri(const U32& a, const U32& b, const U32& c) { + return Inst(Opcode::SMedTri32, a, b, c); +} + +U32 IREmitter::UMedTri(const U32& a, const U32& b, const U32& c) { + return Inst(Opcode::UMedTri32, a, b, c); +} + +U32 IREmitter::IMedTri(const U32& a, const U32& b, const U32& c, bool is_signed) { + return is_signed ? SMedTri(a, b, c) : UMedTri(a, b, c); +} + U32 IREmitter::SClamp(const U32& value, const U32& min, const U32& max) { return Inst(Opcode::SClamp32, value, min, max); } diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 7ac75bf70..186d83a07 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -106,6 +106,10 @@ public: [[nodiscard]] U32 SharedAtomicOr(const U32& address, const U32& data); [[nodiscard]] U32 SharedAtomicXor(const U32& address, const U32& data); + [[nodiscard]] U32 SharedAtomicIIncrement(const U32& address); + [[nodiscard]] U32 SharedAtomicIDecrement(const U32& address); + [[nodiscard]] U32 SharedAtomicISub(const U32& address, const U32& data); + [[nodiscard]] U32 ReadConst(const Value& base, const U32& offset); [[nodiscard]] U32 ReadConstBuffer(const Value& handle, const U32& index); @@ -233,11 +237,14 @@ public: [[nodiscard]] U1 FPUnordered(const F32F64& lhs, const F32F64& rhs); [[nodiscard]] F32F64 FPMax(const F32F64& lhs, const F32F64& rhs, bool is_legacy = false); [[nodiscard]] F32F64 FPMin(const F32F64& lhs, const F32F64& rhs, bool is_legacy = false); + [[nodiscard]] F32F64 FPMinTri(const F32F64& a, const F32F64& b, const F32F64& c); + [[nodiscard]] F32F64 FPMaxTri(const F32F64& a, const F32F64& b, const F32F64& c); + [[nodiscard]] F32F64 FPMedTri(const F32F64& a, const F32F64& b, const F32F64& c); [[nodiscard]] U32U64 IAdd(const U32U64& a, const U32U64& b); [[nodiscard]] Value IAddCary(const U32& a, const U32& b); [[nodiscard]] U32U64 ISub(const U32U64& a, const U32U64& b); - [[nodiscard]] Value IMulExt(const U32& a, const U32& b, bool is_signed = false); + [[nodiscard]] U32 IMulHi(const U32& a, const U32& b, bool is_signed = false); [[nodiscard]] U32U64 IMul(const U32U64& a, const U32U64& b); [[nodiscard]] U32 IDiv(const U32& a, const U32& b, bool is_signed = false); [[nodiscard]] U32 IMod(const U32& a, const U32& b, bool is_signed = false); @@ -266,6 +273,15 @@ public: [[nodiscard]] U32 SMax(const U32& a, const U32& b); [[nodiscard]] U32 UMax(const U32& a, const U32& b); [[nodiscard]] U32 IMax(const U32& a, const U32& b, bool is_signed); + [[nodiscard]] U32 SMinTri(const U32& a, const U32& b, const U32& c); + [[nodiscard]] U32 UMinTri(const U32& a, const U32& b, const U32& c); + [[nodiscard]] U32 IMinTri(const U32& a, const U32& b, const U32& c, bool is_signed); + [[nodiscard]] U32 SMaxTri(const U32& a, const U32& b, const U32& c); + [[nodiscard]] U32 UMaxTri(const U32& a, const U32& b, const U32& c); + [[nodiscard]] U32 IMaxTri(const U32& a, const U32& b, const U32& c, bool is_signed); + [[nodiscard]] U32 SMedTri(const U32& a, const U32& b, const U32& c); + [[nodiscard]] U32 UMedTri(const U32& a, const U32& b, const U32& c); + [[nodiscard]] U32 IMedTri(const U32& a, const U32& b, const U32& c, bool is_signed); [[nodiscard]] U32 SClamp(const U32& value, const U32& min, const U32& max); [[nodiscard]] U32 UClamp(const U32& value, const U32& min, const U32& max); diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index d5e17631b..4932ff9a0 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -44,6 +44,9 @@ OPCODE(SharedAtomicUMax32, U32, U32, OPCODE(SharedAtomicAnd32, U32, U32, U32, ) OPCODE(SharedAtomicOr32, U32, U32, U32, ) OPCODE(SharedAtomicXor32, U32, U32, U32, ) +OPCODE(SharedAtomicISub32, U32, U32, U32, ) +OPCODE(SharedAtomicIIncrement32, U32, U32, ) +OPCODE(SharedAtomicIDecrement32, U32, U32, ) // Context getters/setters OPCODE(GetUserData, U32, ScalarReg, ) @@ -241,6 +244,9 @@ OPCODE(FPMax32, F32, F32, OPCODE(FPMax64, F64, F64, F64, ) OPCODE(FPMin32, F32, F32, F32, U1, ) OPCODE(FPMin64, F64, F64, F64, ) +OPCODE(FPMinTri32, F32, F32, F32, F32, ) +OPCODE(FPMaxTri32, F32, F32, F32, F32, ) +OPCODE(FPMedTri32, F32, F32, F32, F32, ) OPCODE(FPMul32, F32, F32, F32, ) OPCODE(FPMul64, F64, F64, F64, ) OPCODE(FPDiv32, F32, F32, F32, ) @@ -314,8 +320,8 @@ OPCODE(ISub32, U32, U32, OPCODE(ISub64, U64, U64, U64, ) OPCODE(IMul32, U32, U32, U32, ) OPCODE(IMul64, U64, U64, U64, ) -OPCODE(SMulExt, U32x2, U32, U32, ) -OPCODE(UMulExt, U32x2, U32, U32, ) +OPCODE(SMulHi, U32, U32, U32, ) +OPCODE(UMulHi, U32, U32, U32, ) OPCODE(SDiv32, U32, U32, U32, ) OPCODE(UDiv32, U32, U32, U32, ) OPCODE(SMod32, U32, U32, U32, ) @@ -350,6 +356,12 @@ OPCODE(SMin32, U32, U32, OPCODE(UMin32, U32, U32, U32, ) OPCODE(SMax32, U32, U32, U32, ) OPCODE(UMax32, U32, U32, U32, ) +OPCODE(SMinTri32, U32, U32, U32, U32, ) +OPCODE(UMinTri32, U32, U32, U32, U32, ) +OPCODE(SMaxTri32, U32, U32, U32, U32, ) +OPCODE(UMaxTri32, U32, U32, U32, U32, ) +OPCODE(SMedTri32, U32, U32, U32, U32, ) +OPCODE(UMedTri32, U32, U32, U32, U32, ) OPCODE(SClamp32, U32, U32, U32, U32, ) OPCODE(UClamp32, U32, U32, U32, U32, ) OPCODE(SLessThan32, U1, U32, U32, ) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index c5bfe5796..778da149f 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -235,9 +235,12 @@ std::pair TryDisableAnisoLod0(const IR::Inst* inst) { return {prod2, true}; } -SharpLocation TrackSharp(const IR::Inst* inst, const Shader::Info& info) { +SharpLocation AttemptTrackSharp(const IR::Inst* inst, auto& visited_insts) { // Search until we find a potential sharp source. - const auto pred = [](const IR::Inst* inst) -> std::optional { + const auto pred = [&visited_insts](const IR::Inst* inst) -> std::optional { + if (std::ranges::find(visited_insts, inst) != visited_insts.end()) { + return std::nullopt; + } if (inst->GetOpcode() == IR::Opcode::GetUserData || inst->GetOpcode() == IR::Opcode::ReadConst) { return inst; @@ -247,6 +250,7 @@ SharpLocation TrackSharp(const IR::Inst* inst, const Shader::Info& info) { const auto result = IR::BreadthFirstSearch(inst, pred); ASSERT_MSG(result, "Unable to track sharp source"); inst = result.value(); + visited_insts.emplace_back(inst); if (inst->GetOpcode() == IR::Opcode::GetUserData) { return static_cast(inst->Arg(0).ScalarReg()); } else { @@ -256,6 +260,29 @@ SharpLocation TrackSharp(const IR::Inst* inst, const Shader::Info& info) { } } +/// Tracks a sharp with validation of the chosen data type. +template +std::pair TrackSharp(const IR::Inst* inst, const Info& info) { + boost::container::small_vector visited_insts{}; + while (true) { + const auto prev_size = visited_insts.size(); + const auto sharp = AttemptTrackSharp(inst, visited_insts); + if (const auto data = info.ReadUdSharp(sharp); data.Valid()) { + return std::make_pair(sharp, data); + } + if (prev_size == visited_insts.size()) { + // No change in visited instructions, we've run out of paths. + UNREACHABLE_MSG("Unable to find valid sharp."); + } + } +} + +/// Tracks a sharp without data validation. +SharpLocation TrackSharp(const IR::Inst* inst, const Info& info) { + boost::container::static_vector visited_insts{}; + return AttemptTrackSharp(inst, visited_insts); +} + s32 TryHandleInlineCbuf(IR::Inst& inst, Info& info, Descriptors& descriptors, AmdGpu::Buffer& cbuf) { @@ -293,8 +320,8 @@ void PatchBufferSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& if (binding = TryHandleInlineCbuf(inst, info, descriptors, buffer); binding == -1) { IR::Inst* handle = inst.Arg(0).InstRecursive(); IR::Inst* producer = handle->Arg(0).InstRecursive(); - const auto sharp = TrackSharp(producer, info); - buffer = info.ReadUdSharp(sharp); + SharpLocation sharp; + std::tie(sharp, buffer) = TrackSharp(producer, info); binding = descriptors.Add(BufferResource{ .sharp_idx = sharp, .used_types = BufferDataType(inst, buffer.GetNumberFmt()), diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index 40c4b61c3..622190cf0 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -25,6 +25,7 @@ enum class FloatClassFunc : u32 { NaN = SignalingNan | QuietNan, Infinity = PositiveInfinity | NegativeInfinity, + Negative = NegativeInfinity | NegativeNormal | NegativeDenorm | NegativeZero, Finite = NegativeNormal | NegativeDenorm | NegativeZero | PositiveNormal | PositiveDenorm | PositiveZero, }; diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 1ceaea664..3b2854d59 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -26,6 +26,7 @@ struct Profile { bool support_legacy_vertex_attributes{}; bool supports_image_load_store_lod{}; bool supports_native_cube_calc{}; + bool supports_trinary_minmax{}; bool supports_robust_buffer_access{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; diff --git a/src/shadps4.qrc b/src/shadps4.qrc index 83dea01c4..2aee394c8 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -1,6 +1,7 @@ images/shadps4.ico + images/shadps4.png images/about_icon.png images/dump_icon.png images/play_icon.png diff --git a/src/shadps4.rc b/src/shadps4.rc index 8c984f260..9edccfbfd 100644 --- a/src/shadps4.rc +++ b/src/shadps4.rc @@ -1 +1,43 @@ -IDI_ICON1 ICON "images/shadps4.ico" \ No newline at end of file +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "winresrc.h" + +#define xstr(s) str(s) +#define str(s) #s + +// NEUTRAL LANG +LANGUAGE LANG_NEUTRAL, 0x8 + +// ICON +IDI_ICON1 ICON "images/shadps4.ico" + +// VERSION +VS_VERSION_INFO VERSIONINFO + FILEVERSION EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, 0 + PRODUCTVERSION EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, 0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "shadPS4 - PlayStation 4 Emulator\0" + VALUE "FileVersion", xstr(EMULATOR_VERSION_MAJOR) "." xstr(EMULATOR_VERSION_MINOR) "." xstr(EMULATOR_VERSION_PATCH) ".0\0" + VALUE "InternalName", "shadPS4\0" + VALUE "LegalCopyright", "Copyright 2025 shadPS4 Team\0" + VALUE "OriginalFilename", "shadPS4.exe\0" + VALUE "ProductName", "shadPS4\0" + VALUE "ProductVersion", xstr(EMULATOR_VERSION_MAJOR) "." xstr(EMULATOR_VERSION_MINOR) "." xstr(EMULATOR_VERSION_PATCH) ".0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 474c04ec2..8f9292f1c 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -1423,6 +1423,10 @@ struct Liverpool { return num_samples; } + bool IsClipDisabled() const { + return clipper_control.clip_disable || primitive_type == PrimitiveType::RectList; + } + void SetDefaults(); }; diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 843bedb20..a6ae0c304 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -156,6 +156,18 @@ vk::CullModeFlags CullMode(Liverpool::CullMode mode) { } } +vk::FrontFace FrontFace(Liverpool::FrontFace face) { + switch (face) { + case Liverpool::FrontFace::Clockwise: + return vk::FrontFace::eClockwise; + case Liverpool::FrontFace::CounterClockwise: + return vk::FrontFace::eCounterClockwise; + default: + UNREACHABLE(); + return vk::FrontFace::eClockwise; + } +} + vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor) { using BlendFactor = Liverpool::BlendControl::BlendFactor; switch (factor) { diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index 42da7aa06..fca0a8378 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -26,6 +26,8 @@ vk::PolygonMode PolygonMode(Liverpool::PolygonMode mode); vk::CullModeFlags CullMode(Liverpool::CullMode mode); +vk::FrontFace FrontFace(Liverpool::FrontFace mode); + vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor); vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index c528258fb..354e22331 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -28,6 +28,15 @@ static constexpr std::array LogicalStageToStageBit = { vk::ShaderStageFlagBits::eCompute, }; +static bool IsPrimitiveTopologyList(const vk::PrimitiveTopology topology) { + return topology == vk::PrimitiveTopology::ePointList || + topology == vk::PrimitiveTopology::eLineList || + topology == vk::PrimitiveTopology::eTriangleList || + topology == vk::PrimitiveTopology::eLineListWithAdjacency || + topology == vk::PrimitiveTopology::eTriangleListWithAdjacency || + topology == vk::PrimitiveTopology::ePatchList; +} + GraphicsPipeline::GraphicsPipeline( const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap, const Shader::Profile& profile, const GraphicsPipelineKey& key_, @@ -75,19 +84,15 @@ GraphicsPipeline::GraphicsPipeline( .pVertexAttributeDescriptions = vertex_attributes.data(), }; - auto prim_restart = key.enable_primitive_restart != 0; - if (prim_restart && IsPrimitiveListTopology() && !instance.IsListRestartSupported()) { - LOG_DEBUG(Render_Vulkan, - "Primitive restart is enabled for list topology but not supported by driver."); - prim_restart = false; - } + const auto topology = LiverpoolToVK::PrimitiveType(key.prim_type); const vk::PipelineInputAssemblyStateCreateInfo input_assembly = { - .topology = LiverpoolToVK::PrimitiveType(key.prim_type), - .primitiveRestartEnable = prim_restart, + .topology = topology, + // Avoid warning spam on all pipelines about unsupported restart disable, if not supported. + // However, must be false for list topologies to avoid validation errors. + .primitiveRestartEnable = + !instance.IsPrimitiveRestartDisableSupported() && !IsPrimitiveTopologyList(topology), }; - ASSERT_MSG(!prim_restart || key.primitive_restart_index == 0xFFFF || - key.primitive_restart_index == 0xFFFFFFFF, - "Primitive restart index other than -1 is not supported yet"); + const bool is_rect_list = key.prim_type == AmdGpu::PrimitiveType::RectList; const bool is_quad_list = key.prim_type == AmdGpu::PrimitiveType::QuadList; const auto& fs_info = runtime_infos[u32(Shader::LogicalStage::Fragment)].fs_info; @@ -99,13 +104,6 @@ GraphicsPipeline::GraphicsPipeline( .depthClampEnable = false, .rasterizerDiscardEnable = false, .polygonMode = LiverpoolToVK::PolygonMode(key.polygon_mode), - .cullMode = LiverpoolToVK::IsPrimitiveCulled(key.prim_type) - ? LiverpoolToVK::CullMode(key.cull_mode) - : vk::CullModeFlagBits::eNone, - .frontFace = key.front_face == Liverpool::FrontFace::Clockwise - ? vk::FrontFace::eClockwise - : vk::FrontFace::eCounterClockwise, - .depthBiasEnable = key.depth_bias_enable, .lineWidth = 1.0f, }; @@ -123,18 +121,24 @@ GraphicsPipeline::GraphicsPipeline( .pNext = instance.IsDepthClipControlSupported() ? &clip_control : nullptr, }; - boost::container::static_vector dynamic_states = { - vk::DynamicState::eViewportWithCountEXT, - vk::DynamicState::eScissorWithCountEXT, - vk::DynamicState::eBlendConstants, - vk::DynamicState::eDepthBounds, - vk::DynamicState::eDepthBias, - vk::DynamicState::eStencilReference, - vk::DynamicState::eStencilCompareMask, - vk::DynamicState::eStencilWriteMask, - vk::DynamicState::eStencilOpEXT, + boost::container::static_vector dynamic_states = { + vk::DynamicState::eViewportWithCountEXT, vk::DynamicState::eScissorWithCountEXT, + vk::DynamicState::eBlendConstants, vk::DynamicState::eDepthTestEnableEXT, + vk::DynamicState::eDepthWriteEnableEXT, vk::DynamicState::eDepthCompareOpEXT, + vk::DynamicState::eDepthBiasEnableEXT, vk::DynamicState::eDepthBias, + vk::DynamicState::eStencilTestEnableEXT, vk::DynamicState::eStencilReference, + vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask, + vk::DynamicState::eStencilOpEXT, vk::DynamicState::eCullModeEXT, + vk::DynamicState::eFrontFaceEXT, }; + if (instance.IsPrimitiveRestartDisableSupported()) { + dynamic_states.push_back(vk::DynamicState::ePrimitiveRestartEnableEXT); + } + if (instance.IsDepthBoundsSupported()) { + dynamic_states.push_back(vk::DynamicState::eDepthBoundsTestEnableEXT); + dynamic_states.push_back(vk::DynamicState::eDepthBounds); + } if (instance.IsDynamicColorWriteMaskSupported()) { dynamic_states.push_back(vk::DynamicState::eColorWriteMaskEXT); } @@ -149,14 +153,6 @@ GraphicsPipeline::GraphicsPipeline( .pDynamicStates = dynamic_states.data(), }; - const vk::PipelineDepthStencilStateCreateInfo depth_info = { - .depthTestEnable = key.depth_test_enable, - .depthWriteEnable = key.depth_write_enable, - .depthCompareOp = key.depth_compare_op, - .depthBoundsTestEnable = key.depth_bounds_test_enable, - .stencilTestEnable = key.stencil_test_enable, - }; - boost::container::static_vector shader_stages; auto stage = u32(Shader::LogicalStage::Vertex); @@ -292,7 +288,6 @@ GraphicsPipeline::GraphicsPipeline( .pViewportState = &viewport_info, .pRasterizationState = &raster_state, .pMultisampleState = &multisampling, - .pDepthStencilState = &depth_info, .pColorBlendState = &color_blending, .pDynamicState = &dynamic_info, .layout = *pipeline_layout, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index e6596db2f..59230ae46 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -39,26 +39,10 @@ struct GraphicsPipelineKey { vk::Format depth_format; vk::Format stencil_format; - struct { - bool clip_disable : 1; - bool depth_test_enable : 1; - bool depth_write_enable : 1; - bool depth_bounds_test_enable : 1; - bool depth_bias_enable : 1; - bool stencil_test_enable : 1; - // Must be named to be zero-initialized. - u8 _unused : 2; - }; - vk::CompareOp depth_compare_op; - u32 num_samples; u32 mrt_mask; AmdGpu::PrimitiveType prim_type; - u32 enable_primitive_restart; - u32 primitive_restart_index; Liverpool::PolygonMode polygon_mode; - Liverpool::CullMode cull_mode; - Liverpool::FrontFace front_face; Liverpool::ClipSpace clip_space; Liverpool::ColorBufferMask cb_shader_mask; std::array blend_controls; @@ -94,20 +78,6 @@ public: return key.mrt_mask; } - auto IsClipDisabled() const { - return key.clip_disable; - } - - [[nodiscard]] bool IsPrimitiveListTopology() const { - return key.prim_type == AmdGpu::PrimitiveType::PointList || - key.prim_type == AmdGpu::PrimitiveType::LineList || - key.prim_type == AmdGpu::PrimitiveType::TriangleList || - key.prim_type == AmdGpu::PrimitiveType::AdjLineList || - key.prim_type == AmdGpu::PrimitiveType::AdjTriangleList || - key.prim_type == AmdGpu::PrimitiveType::RectList || - key.prim_type == AmdGpu::PrimitiveType::QuadList; - } - /// Gets the attributes and bindings for vertex inputs. template void GetVertexInputs(VertexInputs& attributes, VertexInputs& bindings, diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index ab8f074cd..0df020116 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -242,17 +242,22 @@ bool Instance::CreateDevice() { // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. - add_extension(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME); - add_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); - add_extension(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); - add_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME); - add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); - add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME); - const bool maintenance4 = add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME); + ASSERT(add_extension(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME)); + ASSERT(add_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)); + ASSERT(add_extension(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME)); + ASSERT(add_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME)); + ASSERT(add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME)); + ASSERT(add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME)); + ASSERT(add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME) || + driver_id == vk::DriverId::eIntelProprietaryWindows); + ASSERT(add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME)); - add_extension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - add_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); - add_extension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME); + // Required + ASSERT(add_extension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)); + ASSERT(add_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)); + + // Optional + depth_range_unrestricted = add_extension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME); dynamic_state_3 = add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); if (dynamic_state_3) { dynamic_state_3_features = @@ -276,6 +281,7 @@ bool Instance::CreateDevice() { shader_stencil_export = add_extension(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME); image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME); amd_gcn_shader = add_extension(VK_AMD_GCN_SHADER_EXTENSION_NAME); + amd_shader_trinary_minmax = add_extension(VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME); const bool calibrated_timestamps = TRACY_GPU_ENABLED ? add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME) : false; @@ -379,6 +385,9 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{ .extendedDynamicState = true, }, + vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{ + .extendedDynamicState2 = true, + }, vk::PhysicalDeviceMaintenance4FeaturesKHR{ .maintenance4 = true, }, @@ -417,9 +426,6 @@ bool Instance::CreateDevice() { #endif }; - if (!maintenance4) { - device_chain.unlink(); - } if (!custom_border_color) { device_chain.unlink(); } diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 3ccf89276..a9de01f84 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -84,6 +84,11 @@ public: return features.samplerAnisotropy; } + /// Returns true if depth bounds testing is supported + bool IsDepthBoundsSupported() const { + return features.depthBounds; + } + /// Returns true when VK_EXT_custom_border_color is supported bool IsCustomBorderColorSupported() const { return custom_border_color; @@ -99,6 +104,11 @@ public: return depth_clip_control; } + /// Returns true when VK_EXT_depth_range_unrestricted is supported + bool IsDepthRangeUnrestrictedSupported() const { + return depth_range_unrestricted; + } + /// Returns true when the extendedDynamicState3ColorWriteMask feature of /// VK_EXT_extended_dynamic_state3 is supported. bool IsDynamicColorWriteMaskSupported() const { @@ -145,6 +155,11 @@ public: return amd_gcn_shader; } + /// Returns true when VK_AMD_shader_trinary_minmax is supported. + bool IsAmdShaderTrinaryMinMaxSupported() const { + return amd_shader_trinary_minmax; + } + /// Returns true when geometry shaders are supported by the device bool IsGeometryStageSupported() const { return features.geometryShader; @@ -282,6 +297,11 @@ public: properties.limits.framebufferStencilSampleCounts; } + /// Returns whether disabling primitive restart is supported. + bool IsPrimitiveRestartDisableSupported() const { + return driver_id != vk::DriverId::eMoltenvk; + } + private: /// Creates the logical device opportunistically enabling extensions bool CreateDevice(); @@ -325,6 +345,7 @@ private: bool custom_border_color{}; bool fragment_shader_barycentric{}; bool depth_clip_control{}; + bool depth_range_unrestricted{}; bool dynamic_state_3{}; bool vertex_input_dynamic_state{}; bool robustness2{}; @@ -333,6 +354,7 @@ private: bool shader_stencil_export{}; bool image_load_store_lod{}; bool amd_gcn_shader{}; + bool amd_shader_trinary_minmax{}; bool portability_subset{}; }; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 4823b8ffe..bad2a549c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -123,7 +123,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS info.vs_info.emulate_depth_negative_one_to_one = !instance.IsDepthClipControlSupported() && regs.clipper_control.clip_space == Liverpool::ClipSpace::MinusWToW; - info.vs_info.clip_disable = graphics_key.clip_disable; + info.vs_info.clip_disable = regs.IsClipDisabled(); if (l_stage == LogicalStage::TessellationEval) { info.vs_info.tess_type = regs.tess_config.type; info.vs_info.tess_topology = regs.tess_config.topology; @@ -201,6 +201,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .support_legacy_vertex_attributes = instance_.IsLegacyVertexAttributesSupported(), .supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(), .supports_native_cube_calc = instance_.IsAmdGcnShaderSupported(), + .supports_trinary_minmax = instance_.IsAmdShaderTrinaryMinMaxSupported(), .supports_robust_buffer_access = instance_.IsRobustBufferAccess2Supported(), .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, @@ -266,16 +267,6 @@ bool PipelineCache::RefreshGraphicsKey() { auto& regs = liverpool->regs; auto& key = graphics_key; - key.clip_disable = - regs.clipper_control.clip_disable || regs.primitive_type == AmdGpu::PrimitiveType::RectList; - key.depth_test_enable = regs.depth_control.depth_enable; - key.depth_write_enable = - regs.depth_control.depth_write_enable && !regs.depth_render_control.depth_clear_enable; - key.depth_bounds_test_enable = regs.depth_control.depth_bounds_enable; - key.depth_bias_enable = regs.polygon_control.NeedsBias(); - key.depth_compare_op = LiverpoolToVK::CompareOp(regs.depth_control.depth_func); - key.stencil_test_enable = regs.depth_control.stencil_enable; - const auto depth_format = instance.GetSupportedFormat( LiverpoolToVK::DepthFormat(regs.depth_buffer.z_info.format, regs.depth_buffer.stencil_info.format), @@ -284,22 +275,16 @@ bool PipelineCache::RefreshGraphicsKey() { key.depth_format = depth_format; } else { key.depth_format = vk::Format::eUndefined; - key.depth_test_enable = false; } if (regs.depth_buffer.StencilValid()) { key.stencil_format = depth_format; } else { key.stencil_format = vk::Format::eUndefined; - key.stencil_test_enable = false; } key.prim_type = regs.primitive_type; - key.enable_primitive_restart = regs.enable_primitive_restart & 1; - key.primitive_restart_index = regs.primitive_restart_index; key.polygon_mode = regs.polygon_control.PolyMode(); - key.cull_mode = regs.polygon_control.CullingMode(); key.clip_space = regs.clipper_control.clip_space; - key.front_face = regs.polygon_control.front_face; key.num_samples = regs.NumSamples(); const bool skip_cb_binding = diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index 716473377..e656369b2 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -278,7 +278,6 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e vk::Bool32 enable_force_barriers = vk::True; #ifdef __APPLE__ const vk::Bool32 mvk_debug_mode = enable_crash_diagnostic ? vk::True : vk::False; - constexpr vk::Bool32 mvk_use_mtlheap = vk::True; #endif const std::array layer_setings = { @@ -355,15 +354,6 @@ vk::UniqueInstance CreateInstance(Frontend::WindowSystemType window_type, bool e .valueCount = 1, .pValues = &mvk_debug_mode, }, - // Use MTLHeap to back device memory, which among other things allows us to - // use VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT via memory aliasing. - vk::LayerSettingEXT{ - .pLayerName = "MoltenVK", - .pSettingName = "MVK_CONFIG_USE_MTLHEAP", - .type = vk::LayerSettingTypeEXT::eBool32, - .valueCount = 1, - .pValues = &mvk_use_mtlheap, - }, #endif }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 87d07a967..b04b4a07e 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -930,12 +930,17 @@ bool Rasterizer::IsMapped(VAddr addr, u64 size) { // There is no memory, so not mapped. return false; } - return mapped_ranges.find(boost::icl::interval::right_open(addr, addr + size)) != - mapped_ranges.end(); + const auto range = decltype(mapped_ranges)::interval_type::right_open(addr, addr + size); + + std::shared_lock lock{mapped_ranges_mutex}; + return boost::icl::contains(mapped_ranges, range); } void Rasterizer::MapMemory(VAddr addr, u64 size) { - mapped_ranges += boost::icl::interval::right_open(addr, addr + size); + { + std::unique_lock lock{mapped_ranges_mutex}; + mapped_ranges += decltype(mapped_ranges)::interval_type::right_open(addr, addr + size); + } page_manager.OnGpuMap(addr, size); } @@ -943,85 +948,26 @@ void Rasterizer::UnmapMemory(VAddr addr, u64 size) { buffer_cache.InvalidateMemory(addr, size); texture_cache.UnmapMemory(addr, size); page_manager.OnGpuUnmap(addr, size); - mapped_ranges -= boost::icl::interval::right_open(addr, addr + size); -} - -void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) { - UpdateViewportScissorState(pipeline); - - auto& regs = liverpool->regs; - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.setBlendConstants(®s.blend_constants.red); - - if (instance.IsDynamicColorWriteMaskSupported()) { - cmdbuf.setColorWriteMaskEXT(0, pipeline.GetWriteMasks()); - } - if (regs.depth_control.depth_bounds_enable) { - cmdbuf.setDepthBounds(regs.depth_bounds_min, regs.depth_bounds_max); - } - if (regs.polygon_control.enable_polygon_offset_front) { - cmdbuf.setDepthBias(regs.poly_offset.front_offset, regs.poly_offset.depth_bias, - regs.poly_offset.front_scale / 16.f); - } else if (regs.polygon_control.enable_polygon_offset_back) { - cmdbuf.setDepthBias(regs.poly_offset.back_offset, regs.poly_offset.depth_bias, - regs.poly_offset.back_scale / 16.f); - } - - if (regs.depth_control.stencil_enable) { - const auto front_fail_op = - LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_front); - const auto front_pass_op = - LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_front); - const auto front_depth_fail_op = - LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_front); - const auto front_compare_op = LiverpoolToVK::CompareOp(regs.depth_control.stencil_ref_func); - if (regs.depth_control.backface_enable) { - const auto back_fail_op = - LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_back); - const auto back_pass_op = - LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_back); - const auto back_depth_fail_op = - LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_back); - const auto back_compare_op = - LiverpoolToVK::CompareOp(regs.depth_control.stencil_bf_func); - cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFront, front_fail_op, front_pass_op, - front_depth_fail_op, front_compare_op); - cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eBack, back_fail_op, back_pass_op, - back_depth_fail_op, back_compare_op); - } else { - cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack, front_fail_op, - front_pass_op, front_depth_fail_op, front_compare_op); - } - - const auto front = regs.stencil_ref_front; - const auto back = regs.stencil_ref_back; - if (front.stencil_test_val == back.stencil_test_val) { - cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, - front.stencil_test_val); - } else { - cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFront, front.stencil_test_val); - cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eBack, back.stencil_test_val); - } - - if (front.stencil_write_mask == back.stencil_write_mask) { - cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, - front.stencil_write_mask); - } else { - cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFront, front.stencil_write_mask); - cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eBack, back.stencil_write_mask); - } - - if (front.stencil_mask == back.stencil_mask) { - cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, - front.stencil_mask); - } else { - cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFront, front.stencil_mask); - cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eBack, back.stencil_mask); - } + { + std::unique_lock lock{mapped_ranges_mutex}; + mapped_ranges -= decltype(mapped_ranges)::interval_type::right_open(addr, addr + size); } } -void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { +void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) const { + UpdateViewportScissorState(); + UpdateDepthStencilState(); + UpdatePrimitiveState(); + + auto& dynamic_state = scheduler.GetDynamicState(); + dynamic_state.SetBlendConstants(&liverpool->regs.blend_constants.red); + dynamic_state.SetColorWriteMasks(pipeline.GetWriteMasks()); + + // Commit new dynamic state to the command buffer. + dynamic_state.Commit(instance, scheduler.CommandBuffer()); +} + +void Rasterizer::UpdateViewportScissorState() const { const auto& regs = liverpool->regs; const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) { @@ -1053,16 +999,13 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { boost::container::static_vector viewports; boost::container::static_vector scissors; - const auto& vp_ctl = regs.viewport_control; - const float reduce_z = - regs.clipper_control.clip_space == AmdGpu::Liverpool::ClipSpace::MinusWToW ? 1.0f : 0.0f; - if (regs.polygon_control.enable_window_offset && (regs.window_offset.window_x_offset != 0 || regs.window_offset.window_y_offset != 0)) { LOG_ERROR(Render_Vulkan, "PA_SU_SC_MODE_CNTL.VTX_WINDOW_OFFSET_ENABLE support is not yet implemented."); } + const auto& vp_ctl = regs.viewport_control; for (u32 i = 0; i < Liverpool::NumViewports; i++) { const auto& vp = regs.viewports[i]; const auto& vp_d = regs.viewport_depths[i]; @@ -1072,33 +1015,55 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { const auto zoffset = vp_ctl.zoffset_enable ? vp.zoffset : 0.f; const auto zscale = vp_ctl.zscale_enable ? vp.zscale : 1.f; - if (pipeline.IsClipDisabled()) { + + vk::Viewport viewport{}; + + // https://gitlab.freedesktop.org/mesa/mesa/-/blob/209a0ed/src/amd/vulkan/radv_pipeline_graphics.c#L688-689 + // https://gitlab.freedesktop.org/mesa/mesa/-/blob/209a0ed/src/amd/vulkan/radv_cmd_buffer.c#L3103-3109 + // When the clip space is ranged [-1...1], the zoffset is centered. + // By reversing the above viewport calculations, we get the following: + if (regs.clipper_control.clip_space == AmdGpu::Liverpool::ClipSpace::MinusWToW) { + viewport.minDepth = zoffset - zscale; + viewport.maxDepth = zoffset + zscale; + } else { + viewport.minDepth = zoffset; + viewport.maxDepth = zoffset + zscale; + } + + if (!regs.depth_render_override.disable_viewport_clamp) { + // Apply depth clamp. + viewport.minDepth = std::max(viewport.minDepth, vp_d.zmin); + viewport.maxDepth = std::min(viewport.maxDepth, vp_d.zmax); + } + + if (!instance.IsDepthRangeUnrestrictedSupported()) { + // Unrestricted depth range not supported by device. Restrict to valid range. + viewport.minDepth = std::max(viewport.minDepth, 0.f); + viewport.maxDepth = std::min(viewport.maxDepth, 1.f); + } + + if (regs.IsClipDisabled()) { // In case if clipping is disabled we patch the shader to convert vertex position // from screen space coordinates to NDC by defining a render space as full hardware // window range [0..16383, 0..16383] and setting the viewport to its size. - viewports.push_back({ - .x = 0.f, - .y = 0.f, - .width = float(std::min(instance.GetMaxViewportWidth(), 16_KB)), - .height = float(std::min(instance.GetMaxViewportHeight(), 16_KB)), - .minDepth = zoffset - zscale * reduce_z, - .maxDepth = zscale + zoffset, - }); + viewport.x = 0.f; + viewport.y = 0.f; + viewport.width = float(std::min(instance.GetMaxViewportWidth(), 16_KB)); + viewport.height = float(std::min(instance.GetMaxViewportHeight(), 16_KB)); } else { const auto xoffset = vp_ctl.xoffset_enable ? vp.xoffset : 0.f; const auto xscale = vp_ctl.xscale_enable ? vp.xscale : 1.f; const auto yoffset = vp_ctl.yoffset_enable ? vp.yoffset : 0.f; const auto yscale = vp_ctl.yscale_enable ? vp.yscale : 1.f; - viewports.push_back({ - .x = xoffset - xscale, - .y = yoffset - yscale, - .width = xscale * 2.0f, - .height = yscale * 2.0f, - .minDepth = zoffset - zscale * reduce_z, - .maxDepth = zscale + zoffset, - }); + + viewport.x = xoffset - xscale; + viewport.y = yoffset - yscale; + viewport.width = xscale * 2.0f; + viewport.height = yscale * 2.0f; } + viewports.push_back(viewport); + auto vp_scsr = scsr; if (regs.mode_control.vport_scissor_enable) { vp_scsr.top_left_x = @@ -1134,9 +1099,84 @@ void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) { scissors.push_back(empty_scissor); } - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.setViewportWithCountEXT(viewports); - cmdbuf.setScissorWithCountEXT(scissors); + auto& dynamic_state = scheduler.GetDynamicState(); + dynamic_state.SetViewports(viewports); + dynamic_state.SetScissors(scissors); +} + +void Rasterizer::UpdateDepthStencilState() const { + const auto& regs = liverpool->regs; + auto& dynamic_state = scheduler.GetDynamicState(); + + const auto depth_test_enabled = + regs.depth_control.depth_enable && regs.depth_buffer.DepthValid(); + dynamic_state.SetDepthTestEnabled(depth_test_enabled); + if (depth_test_enabled) { + dynamic_state.SetDepthWriteEnabled(regs.depth_control.depth_write_enable && + !regs.depth_render_control.depth_clear_enable); + dynamic_state.SetDepthCompareOp(LiverpoolToVK::CompareOp(regs.depth_control.depth_func)); + } + + const auto depth_bounds_test_enabled = regs.depth_control.depth_bounds_enable; + dynamic_state.SetDepthBoundsTestEnabled(depth_bounds_test_enabled); + if (depth_bounds_test_enabled) { + dynamic_state.SetDepthBounds(regs.depth_bounds_min, regs.depth_bounds_max); + } + + const auto depth_bias_enabled = regs.polygon_control.NeedsBias(); + dynamic_state.SetDepthBiasEnabled(depth_bias_enabled); + if (depth_bias_enabled) { + const bool front = regs.polygon_control.enable_polygon_offset_front; + dynamic_state.SetDepthBias( + front ? regs.poly_offset.front_offset : regs.poly_offset.back_offset, + regs.poly_offset.depth_bias, + (front ? regs.poly_offset.front_scale : regs.poly_offset.back_scale) / 16.f); + } + + const auto stencil_test_enabled = + regs.depth_control.stencil_enable && regs.depth_buffer.StencilValid(); + dynamic_state.SetStencilTestEnabled(stencil_test_enabled); + if (stencil_test_enabled) { + const StencilOps front_ops{ + .fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_front), + .pass_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_front), + .depth_fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_front), + .compare_op = LiverpoolToVK::CompareOp(regs.depth_control.stencil_ref_func), + }; + const StencilOps back_ops = regs.depth_control.backface_enable ? StencilOps{ + .fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_fail_back), + .pass_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zpass_back), + .depth_fail_op = LiverpoolToVK::StencilOp(regs.stencil_control.stencil_zfail_back), + .compare_op = LiverpoolToVK::CompareOp(regs.depth_control.stencil_bf_func), + } : front_ops; + dynamic_state.SetStencilOps(front_ops, back_ops); + + const auto front = regs.stencil_ref_front; + const auto back = + regs.depth_control.backface_enable ? regs.stencil_ref_back : regs.stencil_ref_front; + dynamic_state.SetStencilReferences(front.stencil_test_val, back.stencil_test_val); + dynamic_state.SetStencilWriteMasks(front.stencil_write_mask, back.stencil_write_mask); + dynamic_state.SetStencilCompareMasks(front.stencil_mask, back.stencil_mask); + } +} + +void Rasterizer::UpdatePrimitiveState() const { + const auto& regs = liverpool->regs; + auto& dynamic_state = scheduler.GetDynamicState(); + + const auto prim_restart = (regs.enable_primitive_restart & 1) != 0; + ASSERT_MSG(!prim_restart || regs.primitive_restart_index == 0xFFFF || + regs.primitive_restart_index == 0xFFFFFFFF, + "Primitive restart index other than -1 is not supported yet"); + + const auto cull_mode = LiverpoolToVK::IsPrimitiveCulled(regs.primitive_type) + ? LiverpoolToVK::CullMode(regs.polygon_control.CullingMode()) + : vk::CullModeFlagBits::eNone; + const auto front_face = LiverpoolToVK::FrontFace(regs.polygon_control.front_face); + + dynamic_state.SetPrimitiveRestartEnabled(prim_restart); + dynamic_state.SetCullMode(cull_mode); + dynamic_state.SetFrontFace(front_face); } void Rasterizer::ScopeMarkerBegin(const std::string_view& str, bool from_guest) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 2fac8c8da..4e0ed0996 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/page_manager.h" #include "video_core/renderer_vulkan/vk_pipeline_cache.h" @@ -75,8 +77,10 @@ private: void DepthStencilCopy(bool is_depth, bool is_stencil); void EliminateFastClear(); - void UpdateDynamicState(const GraphicsPipeline& pipeline); - void UpdateViewportScissorState(const GraphicsPipeline& pipeline); + void UpdateDynamicState(const GraphicsPipeline& pipeline) const; + void UpdateViewportScissorState() const; + void UpdateDepthStencilState() const; + void UpdatePrimitiveState() const; bool FilterDraw(); @@ -104,6 +108,7 @@ private: AmdGpu::Liverpool* liverpool; Core::MemoryManager* memory; boost::icl::interval_set mapped_ranges; + std::shared_mutex mapped_ranges_mutex; PipelineCache pipeline_cache; boost::container::static_vector< diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index fd84c54ed..a48d93dee 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -97,6 +97,9 @@ void Scheduler::AllocateWorkerCommandBuffers() { ASSERT_MSG(begin_result == vk::Result::eSuccess, "Failed to begin command buffer: {}", vk::to_string(begin_result)); + // Invalidate dynamic state so it gets applied to the new command buffer. + dynamic_state.Invalidate(); + #if TRACY_GPU_ENABLED auto* profiler_ctx = instance.GetProfilerContext(); if (profiler_ctx) { @@ -164,4 +167,151 @@ void Scheduler::SubmitExecution(SubmitInfo& info) { } } +void DynamicState::Commit(const Instance& instance, const vk::CommandBuffer& cmdbuf) { + if (dirty_state.viewports) { + dirty_state.viewports = false; + cmdbuf.setViewportWithCountEXT(viewports); + } + if (dirty_state.scissors) { + dirty_state.scissors = false; + cmdbuf.setScissorWithCountEXT(scissors); + } + if (dirty_state.depth_test_enabled) { + dirty_state.depth_test_enabled = false; + cmdbuf.setDepthTestEnableEXT(depth_test_enabled); + } + if (dirty_state.depth_write_enabled) { + dirty_state.depth_write_enabled = false; + // Note that this must be set in a command buffer even if depth test is disabled. + cmdbuf.setDepthWriteEnableEXT(depth_write_enabled); + } + if (depth_test_enabled && dirty_state.depth_compare_op) { + dirty_state.depth_compare_op = false; + cmdbuf.setDepthCompareOpEXT(depth_compare_op); + } + if (dirty_state.depth_bounds_test_enabled) { + dirty_state.depth_bounds_test_enabled = false; + if (instance.IsDepthBoundsSupported()) { + cmdbuf.setDepthBoundsTestEnableEXT(depth_bounds_test_enabled); + } + } + if (depth_bounds_test_enabled && dirty_state.depth_bounds) { + dirty_state.depth_bounds = false; + if (instance.IsDepthBoundsSupported()) { + cmdbuf.setDepthBounds(depth_bounds_min, depth_bounds_max); + } + } + if (dirty_state.depth_bias_enabled) { + dirty_state.depth_bias_enabled = false; + cmdbuf.setDepthBiasEnableEXT(depth_bias_enabled); + } + if (depth_bias_enabled && dirty_state.depth_bias) { + dirty_state.depth_bias = false; + cmdbuf.setDepthBias(depth_bias_constant, depth_bias_clamp, depth_bias_slope); + } + if (dirty_state.stencil_test_enabled) { + dirty_state.stencil_test_enabled = false; + cmdbuf.setStencilTestEnableEXT(stencil_test_enabled); + } + if (stencil_test_enabled) { + if (dirty_state.stencil_front_ops && dirty_state.stencil_back_ops && + stencil_front_ops == stencil_back_ops) { + dirty_state.stencil_front_ops = false; + dirty_state.stencil_back_ops = false; + cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack, + stencil_front_ops.fail_op, stencil_front_ops.pass_op, + stencil_front_ops.depth_fail_op, stencil_front_ops.compare_op); + } else { + if (dirty_state.stencil_front_ops) { + dirty_state.stencil_front_ops = false; + cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eFront, stencil_front_ops.fail_op, + stencil_front_ops.pass_op, stencil_front_ops.depth_fail_op, + stencil_front_ops.compare_op); + } + if (dirty_state.stencil_back_ops) { + dirty_state.stencil_back_ops = false; + cmdbuf.setStencilOpEXT(vk::StencilFaceFlagBits::eBack, stencil_back_ops.fail_op, + stencil_back_ops.pass_op, stencil_back_ops.depth_fail_op, + stencil_back_ops.compare_op); + } + } + if (dirty_state.stencil_front_reference && dirty_state.stencil_back_reference && + stencil_front_reference == stencil_back_reference) { + dirty_state.stencil_front_reference = false; + dirty_state.stencil_back_reference = false; + cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, + stencil_front_reference); + } else { + if (dirty_state.stencil_front_reference) { + dirty_state.stencil_front_reference = false; + cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFront, + stencil_front_reference); + } + if (dirty_state.stencil_back_reference) { + dirty_state.stencil_back_reference = false; + cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eBack, stencil_back_reference); + } + } + if (dirty_state.stencil_front_write_mask && dirty_state.stencil_back_write_mask && + stencil_front_write_mask == stencil_back_write_mask) { + dirty_state.stencil_front_write_mask = false; + dirty_state.stencil_back_write_mask = false; + cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, + stencil_front_write_mask); + } else { + if (dirty_state.stencil_front_write_mask) { + dirty_state.stencil_front_write_mask = false; + cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFront, + stencil_front_write_mask); + } + if (dirty_state.stencil_back_write_mask) { + dirty_state.stencil_back_write_mask = false; + cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eBack, stencil_back_write_mask); + } + } + if (dirty_state.stencil_front_compare_mask && dirty_state.stencil_back_compare_mask && + stencil_front_compare_mask == stencil_back_compare_mask) { + dirty_state.stencil_front_compare_mask = false; + dirty_state.stencil_back_compare_mask = false; + cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, + stencil_front_compare_mask); + } else { + if (dirty_state.stencil_front_compare_mask) { + dirty_state.stencil_front_compare_mask = false; + cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFront, + stencil_front_compare_mask); + } + if (dirty_state.stencil_back_compare_mask) { + dirty_state.stencil_back_compare_mask = false; + cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eBack, + stencil_back_compare_mask); + } + } + } + if (dirty_state.primitive_restart_enable) { + dirty_state.primitive_restart_enable = false; + if (instance.IsPrimitiveRestartDisableSupported()) { + cmdbuf.setPrimitiveRestartEnableEXT(primitive_restart_enable); + } + } + if (dirty_state.cull_mode) { + dirty_state.cull_mode = false; + cmdbuf.setCullModeEXT(cull_mode); + } + if (dirty_state.front_face) { + dirty_state.front_face = false; + cmdbuf.setFrontFaceEXT(front_face); + } + if (dirty_state.blend_constants) { + dirty_state.blend_constants = false; + cmdbuf.setBlendConstants(blend_constants); + } + if (dirty_state.color_write_masks) { + dirty_state.color_write_masks = false; + if (instance.IsDynamicColorWriteMaskSupported()) { + cmdbuf.setColorWriteMaskEXT(0, color_write_masks); + } + } +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index fd5e68373..7709e1d41 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -7,6 +7,7 @@ #include #include "common/types.h" #include "common/unique_function.h" +#include "video_core/amdgpu/liverpool.h" #include "video_core/renderer_vulkan/vk_master_semaphore.h" #include "video_core/renderer_vulkan/vk_resource_pool.h" @@ -55,6 +56,248 @@ struct SubmitInfo { } }; +using Viewports = boost::container::static_vector; +using Scissors = boost::container::static_vector; +using ColorWriteMasks = std::array; +struct StencilOps { + vk::StencilOp fail_op{}; + vk::StencilOp pass_op{}; + vk::StencilOp depth_fail_op{}; + vk::CompareOp compare_op{}; + + bool operator==(const StencilOps& other) const { + return fail_op == other.fail_op && pass_op == other.pass_op && + depth_fail_op == other.depth_fail_op && compare_op == other.compare_op; + } +}; +struct DynamicState { + struct { + bool viewports : 1; + bool scissors : 1; + + bool depth_test_enabled : 1; + bool depth_write_enabled : 1; + bool depth_compare_op : 1; + + bool depth_bounds_test_enabled : 1; + bool depth_bounds : 1; + + bool depth_bias_enabled : 1; + bool depth_bias : 1; + + bool stencil_test_enabled : 1; + bool stencil_front_ops : 1; + bool stencil_front_reference : 1; + bool stencil_front_write_mask : 1; + bool stencil_front_compare_mask : 1; + bool stencil_back_ops : 1; + bool stencil_back_reference : 1; + bool stencil_back_write_mask : 1; + bool stencil_back_compare_mask : 1; + + bool primitive_restart_enable : 1; + bool cull_mode : 1; + bool front_face : 1; + + bool blend_constants : 1; + bool color_write_masks : 1; + } dirty_state{}; + + Viewports viewports{}; + Scissors scissors{}; + + bool depth_test_enabled{}; + bool depth_write_enabled{}; + vk::CompareOp depth_compare_op{}; + + bool depth_bounds_test_enabled{}; + float depth_bounds_min{}; + float depth_bounds_max{}; + + bool depth_bias_enabled{}; + float depth_bias_constant{}; + float depth_bias_clamp{}; + float depth_bias_slope{}; + + bool stencil_test_enabled{}; + StencilOps stencil_front_ops{}; + u32 stencil_front_reference{}; + u32 stencil_front_write_mask{}; + u32 stencil_front_compare_mask{}; + StencilOps stencil_back_ops{}; + u32 stencil_back_reference{}; + u32 stencil_back_write_mask{}; + u32 stencil_back_compare_mask{}; + + bool primitive_restart_enable{}; + vk::CullModeFlags cull_mode{}; + vk::FrontFace front_face{}; + + float blend_constants[4]{}; + ColorWriteMasks color_write_masks{}; + + /// Commits the dynamic state to the provided command buffer. + void Commit(const Instance& instance, const vk::CommandBuffer& cmdbuf); + + /// Invalidates all dynamic state to be flushed into the next command buffer. + void Invalidate() { + std::memset(&dirty_state, 0xFF, sizeof(dirty_state)); + } + + void SetViewports(const Viewports& viewports_) { + if (!std::ranges::equal(viewports, viewports_)) { + viewports = viewports_; + dirty_state.viewports = true; + } + } + + void SetScissors(const Scissors& scissors_) { + if (!std::ranges::equal(scissors, scissors_)) { + scissors = scissors_; + dirty_state.scissors = true; + } + } + + void SetDepthTestEnabled(const bool enabled) { + if (depth_test_enabled != enabled) { + depth_test_enabled = enabled; + dirty_state.depth_test_enabled = true; + } + } + + void SetDepthWriteEnabled(const bool enabled) { + if (depth_write_enabled != enabled) { + depth_write_enabled = enabled; + dirty_state.depth_write_enabled = true; + } + } + + void SetDepthCompareOp(const vk::CompareOp compare_op) { + if (depth_compare_op != compare_op) { + depth_compare_op = compare_op; + dirty_state.depth_compare_op = true; + } + } + + void SetDepthBoundsTestEnabled(const bool enabled) { + if (depth_bounds_test_enabled != enabled) { + depth_bounds_test_enabled = enabled; + dirty_state.depth_bounds_test_enabled = true; + } + } + + void SetDepthBounds(const float min, const float max) { + if (depth_bounds_min != min || depth_bounds_max != max) { + depth_bounds_min = min; + depth_bounds_max = max; + dirty_state.depth_bounds = true; + } + } + + void SetDepthBiasEnabled(const bool enabled) { + if (depth_bias_enabled != enabled) { + depth_bias_enabled = enabled; + dirty_state.depth_bias_enabled = true; + } + } + + void SetDepthBias(const float constant, const float clamp, const float slope) { + if (depth_bias_constant != constant || depth_bias_clamp != clamp || + depth_bias_slope != slope) { + depth_bias_constant = constant; + depth_bias_clamp = clamp; + depth_bias_slope = slope; + dirty_state.depth_bias = true; + } + } + + void SetStencilTestEnabled(const bool enabled) { + if (stencil_test_enabled != enabled) { + stencil_test_enabled = enabled; + dirty_state.stencil_test_enabled = true; + } + } + + void SetStencilOps(const StencilOps& front_ops, const StencilOps& back_ops) { + if (stencil_front_ops != front_ops) { + stencil_front_ops = front_ops; + dirty_state.stencil_front_ops = true; + } + if (stencil_back_ops != back_ops) { + stencil_back_ops = back_ops; + dirty_state.stencil_back_ops = true; + } + } + + void SetStencilReferences(const u32 front_reference, const u32 back_reference) { + if (stencil_front_reference != front_reference) { + stencil_front_reference = front_reference; + dirty_state.stencil_front_reference = true; + } + if (stencil_back_reference != back_reference) { + stencil_back_reference = back_reference; + dirty_state.stencil_back_reference = true; + } + } + + void SetStencilWriteMasks(const u32 front_write_mask, const u32 back_write_mask) { + if (stencil_front_write_mask != front_write_mask) { + stencil_front_write_mask = front_write_mask; + dirty_state.stencil_front_write_mask = true; + } + if (stencil_back_write_mask != back_write_mask) { + stencil_back_write_mask = back_write_mask; + dirty_state.stencil_back_write_mask = true; + } + } + + void SetStencilCompareMasks(const u32 front_compare_mask, const u32 back_compare_mask) { + if (stencil_front_compare_mask != front_compare_mask) { + stencil_front_compare_mask = front_compare_mask; + dirty_state.stencil_front_compare_mask = true; + } + if (stencil_back_compare_mask != back_compare_mask) { + stencil_back_compare_mask = back_compare_mask; + dirty_state.stencil_back_compare_mask = true; + } + } + + void SetPrimitiveRestartEnabled(const bool enabled) { + if (primitive_restart_enable != enabled) { + primitive_restart_enable = enabled; + dirty_state.primitive_restart_enable = true; + } + } + + void SetCullMode(const vk::CullModeFlags cull_mode_) { + if (cull_mode != cull_mode_) { + cull_mode = cull_mode_; + dirty_state.cull_mode = true; + } + } + + void SetFrontFace(const vk::FrontFace front_face_) { + if (front_face != front_face_) { + front_face = front_face_; + dirty_state.front_face = true; + } + } + + void SetBlendConstants(const float blend_constants_[4]) { + if (!std::equal(blend_constants, std::end(blend_constants), blend_constants_)) { + std::memcpy(blend_constants, blend_constants_, sizeof(blend_constants)); + dirty_state.blend_constants = true; + } + } + + void SetColorWriteMasks(const ColorWriteMasks& color_write_masks_) { + if (!std::ranges::equal(color_write_masks, color_write_masks_)) { + color_write_masks = color_write_masks_; + dirty_state.color_write_masks = true; + } + } +}; + class Scheduler { public: explicit Scheduler(const Instance& instance); @@ -81,6 +324,10 @@ public: return render_state; } + DynamicState& GetDynamicState() { + return dynamic_state; + } + /// Returns the current command buffer. vk::CommandBuffer CommandBuffer() const { return current_cmdbuf; @@ -125,6 +372,7 @@ private: }; std::queue pending_ops; RenderState render_state; + DynamicState dynamic_state; bool is_rendering = false; tracy::VkCtxScope* profiler_scope{}; };