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/CMakeLists.txt b/CMakeLists.txt
index a689be7d4..df2ddafcd 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,7 +10,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
if(APPLE)
list(APPEND ADDITIONAL_LANGUAGES OBJC)
# Starting with 15.4, Rosetta 2 has support for all the necessary instruction sets.
- set(CMAKE_OSX_DEPLOYMENT_TARGET 15.4)
+ set(CMAKE_OSX_DEPLOYMENT_TARGET 15.4 CACHE STRING "")
endif()
if (NOT CMAKE_BUILD_TYPE)
@@ -54,8 +54,9 @@ else()
endif()
if (ARCHITECTURE STREQUAL "x86_64")
- # Target the same x86_64 feature set as the PS4 CPU to match requirements.
- add_compile_options(-march=btver2 -mno-sse4a)
+ # 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")
@@ -104,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)
@@ -202,10 +200,13 @@ execute_process(
OUTPUT_STRIP_TRAILING_WHITESPACE
)
+set(APP_VERSION "0.7.1 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)
@@ -218,7 +219,7 @@ 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)
@@ -226,10 +227,10 @@ find_package(ZLIB 1.3 MODULE)
find_package(Zydis 5.0.0 CONFIG)
find_package(pugixml 1.14 CONFIG)
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)
@@ -449,8 +450,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
@@ -675,7 +674,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
@@ -1176,7 +1174,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 +1195,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 6462c8bd2..0e2248970 100644
--- a/README.md
+++ b/README.md
@@ -122,6 +122,27 @@ 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)
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/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md
index 9c6bc5a6f..55825ac7d 100644
--- a/documents/Quickstart/Quickstart.md
+++ b/documents/Quickstart/Quickstart.md
@@ -24,7 +24,7 @@ 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
diff --git a/externals/MoltenVK/MoltenVK b/externals/MoltenVK/MoltenVK
index 2048427e5..83510e0f3 160000
--- a/externals/MoltenVK/MoltenVK
+++ b/externals/MoltenVK/MoltenVK
@@ -1 +1 @@
-Subproject commit 2048427e50f9eb20f2b8f98d316ecaee398c9b91
+Subproject commit 83510e0f3835c3c43651dda087305abc42572e17
diff --git a/externals/MoltenVK/SPIRV-Cross b/externals/MoltenVK/SPIRV-Cross
index 2c32b6bf8..68300dc07 160000
--- a/externals/MoltenVK/SPIRV-Cross
+++ b/externals/MoltenVK/SPIRV-Cross
@@ -1 +1 @@
-Subproject commit 2c32b6bf86f3c4a5539aa1f0bacbd59fe61759cf
+Subproject commit 68300dc07ac3dc592dbbdb87e02d5180f984ad12
diff --git a/externals/MoltenVK/cereal b/externals/MoltenVK/cereal
index d1fcec807..a56bad8bb 160000
--- a/externals/MoltenVK/cereal
+++ b/externals/MoltenVK/cereal
@@ -1 +1 @@
-Subproject commit d1fcec807b372f04e4c1041b3058e11c12853e6e
+Subproject commit a56bad8bbb770ee266e930c95d37fff2a5be7fea
diff --git a/externals/date b/externals/date
index 28b7b2325..a45ea7c17 160000
--- a/externals/date
+++ b/externals/date
@@ -1 +1 @@
-Subproject commit 28b7b232521ace2c8ef3f2ad4126daec3569c14f
+Subproject commit a45ea7c17b4a7f320e199b71436074bd624c9e15
diff --git a/externals/dear_imgui b/externals/dear_imgui
index 636cd4a7d..f4d935909 160000
--- a/externals/dear_imgui
+++ b/externals/dear_imgui
@@ -1 +1 @@
-Subproject commit 636cd4a7d623a2bc9bf59bb3acbb4ca075befba3
+Subproject commit f4d9359095eff3eb03f685921edc1cf0e37b1687
diff --git a/externals/discord-rpc b/externals/discord-rpc
index d3b5af882..19f66e6dc 160000
--- a/externals/discord-rpc
+++ b/externals/discord-rpc
@@ -1 +1 @@
-Subproject commit d3b5af8827031f3bccbf8c15d5dc1bfdc9467f17
+Subproject commit 19f66e6dcabb2268965f453db9e5774ede43238f
diff --git a/externals/ffmpeg-core b/externals/ffmpeg-core
index 27de97c82..b0de1dcca 160000
--- a/externals/ffmpeg-core
+++ b/externals/ffmpeg-core
@@ -1 +1 @@
-Subproject commit 27de97c826b6b40c255891c37ac046a25836a575
+Subproject commit b0de1dcca26c0ebfb8011b8e59dd17fc399db0ff
diff --git a/externals/fmt b/externals/fmt
index 8ee89546f..64db979e3 160000
--- a/externals/fmt
+++ b/externals/fmt
@@ -1 +1 @@
-Subproject commit 8ee89546ffcf046309d1f0d38c0393f02fde56c8
+Subproject commit 64db979e38ec644b1798e41610b28c8d2c8a2739
diff --git a/externals/glslang b/externals/glslang
index a0995c49e..ba1640446 160000
--- a/externals/glslang
+++ b/externals/glslang
@@ -1 +1 @@
-Subproject commit a0995c49ebcaca2c6d3b03efbabf74f3843decdb
+Subproject commit ba1640446f3826a518721d1f083f3a8cca1120c3
diff --git a/externals/libusb b/externals/libusb
index 8f0b4a38f..a63a7e43e 160000
--- a/externals/libusb
+++ b/externals/libusb
@@ -1 +1 @@
-Subproject commit 8f0b4a38fc3eefa2b26a99dff89e1c12bf37afd4
+Subproject commit a63a7e43e0950a595cf4b98a0eaf4051749ace5f
diff --git a/externals/magic_enum b/externals/magic_enum
index 1a1824df7..a413fcc9c 160000
--- a/externals/magic_enum
+++ b/externals/magic_enum
@@ -1 +1 @@
-Subproject commit 1a1824df7ac798177a521eed952720681b0bf482
+Subproject commit a413fcc9c46a020a746907136a384c227f3cd095
diff --git a/externals/pugixml b/externals/pugixml
index 4bc14418d..caade5a28 160000
--- a/externals/pugixml
+++ b/externals/pugixml
@@ -1 +1 @@
-Subproject commit 4bc14418d12d289dd9978fdce9490a45deeb653e
+Subproject commit caade5a28aad86b92a4b5337a9dc70c4ba73c5eb
diff --git a/externals/robin-map b/externals/robin-map
index fe845fd78..4ec1bf19c 160000
--- a/externals/robin-map
+++ b/externals/robin-map
@@ -1 +1 @@
-Subproject commit fe845fd7852ef541c5479ae23b3d36b57f8608ee
+Subproject commit 4ec1bf19c6a96125ea22062f38c2cf5b958e448e
diff --git a/externals/sdl3 b/externals/sdl3
index a336b62d8..4093e4a19 160000
--- a/externals/sdl3
+++ b/externals/sdl3
@@ -1 +1 @@
-Subproject commit a336b62d8b0b97b09214e053203e442e2b6e2be5
+Subproject commit 4093e4a193971ef1d4928158e0a1832be42e4599
diff --git a/externals/toml11 b/externals/toml11
index 7f6c574ff..a01fe3b4c 160000
--- a/externals/toml11
+++ b/externals/toml11
@@ -1 +1 @@
-Subproject commit 7f6c574ff5aa1053534e7e19c0a4f22bf4c6aaca
+Subproject commit a01fe3b4c14c6d7b99ee3f07c9e80058c6403097
diff --git a/externals/vma b/externals/vma
index 5a53a1989..f378e7b3f 160000
--- a/externals/vma
+++ b/externals/vma
@@ -1 +1 @@
-Subproject commit 5a53a198945ba8260fbc58fadb788745ce6aa263
+Subproject commit f378e7b3f18f6e2b06b957f6ba7b1c7207d2a536
diff --git a/externals/vulkan-headers b/externals/vulkan-headers
index a03d2f6d5..5ceb9ed48 160000
--- a/externals/vulkan-headers
+++ b/externals/vulkan-headers
@@ -1 +1 @@
-Subproject commit a03d2f6d5753b365d704d58161825890baad0755
+Subproject commit 5ceb9ed481e58e705d0d9b5326537daedd06b97d
diff --git a/externals/winpthreads b/externals/winpthreads
index f00c973a6..f35b0948d 160000
--- a/externals/winpthreads
+++ b/externals/winpthreads
@@ -1 +1 @@
-Subproject commit f00c973a6ab2a23573708568b8ef4acc20a9d36b
+Subproject commit f35b0948d36a736e6a2d052ae295a3ffde09703f
diff --git a/externals/xbyak b/externals/xbyak
index 4e44f4614..44a72f369 160000
--- a/externals/xbyak
+++ b/externals/xbyak
@@ -1 +1 @@
-Subproject commit 4e44f4614ddbf038f2a6296f5b906d5c72691e0f
+Subproject commit 44a72f369268f7d552650891b296693e91db86bb
diff --git a/externals/xxhash b/externals/xxhash
index 2bf8313b9..953a09abc 160000
--- a/externals/xxhash
+++ b/externals/xxhash
@@ -1 +1 @@
-Subproject commit 2bf8313b934633b2a5b7e8fd239645b85e10c852
+Subproject commit 953a09abc39096da9e216b6eb0002c681cdc1199
diff --git a/externals/zlib-ng b/externals/zlib-ng
index d54e3769b..fd0d263ce 160000
--- a/externals/zlib-ng
+++ b/externals/zlib-ng
@@ -1 +1 @@
-Subproject commit d54e3769be0c522015b784eca2af258b1c026107
+Subproject commit fd0d263cedab1a136f40d65199987e3eaeecfcbd
diff --git a/externals/zydis b/externals/zydis
index bffbb610c..120e0e705 160000
--- a/externals/zydis
+++ b/externals/zydis
@@ -1 +1 @@
-Subproject commit bffbb610cfea643b98e87658b9058382f7522807
+Subproject commit 120e0e705f8e3b507dc49377ac2879979f0d545c
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/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/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/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/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/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/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/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/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 63ae733e8..f71ec7d3a 100644
--- a/src/qt_gui/translations/ar_SA.ts
+++ b/src/qt_gui/translations/ar_SA.ts
@@ -1892,43 +1892,43 @@
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 +1936,15 @@
async
- async
+ غير متزامن
sync
- sync
+ متزامن
Auto Select
- Auto Select
+ تلقائي
Directory to install games
@@ -1952,11 +1952,11 @@
Directory to save data
- Directory to save data
+ مسار الحفظ لبيانات الألعاب
Video
- Video
+ الفيديو
Display Mode
@@ -1992,7 +1992,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 +2028,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 +2040,7 @@
Portable user folder created
- Portable user folder created
+ تم إنشاء مجلد مستخدم محمول
%1 successfully created.
@@ -2048,7 +2048,7 @@
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.
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/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_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
index 8de903ce6..347c4cb0a 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp
@@ -267,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/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..f89f0a582 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},
diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp
index 22020d59f..da25f5434 100644
--- a/src/shader_recompiler/frontend/translate/vector_alu.cpp
+++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp
@@ -1010,8 +1010,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.
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/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/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 1f0125791..d45889054 100644
--- a/src/video_core/renderer_vulkan/vk_instance.cpp
+++ b/src/video_core/renderer_vulkan/vk_instance.cpp
@@ -247,6 +247,7 @@ bool Instance::CreateDevice() {
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_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME);
const bool maintenance4 = add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME);
@@ -380,6 +381,9 @@ bool Instance::CreateDevice() {
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{
.extendedDynamicState = true,
},
+ vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{
+ .extendedDynamicState2 = true,
+ },
vk::PhysicalDeviceMaintenance4FeaturesKHR{
.maintenance4 = true,
},
diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h
index 013003b9b..6de419041 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;
@@ -287,6 +292,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();
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index d51c8fbd5..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;
@@ -267,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),
@@ -285,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..30102960a 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -946,82 +946,20 @@ void Rasterizer::UnmapMemory(VAddr addr, u64 size) {
mapped_ranges -= boost::icl::interval::right_open(addr, addr + size);
}
-void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) {
- UpdateViewportScissorState(pipeline);
+void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) const {
+ UpdateViewportScissorState();
+ UpdateDepthStencilState();
+ UpdatePrimitiveState();
- auto& regs = liverpool->regs;
- const auto cmdbuf = scheduler.CommandBuffer();
- cmdbuf.setBlendConstants(®s.blend_constants.red);
+ auto& dynamic_state = scheduler.GetDynamicState();
+ dynamic_state.SetBlendConstants(&liverpool->regs.blend_constants.red);
+ dynamic_state.SetColorWriteMasks(pipeline.GetWriteMasks());
- 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);
- }
- }
+ // Commit new dynamic state to the command buffer.
+ dynamic_state.Commit(instance, scheduler.CommandBuffer());
}
-void Rasterizer::UpdateViewportScissorState(const GraphicsPipeline& pipeline) {
+void Rasterizer::UpdateViewportScissorState() const {
const auto& regs = liverpool->regs;
const auto combined_scissor_value_tl = [](s16 scr, s16 win, s16 gen, s16 win_offset) {
@@ -1072,7 +1010,7 @@ 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()) {
+ 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.
@@ -1134,9 +1072,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..54bf3d253 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -75,8 +75,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();
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{};
};