mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-12 04:35:56 +00:00
Merge branch 'main' into Feature/initial-ps4-ime-keyboard
This commit is contained in:
commit
54621e6099
126 changed files with 3282 additions and 994 deletions
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -30,10 +30,6 @@
|
||||||
path = externals/xbyak
|
path = externals/xbyak
|
||||||
url = https://github.com/herumi/xbyak.git
|
url = https://github.com/herumi/xbyak.git
|
||||||
shallow = true
|
shallow = true
|
||||||
[submodule "externals/winpthreads"]
|
|
||||||
path = externals/winpthreads
|
|
||||||
url = https://github.com/shadps4-emu/winpthreads.git
|
|
||||||
shallow = true
|
|
||||||
[submodule "externals/magic_enum"]
|
[submodule "externals/magic_enum"]
|
||||||
path = externals/magic_enum
|
path = externals/magic_enum
|
||||||
url = https://github.com/Neargye/magic_enum.git
|
url = https://github.com/Neargye/magic_enum.git
|
||||||
|
|
|
@ -54,9 +54,9 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ARCHITECTURE STREQUAL "x86_64")
|
if (ARCHITECTURE STREQUAL "x86_64")
|
||||||
# Target the same CPU architecture as the PS4, to maintain the same level of compatibility.
|
# Target x86-64-v3 CPU architecture as this is a good balance between supporting performance critical
|
||||||
# Exclude SSE4a as it is only available on AMD CPUs.
|
# instructions like AVX2 and maintaining support for older CPUs.
|
||||||
add_compile_options(-march=btver2 -mtune=generic -mno-sse4a)
|
add_compile_options(-march=x86-64-v3)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
|
if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||||
|
@ -134,6 +134,7 @@ if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "")
|
||||||
ERROR_QUIET
|
ERROR_QUIET
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
)
|
)
|
||||||
|
message("got remote: ${GIT_REMOTE_NAME}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# If running in GitHub Actions and the above fails
|
# If running in GitHub Actions and the above fails
|
||||||
|
@ -177,7 +178,7 @@ if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "")
|
||||||
set(GIT_BRANCH "${GITHUB_BRANCH}")
|
set(GIT_BRANCH "${GITHUB_BRANCH}")
|
||||||
elseif ("${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_REF}" STREQUAL "")
|
elseif ("${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_REF}" STREQUAL "")
|
||||||
set(GIT_BRANCH "${GITHUB_REF}")
|
set(GIT_BRANCH "${GITHUB_REF}")
|
||||||
else()
|
elseif("${GIT_BRANCH}" STREQUAL "")
|
||||||
message("couldn't find branch")
|
message("couldn't find branch")
|
||||||
set(GIT_BRANCH "detached-head")
|
set(GIT_BRANCH "detached-head")
|
||||||
endif()
|
endif()
|
||||||
|
@ -186,8 +187,8 @@ else()
|
||||||
string(FIND "${GIT_REMOTE_NAME}" "/" INDEX)
|
string(FIND "${GIT_REMOTE_NAME}" "/" INDEX)
|
||||||
if (INDEX GREATER -1)
|
if (INDEX GREATER -1)
|
||||||
string(SUBSTRING "${GIT_REMOTE_NAME}" 0 "${INDEX}" GIT_REMOTE_NAME)
|
string(SUBSTRING "${GIT_REMOTE_NAME}" 0 "${INDEX}" GIT_REMOTE_NAME)
|
||||||
else()
|
elseif("${GIT_REMOTE_NAME}" STREQUAL "")
|
||||||
# If no remote is present (only a branch name), default to origin
|
message("reset to origin")
|
||||||
set(GIT_REMOTE_NAME "origin")
|
set(GIT_REMOTE_NAME "origin")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
@ -202,7 +203,7 @@ execute_process(
|
||||||
|
|
||||||
# Set Version
|
# Set Version
|
||||||
set(EMULATOR_VERSION_MAJOR "0")
|
set(EMULATOR_VERSION_MAJOR "0")
|
||||||
set(EMULATOR_VERSION_MINOR "8")
|
set(EMULATOR_VERSION_MINOR "9")
|
||||||
set(EMULATOR_VERSION_PATCH "1")
|
set(EMULATOR_VERSION_PATCH "1")
|
||||||
|
|
||||||
set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}")
|
set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}")
|
||||||
|
@ -226,7 +227,7 @@ find_package(SDL3 3.1.2 CONFIG)
|
||||||
find_package(stb MODULE)
|
find_package(stb MODULE)
|
||||||
find_package(toml11 4.2.0 CONFIG)
|
find_package(toml11 4.2.0 CONFIG)
|
||||||
find_package(tsl-robin-map 1.3.0 CONFIG)
|
find_package(tsl-robin-map 1.3.0 CONFIG)
|
||||||
find_package(VulkanHeaders 1.4.309 CONFIG)
|
find_package(VulkanHeaders 1.4.314 CONFIG)
|
||||||
find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
|
find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
|
||||||
find_package(xbyak 7.07 CONFIG)
|
find_package(xbyak 7.07 CONFIG)
|
||||||
find_package(xxHash 0.8.2 MODULE)
|
find_package(xxHash 0.8.2 MODULE)
|
||||||
|
@ -239,13 +240,6 @@ if (APPLE)
|
||||||
endif()
|
endif()
|
||||||
list(POP_BACK CMAKE_MODULE_PATH)
|
list(POP_BACK CMAKE_MODULE_PATH)
|
||||||
|
|
||||||
# Note: Windows always has these functions through winpthreads
|
|
||||||
include(CheckSymbolExists)
|
|
||||||
check_symbol_exists(pthread_mutex_timedlock "pthread.h" HAVE_PTHREAD_MUTEX_TIMEDLOCK)
|
|
||||||
if(HAVE_PTHREAD_MUTEX_TIMEDLOCK OR WIN32)
|
|
||||||
add_compile_options(-DHAVE_PTHREAD_MUTEX_TIMEDLOCK)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||||
# libc++ requires -fexperimental-library to enable std::jthread and std::stop_token support.
|
# libc++ requires -fexperimental-library to enable std::jthread and std::stop_token support.
|
||||||
include(CheckCXXSymbolExists)
|
include(CheckCXXSymbolExists)
|
||||||
|
@ -416,6 +410,7 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
|
||||||
src/core/libraries/save_data/save_memory.h
|
src/core/libraries/save_data/save_memory.h
|
||||||
src/core/libraries/save_data/savedata.cpp
|
src/core/libraries/save_data/savedata.cpp
|
||||||
src/core/libraries/save_data/savedata.h
|
src/core/libraries/save_data/savedata.h
|
||||||
|
src/core/libraries/save_data/savedata_error.h
|
||||||
src/core/libraries/save_data/dialog/savedatadialog.cpp
|
src/core/libraries/save_data/dialog/savedatadialog.cpp
|
||||||
src/core/libraries/save_data/dialog/savedatadialog.h
|
src/core/libraries/save_data/dialog/savedatadialog.h
|
||||||
src/core/libraries/save_data/dialog/savedatadialog_ui.cpp
|
src/core/libraries/save_data/dialog/savedatadialog_ui.cpp
|
||||||
|
@ -610,6 +605,10 @@ set(CAMERA_LIBS src/core/libraries/camera/camera.cpp
|
||||||
src/core/libraries/camera/camera_error.h
|
src/core/libraries/camera/camera_error.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(COMPANION_LIBS src/core/libraries/companion/companion_httpd.cpp
|
||||||
|
src/core/libraries/companion/companion_httpd.h
|
||||||
|
src/core/libraries/companion/companion_error.h
|
||||||
|
)
|
||||||
set(DEV_TOOLS src/core/devtools/layer.cpp
|
set(DEV_TOOLS src/core/devtools/layer.cpp
|
||||||
src/core/devtools/layer.h
|
src/core/devtools/layer.h
|
||||||
src/core/devtools/options.cpp
|
src/core/devtools/options.cpp
|
||||||
|
@ -627,6 +626,8 @@ set(DEV_TOOLS src/core/devtools/layer.cpp
|
||||||
src/core/devtools/widget/imgui_memory_editor.h
|
src/core/devtools/widget/imgui_memory_editor.h
|
||||||
src/core/devtools/widget/memory_map.cpp
|
src/core/devtools/widget/memory_map.cpp
|
||||||
src/core/devtools/widget/memory_map.h
|
src/core/devtools/widget/memory_map.h
|
||||||
|
src/core/devtools/widget/module_list.cpp
|
||||||
|
src/core/devtools/widget/module_list.h
|
||||||
src/core/devtools/widget/reg_popup.cpp
|
src/core/devtools/widget/reg_popup.cpp
|
||||||
src/core/devtools/widget/reg_popup.h
|
src/core/devtools/widget/reg_popup.h
|
||||||
src/core/devtools/widget/reg_view.cpp
|
src/core/devtools/widget/reg_view.cpp
|
||||||
|
@ -679,6 +680,8 @@ set(COMMON src/common/logging/backend.cpp
|
||||||
src/common/polyfill_thread.h
|
src/common/polyfill_thread.h
|
||||||
src/common/rdtsc.cpp
|
src/common/rdtsc.cpp
|
||||||
src/common/rdtsc.h
|
src/common/rdtsc.h
|
||||||
|
src/common/recursive_lock.cpp
|
||||||
|
src/common/recursive_lock.h
|
||||||
src/common/sha1.h
|
src/common/sha1.h
|
||||||
src/common/signal_context.h
|
src/common/signal_context.h
|
||||||
src/common/signal_context.cpp
|
src/common/signal_context.cpp
|
||||||
|
@ -774,6 +777,7 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||||
${VDEC_LIB}
|
${VDEC_LIB}
|
||||||
${VR_LIBS}
|
${VR_LIBS}
|
||||||
${CAMERA_LIBS}
|
${CAMERA_LIBS}
|
||||||
|
${COMPANION_LIBS}
|
||||||
${DEV_TOOLS}
|
${DEV_TOOLS}
|
||||||
src/core/debug_state.cpp
|
src/core/debug_state.cpp
|
||||||
src/core/debug_state.h
|
src/core/debug_state.h
|
||||||
|
@ -868,6 +872,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
|
||||||
src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp
|
src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp
|
||||||
src/shader_recompiler/ir/passes/shared_memory_to_storage_pass.cpp
|
src/shader_recompiler/ir/passes/shared_memory_to_storage_pass.cpp
|
||||||
src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp
|
src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp
|
||||||
|
src/shader_recompiler/ir/abstract_syntax_list.cpp
|
||||||
src/shader_recompiler/ir/abstract_syntax_list.h
|
src/shader_recompiler/ir/abstract_syntax_list.h
|
||||||
src/shader_recompiler/ir/attribute.cpp
|
src/shader_recompiler/ir/attribute.cpp
|
||||||
src/shader_recompiler/ir/attribute.h
|
src/shader_recompiler/ir/attribute.h
|
||||||
|
@ -1160,7 +1165,7 @@ if (ENABLE_QT_GUI)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_link_libraries(shadps4 PRIVATE mincore winpthreads)
|
target_link_libraries(shadps4 PRIVATE mincore)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
# MSVC likes putting opinions on what people can use, disable:
|
# MSVC likes putting opinions on what people can use, disable:
|
||||||
|
|
5
dist/net.shadps4.shadPS4.metainfo.xml
vendored
5
dist/net.shadps4.shadPS4.metainfo.xml
vendored
|
@ -37,7 +37,10 @@
|
||||||
<category translate="no">Game</category>
|
<category translate="no">Game</category>
|
||||||
</categories>
|
</categories>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="0.8.0" date="2025-05-23">
|
<release version="0.9.0" date="2025-05-22">
|
||||||
|
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.9.0</url>
|
||||||
|
</release>
|
||||||
|
<release version="0.8.0" date="2025-04-23">
|
||||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.8.0</url>
|
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.8.0</url>
|
||||||
</release>
|
</release>
|
||||||
<release version="0.7.0" date="2025-03-23">
|
<release version="0.7.0" date="2025-03-23">
|
||||||
|
|
|
@ -21,9 +21,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
- A processor with at least 4 cores and 6 threads
|
- A processor with at least 4 cores and 6 threads
|
||||||
- Above 2.5 GHz frequency
|
- Above 2.5 GHz frequency
|
||||||
- A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM
|
- A CPU supporting the x86-64-v3 baseline.
|
||||||
- **Intel**: Haswell generation or newer
|
- **Intel**: Haswell generation or newer
|
||||||
- **AMD**: Jaguar generation or newer
|
- **AMD**: Excavator generation or newer
|
||||||
- **Apple**: Rosetta 2 on macOS 15.4 or newer
|
- **Apple**: Rosetta 2 on macOS 15.4 or newer
|
||||||
|
|
||||||
### GPU
|
### GPU
|
||||||
|
|
6
externals/CMakeLists.txt
vendored
6
externals/CMakeLists.txt
vendored
|
@ -137,12 +137,6 @@ if (NOT TARGET Zydis::Zydis)
|
||||||
add_subdirectory(zydis)
|
add_subdirectory(zydis)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Winpthreads
|
|
||||||
if (WIN32)
|
|
||||||
add_subdirectory(winpthreads)
|
|
||||||
target_include_directories(winpthreads INTERFACE winpthreads/include)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# sirit
|
# sirit
|
||||||
add_subdirectory(sirit)
|
add_subdirectory(sirit)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
|
2
externals/MoltenVK/MoltenVK
vendored
2
externals/MoltenVK/MoltenVK
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 87a8e8b13d4ad8835367fea1ebad1896d0460946
|
Subproject commit 3a0b07a24a4a681ffe70b461b1f4333b2729e2ef
|
2
externals/MoltenVK/SPIRV-Cross
vendored
2
externals/MoltenVK/SPIRV-Cross
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 7918775748c5e2f5c40d9918ce68825035b5a1e1
|
Subproject commit 969e75f7cc0718774231d029f9d52fa87d4ae1b2
|
2
externals/sirit
vendored
2
externals/sirit
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 09a1416ab1b59ddfebd2618412f118f2004f3b2c
|
Subproject commit 6b450704f6fedb9413d0c89a9eb59d028eb1e6c0
|
2
externals/vulkan-headers
vendored
2
externals/vulkan-headers
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 5ceb9ed481e58e705d0d9b5326537daedd06b97d
|
Subproject commit 9c77de5c3dd216f28e407eec65ed9c0a296c1f74
|
1
externals/winpthreads
vendored
1
externals/winpthreads
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit f35b0948d36a736e6a2d052ae295a3ffde09703f
|
|
|
@ -154,7 +154,7 @@ bool GetLoadGameSizeEnabled() {
|
||||||
|
|
||||||
std::filesystem::path GetSaveDataPath() {
|
std::filesystem::path GetSaveDataPath() {
|
||||||
if (save_data_path.empty()) {
|
if (save_data_path.empty()) {
|
||||||
return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir);
|
return Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "savedata";
|
||||||
}
|
}
|
||||||
return save_data_path;
|
return save_data_path;
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ class ElfInfo {
|
||||||
PSFAttributes psf_attributes{};
|
PSFAttributes psf_attributes{};
|
||||||
|
|
||||||
std::filesystem::path splash_path{};
|
std::filesystem::path splash_path{};
|
||||||
|
std::filesystem::path game_folder{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr u32 FW_15 = 0x1500000;
|
static constexpr u32 FW_15 = 0x1500000;
|
||||||
|
@ -123,6 +124,10 @@ public:
|
||||||
[[nodiscard]] const std::filesystem::path& GetSplashPath() const {
|
[[nodiscard]] const std::filesystem::path& GetSplashPath() const {
|
||||||
return splash_path;
|
return splash_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const std::filesystem::path& GetGameFolder() const {
|
||||||
|
return game_folder;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -139,6 +139,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||||
SUB(Lib, Hmd) \
|
SUB(Lib, Hmd) \
|
||||||
SUB(Lib, SigninDialog) \
|
SUB(Lib, SigninDialog) \
|
||||||
SUB(Lib, Camera) \
|
SUB(Lib, Camera) \
|
||||||
|
SUB(Lib, CompanionHttpd) \
|
||||||
CLS(Frontend) \
|
CLS(Frontend) \
|
||||||
CLS(Render) \
|
CLS(Render) \
|
||||||
SUB(Render, Vulkan) \
|
SUB(Render, Vulkan) \
|
||||||
|
|
|
@ -106,6 +106,7 @@ enum class Class : u8 {
|
||||||
Lib_Hmd, ///< The LibSceHmd implementation.
|
Lib_Hmd, ///< The LibSceHmd implementation.
|
||||||
Lib_SigninDialog, ///< The LibSigninDialog implementation.
|
Lib_SigninDialog, ///< The LibSigninDialog implementation.
|
||||||
Lib_Camera, ///< The LibCamera implementation.
|
Lib_Camera, ///< The LibCamera implementation.
|
||||||
|
Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
|
||||||
Frontend, ///< Emulator UI
|
Frontend, ///< Emulator UI
|
||||||
Render, ///< Video Core
|
Render, ///< Video Core
|
||||||
Render_Vulkan, ///< Vulkan backend
|
Render_Vulkan, ///< Vulkan backend
|
||||||
|
|
|
@ -128,7 +128,6 @@ static auto UserPaths = [] {
|
||||||
create_path(PathType::LogDir, user_dir / LOG_DIR);
|
create_path(PathType::LogDir, user_dir / LOG_DIR);
|
||||||
create_path(PathType::ScreenshotsDir, user_dir / SCREENSHOTS_DIR);
|
create_path(PathType::ScreenshotsDir, user_dir / SCREENSHOTS_DIR);
|
||||||
create_path(PathType::ShaderDir, user_dir / SHADER_DIR);
|
create_path(PathType::ShaderDir, user_dir / SHADER_DIR);
|
||||||
create_path(PathType::SaveDataDir, user_dir / SAVEDATA_DIR);
|
|
||||||
create_path(PathType::GameDataDir, user_dir / GAMEDATA_DIR);
|
create_path(PathType::GameDataDir, user_dir / GAMEDATA_DIR);
|
||||||
create_path(PathType::TempDataDir, user_dir / TEMPDATA_DIR);
|
create_path(PathType::TempDataDir, user_dir / TEMPDATA_DIR);
|
||||||
create_path(PathType::SysModuleDir, user_dir / SYSMODULES_DIR);
|
create_path(PathType::SysModuleDir, user_dir / SYSMODULES_DIR);
|
||||||
|
|
|
@ -18,7 +18,6 @@ enum class PathType {
|
||||||
LogDir, // Where log files are stored.
|
LogDir, // Where log files are stored.
|
||||||
ScreenshotsDir, // Where screenshots are stored.
|
ScreenshotsDir, // Where screenshots are stored.
|
||||||
ShaderDir, // Where shaders are stored.
|
ShaderDir, // Where shaders are stored.
|
||||||
SaveDataDir, // Where guest save data is stored.
|
|
||||||
TempDataDir, // Where game temp data is stored.
|
TempDataDir, // Where game temp data is stored.
|
||||||
GameDataDir, // Where game data is stored.
|
GameDataDir, // Where game data is stored.
|
||||||
SysModuleDir, // Where system modules are stored.
|
SysModuleDir, // Where system modules are stored.
|
||||||
|
@ -36,7 +35,6 @@ constexpr auto PORTABLE_DIR = "user";
|
||||||
constexpr auto LOG_DIR = "log";
|
constexpr auto LOG_DIR = "log";
|
||||||
constexpr auto SCREENSHOTS_DIR = "screenshots";
|
constexpr auto SCREENSHOTS_DIR = "screenshots";
|
||||||
constexpr auto SHADER_DIR = "shader";
|
constexpr auto SHADER_DIR = "shader";
|
||||||
constexpr auto SAVEDATA_DIR = "savedata";
|
|
||||||
constexpr auto GAMEDATA_DIR = "data";
|
constexpr auto GAMEDATA_DIR = "data";
|
||||||
constexpr auto TEMPDATA_DIR = "temp";
|
constexpr auto TEMPDATA_DIR = "temp";
|
||||||
constexpr auto SYSMODULES_DIR = "sys_modules";
|
constexpr auto SYSMODULES_DIR = "sys_modules";
|
||||||
|
|
37
src/common/recursive_lock.cpp
Normal file
37
src/common/recursive_lock.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/recursive_lock.h"
|
||||||
|
|
||||||
|
namespace Common::Detail {
|
||||||
|
|
||||||
|
struct RecursiveLockState {
|
||||||
|
RecursiveLockType type;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
thread_local std::unordered_map<void*, RecursiveLockState> g_recursive_locks;
|
||||||
|
|
||||||
|
bool IncrementRecursiveLock(void* mutex, RecursiveLockType type) {
|
||||||
|
auto& state = g_recursive_locks[mutex];
|
||||||
|
if (state.count == 0) {
|
||||||
|
ASSERT(state.type == RecursiveLockType::None);
|
||||||
|
state.type = type;
|
||||||
|
}
|
||||||
|
ASSERT(state.type == type);
|
||||||
|
return state.count++ == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DecrementRecursiveLock(void* mutex, RecursiveLockType type) {
|
||||||
|
auto& state = g_recursive_locks[mutex];
|
||||||
|
ASSERT(state.type == type && state.count > 0);
|
||||||
|
if (--state.count == 0) {
|
||||||
|
g_recursive_locks.erase(mutex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common::Detail
|
67
src/common/recursive_lock.h
Normal file
67
src/common/recursive_lock.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
namespace Detail {
|
||||||
|
|
||||||
|
enum class RecursiveLockType { None, Shared, Exclusive };
|
||||||
|
|
||||||
|
bool IncrementRecursiveLock(void* mutex, RecursiveLockType type);
|
||||||
|
bool DecrementRecursiveLock(void* mutex, RecursiveLockType type);
|
||||||
|
|
||||||
|
} // namespace Detail
|
||||||
|
|
||||||
|
template <typename MutexType>
|
||||||
|
class RecursiveScopedLock {
|
||||||
|
public:
|
||||||
|
explicit RecursiveScopedLock(MutexType& mutex) : m_mutex(mutex), m_locked(false) {
|
||||||
|
if (Detail::IncrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Exclusive)) {
|
||||||
|
m_locked = true;
|
||||||
|
m_lock.emplace(m_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~RecursiveScopedLock() {
|
||||||
|
Detail::DecrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Exclusive);
|
||||||
|
if (m_locked) {
|
||||||
|
m_lock.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MutexType& m_mutex;
|
||||||
|
std::optional<std::unique_lock<MutexType>> m_lock;
|
||||||
|
bool m_locked = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename MutexType>
|
||||||
|
class RecursiveSharedLock {
|
||||||
|
public:
|
||||||
|
explicit RecursiveSharedLock(MutexType& mutex) : m_mutex(mutex), m_locked(false) {
|
||||||
|
if (Detail::IncrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Shared)) {
|
||||||
|
m_locked = true;
|
||||||
|
m_lock.emplace(m_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~RecursiveSharedLock() {
|
||||||
|
Detail::DecrementRecursiveLock(&m_mutex, Detail::RecursiveLockType::Shared);
|
||||||
|
if (m_locked) {
|
||||||
|
m_lock.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MutexType& m_mutex;
|
||||||
|
std::optional<std::shared_lock<MutexType>> m_lock;
|
||||||
|
bool m_locked = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Common
|
|
@ -14,6 +14,9 @@ namespace Common {
|
||||||
struct SlotId {
|
struct SlotId {
|
||||||
static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
|
static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
|
||||||
|
|
||||||
|
SlotId() noexcept = default;
|
||||||
|
constexpr SlotId(u32 index) noexcept : index(index) {}
|
||||||
|
|
||||||
constexpr auto operator<=>(const SlotId&) const noexcept = default;
|
constexpr auto operator<=>(const SlotId&) const noexcept = default;
|
||||||
|
|
||||||
constexpr explicit operator bool() const noexcept {
|
constexpr explicit operator bool() const noexcept {
|
||||||
|
@ -28,6 +31,63 @@ class SlotVector {
|
||||||
constexpr static std::size_t InitialCapacity = 2048;
|
constexpr static std::size_t InitialCapacity = 2048;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
template <typename ValueType, typename Pointer, typename Reference>
|
||||||
|
class Iterator {
|
||||||
|
public:
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
using value_type = ValueType;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = Pointer;
|
||||||
|
using reference = Reference;
|
||||||
|
|
||||||
|
Iterator(SlotVector& vector_, SlotId index_) : vector(vector_), slot(index_) {
|
||||||
|
AdvanceToValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
reference operator*() const {
|
||||||
|
return vector[slot];
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer operator->() const {
|
||||||
|
return &vector[slot];
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator& operator++() {
|
||||||
|
++slot.index;
|
||||||
|
AdvanceToValid();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator operator++(int) {
|
||||||
|
Iterator temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Iterator& other) const {
|
||||||
|
return slot == other.slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Iterator& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AdvanceToValid() {
|
||||||
|
while (slot < vector.values_capacity && !vector.ReadStorageBit(slot.index)) {
|
||||||
|
++slot.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotVector& vector;
|
||||||
|
SlotId slot;
|
||||||
|
};
|
||||||
|
|
||||||
|
using iterator = Iterator<T, T*, T&>;
|
||||||
|
using const_iterator = Iterator<const T, const T*, const T&>;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
|
||||||
SlotVector() {
|
SlotVector() {
|
||||||
Reserve(InitialCapacity);
|
Reserve(InitialCapacity);
|
||||||
}
|
}
|
||||||
|
@ -60,7 +120,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
[[nodiscard]] SlotId insert(Args&&... args) noexcept {
|
SlotId insert(Args&&... args) noexcept {
|
||||||
const u32 index = FreeValueIndex();
|
const u32 index = FreeValueIndex();
|
||||||
new (&values[index].object) T(std::forward<Args>(args)...);
|
new (&values[index].object) T(std::forward<Args>(args)...);
|
||||||
SetStorageBit(index);
|
SetStorageBit(index);
|
||||||
|
@ -78,6 +138,54 @@ public:
|
||||||
return values_capacity - free_list.size();
|
return values_capacity - free_list.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iterator begin() noexcept {
|
||||||
|
return iterator(*this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator begin() const noexcept {
|
||||||
|
return const_iterator(*this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cbegin() const noexcept {
|
||||||
|
return begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() noexcept {
|
||||||
|
return iterator(*this, values_capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator end() const noexcept {
|
||||||
|
return const_iterator(*this, values_capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cend() const noexcept {
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_iterator rbegin() noexcept {
|
||||||
|
return reverse_iterator(end());
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reverse_iterator rbegin() const noexcept {
|
||||||
|
return const_reverse_iterator(end());
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reverse_iterator crbegin() const noexcept {
|
||||||
|
return rbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_iterator rend() noexcept {
|
||||||
|
return reverse_iterator(begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reverse_iterator rend() const noexcept {
|
||||||
|
return const_reverse_iterator(begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reverse_iterator crend() const noexcept {
|
||||||
|
return rend();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct NonTrivialDummy {
|
struct NonTrivialDummy {
|
||||||
NonTrivialDummy() noexcept {}
|
NonTrivialDummy() noexcept {}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
@ -104,14 +105,24 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||||
SetThreadPriority(handle, windows_priority);
|
SetThreadPriority(handle, windows_priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AccurateSleep(std::chrono::nanoseconds duration) {
|
bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
|
||||||
|
const bool interruptible) {
|
||||||
|
const auto begin_sleep = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
LARGE_INTEGER interval{
|
LARGE_INTEGER interval{
|
||||||
.QuadPart = -1 * (duration.count() / 100u),
|
.QuadPart = -1 * (duration.count() / 100u),
|
||||||
};
|
};
|
||||||
HANDLE timer = ::CreateWaitableTimer(NULL, TRUE, NULL);
|
HANDLE timer = ::CreateWaitableTimer(NULL, TRUE, NULL);
|
||||||
SetWaitableTimer(timer, &interval, 0, NULL, NULL, 0);
|
SetWaitableTimer(timer, &interval, 0, NULL, NULL, 0);
|
||||||
WaitForSingleObject(timer, INFINITE);
|
const auto ret = WaitForSingleObjectEx(timer, INFINITE, interruptible);
|
||||||
::CloseHandle(timer);
|
::CloseHandle(timer);
|
||||||
|
|
||||||
|
if (remaining) {
|
||||||
|
const auto end_sleep = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto sleep_time = end_sleep - begin_sleep;
|
||||||
|
*remaining = duration > sleep_time ? duration - sleep_time : std::chrono::nanoseconds(0);
|
||||||
|
}
|
||||||
|
return ret == WAIT_OBJECT_0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
@ -134,8 +145,24 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||||
pthread_setschedparam(this_thread, scheduling_type, ¶ms);
|
pthread_setschedparam(this_thread, scheduling_type, ¶ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AccurateSleep(std::chrono::nanoseconds duration) {
|
bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
|
||||||
std::this_thread::sleep_for(duration);
|
const bool interruptible) {
|
||||||
|
timespec request = {
|
||||||
|
.tv_sec = duration.count() / 1'000'000'000,
|
||||||
|
.tv_nsec = duration.count() % 1'000'000'000,
|
||||||
|
};
|
||||||
|
timespec remain;
|
||||||
|
int ret;
|
||||||
|
while ((ret = nanosleep(&request, &remain)) < 0 && errno == EINTR) {
|
||||||
|
if (interruptible) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
request = remain;
|
||||||
|
}
|
||||||
|
if (remaining) {
|
||||||
|
*remaining = std::chrono::nanoseconds(remain.tv_sec * 1'000'000'000 + remain.tv_nsec);
|
||||||
|
}
|
||||||
|
return ret == 0 || errno != EINTR;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -196,9 +223,9 @@ AccurateTimer::AccurateTimer(std::chrono::nanoseconds target_interval)
|
||||||
: target_interval(target_interval) {}
|
: target_interval(target_interval) {}
|
||||||
|
|
||||||
void AccurateTimer::Start() {
|
void AccurateTimer::Start() {
|
||||||
auto begin_sleep = std::chrono::high_resolution_clock::now();
|
const auto begin_sleep = std::chrono::high_resolution_clock::now();
|
||||||
if (total_wait.count() > 0) {
|
if (total_wait.count() > 0) {
|
||||||
AccurateSleep(total_wait);
|
AccurateSleep(total_wait, nullptr, false);
|
||||||
}
|
}
|
||||||
start_time = std::chrono::high_resolution_clock::now();
|
start_time = std::chrono::high_resolution_clock::now();
|
||||||
total_wait -= std::chrono::duration_cast<std::chrono::nanoseconds>(start_time - begin_sleep);
|
total_wait -= std::chrono::duration_cast<std::chrono::nanoseconds>(start_time - begin_sleep);
|
||||||
|
|
|
@ -25,6 +25,9 @@ void SetCurrentThreadName(const char* name);
|
||||||
|
|
||||||
void SetThreadName(void* thread, const char* name);
|
void SetThreadName(void* thread, const char* name);
|
||||||
|
|
||||||
|
bool AccurateSleep(std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
|
||||||
|
bool interruptible);
|
||||||
|
|
||||||
class AccurateTimer {
|
class AccurateTimer {
|
||||||
std::chrono::nanoseconds target_interval{};
|
std::chrono::nanoseconds target_interval{};
|
||||||
std::chrono::nanoseconds total_wait{};
|
std::chrono::nanoseconds total_wait{};
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "widget/frame_dump.h"
|
#include "widget/frame_dump.h"
|
||||||
#include "widget/frame_graph.h"
|
#include "widget/frame_graph.h"
|
||||||
#include "widget/memory_map.h"
|
#include "widget/memory_map.h"
|
||||||
|
#include "widget/module_list.h"
|
||||||
#include "widget/shader_list.h"
|
#include "widget/shader_list.h"
|
||||||
|
|
||||||
extern std::unique_ptr<Vulkan::Presenter> presenter;
|
extern std::unique_ptr<Vulkan::Presenter> presenter;
|
||||||
|
@ -40,6 +41,7 @@ static bool just_opened_options = false;
|
||||||
|
|
||||||
static Widget::MemoryMapViewer memory_map;
|
static Widget::MemoryMapViewer memory_map;
|
||||||
static Widget::ShaderList shader_list;
|
static Widget::ShaderList shader_list;
|
||||||
|
static Widget::ModuleList module_list;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static std::string help_text =
|
static std::string help_text =
|
||||||
|
@ -108,6 +110,9 @@ void L::DrawMenuBar() {
|
||||||
if (MenuItem("Memory map")) {
|
if (MenuItem("Memory map")) {
|
||||||
memory_map.open = true;
|
memory_map.open = true;
|
||||||
}
|
}
|
||||||
|
if (MenuItem("Module list")) {
|
||||||
|
module_list.open = true;
|
||||||
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,6 +261,9 @@ void L::DrawAdvanced() {
|
||||||
if (shader_list.open) {
|
if (shader_list.open) {
|
||||||
shader_list.Draw();
|
shader_list.Draw();
|
||||||
}
|
}
|
||||||
|
if (module_list.open) {
|
||||||
|
module_list.Draw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void L::DrawSimple() {
|
void L::DrawSimple() {
|
||||||
|
|
55
src/core/devtools/widget/module_list.cpp
Normal file
55
src/core/devtools/widget/module_list.cpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "module_list.h"
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "core/debug_state.h"
|
||||||
|
#include "imgui/imgui_std.h"
|
||||||
|
|
||||||
|
using namespace ImGui;
|
||||||
|
|
||||||
|
namespace Core::Devtools::Widget {
|
||||||
|
void ModuleList::Draw() {
|
||||||
|
SetNextWindowSize({550.0f, 600.0f}, ImGuiCond_FirstUseEver);
|
||||||
|
if (!Begin("Module List", &open)) {
|
||||||
|
End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BeginTable("ModuleTable", 3,
|
||||||
|
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
|
||||||
|
ImGuiTableFlags_RowBg)) {
|
||||||
|
TableSetupColumn("Modulname", ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
TableHeadersRow();
|
||||||
|
|
||||||
|
std::scoped_lock lock(modules_mutex);
|
||||||
|
for (const auto& module : modules) {
|
||||||
|
TableNextRow();
|
||||||
|
|
||||||
|
TableSetColumnIndex(0);
|
||||||
|
TextUnformatted(module.name.c_str());
|
||||||
|
|
||||||
|
TableSetColumnIndex(1);
|
||||||
|
if (module.is_sys_module) {
|
||||||
|
TextColored({0.2f, 0.6f, 0.8f, 1.0f}, "System Module");
|
||||||
|
} else {
|
||||||
|
TextColored({0.8f, 0.4f, 0.2f, 1.0f}, "Game Module");
|
||||||
|
}
|
||||||
|
|
||||||
|
TableSetColumnIndex(2);
|
||||||
|
if (module.is_lle) {
|
||||||
|
TextColored({0.4f, 0.7f, 0.4f, 1.0f}, "LLE");
|
||||||
|
} else {
|
||||||
|
TextColored({0.7f, 0.4f, 0.5f, 1.0f}, "HLE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
End();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core::Devtools::Widget
|
82
src/core/devtools/widget/module_list.h
Normal file
82
src/core/devtools/widget/module_list.h
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "common/elf_info.h"
|
||||||
|
#include "common/path_util.h"
|
||||||
|
|
||||||
|
namespace Core::Devtools::Widget {
|
||||||
|
|
||||||
|
class ModuleList {
|
||||||
|
public:
|
||||||
|
ModuleList() = default;
|
||||||
|
~ModuleList() = default;
|
||||||
|
|
||||||
|
void Draw();
|
||||||
|
bool open = false;
|
||||||
|
|
||||||
|
static bool IsSystemModule(const std::filesystem::path& path) {
|
||||||
|
const auto sys_modules_path = Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir);
|
||||||
|
|
||||||
|
const auto abs_path = std::filesystem::absolute(path).lexically_normal();
|
||||||
|
const auto abs_sys_path = std::filesystem::absolute(sys_modules_path).lexically_normal();
|
||||||
|
|
||||||
|
const auto path_str = abs_path.string();
|
||||||
|
const auto sys_path_str = abs_sys_path.string();
|
||||||
|
|
||||||
|
return path_str.starts_with(sys_path_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsSystemModule(const std::string& name) {
|
||||||
|
const auto game_modules_path = Common::ElfInfo::Instance().GetGameFolder() / "sce_module";
|
||||||
|
const auto prx_path = game_modules_path / name;
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(prx_path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AddModule(const std::string& name, std::filesystem::path path) {
|
||||||
|
if (name == "eboot.bin") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::scoped_lock lock(modules_mutex);
|
||||||
|
modules.push_back({name, IsSystemModule(path), true});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AddModule(std::string name) {
|
||||||
|
name = name + ".prx";
|
||||||
|
std::scoped_lock lock(modules_mutex);
|
||||||
|
|
||||||
|
bool is_sys_module = IsSystemModule(name);
|
||||||
|
bool is_lle = false;
|
||||||
|
auto it = std::find_if(modules.begin(), modules.end(),
|
||||||
|
[&name, is_sys_module, is_lle](const ModuleInfo& entry) {
|
||||||
|
return entry.name == name && !entry.is_lle;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it == modules.end()) {
|
||||||
|
modules.push_back({name, is_sys_module, is_lle});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ModuleInfo {
|
||||||
|
std::string name;
|
||||||
|
bool is_sys_module;
|
||||||
|
bool is_lle;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline std::mutex modules_mutex;
|
||||||
|
|
||||||
|
static inline std::vector<ModuleInfo> modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Core::Devtools::Widget
|
20
src/core/libraries/companion/companion_error.h
Normal file
20
src/core/libraries/companion/companion_error.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// companion_httpd error codes
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_UNKNOWN = 0x80E40001;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_FATAL = 0x80E40002;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOMEM = 0x80E40003;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_INVALID_PARAM = 0x80E40004;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_INVALID_OPERATION = 0x80E40005;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_INITIALIZED = 0x80E40006;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_ALREADY_INITIALIZED = 0x80E40007;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_NO_EVENT = 0x80E40008;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_GENERATE_RESPONSE = 0x80E40009;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_ALREADY_STARTED = 0x80E4000A;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_STARTED = 0x80E4000B;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_ALREADY_REGISTERED = 0x80E4000;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_CONNECTED = 0x80E4000D;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_ERROR_USER_NOT_FOUND = 0x80E4000E;
|
142
src/core/libraries/companion/companion_httpd.cpp
Normal file
142
src/core/libraries/companion/companion_httpd.cpp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "companion_error.h"
|
||||||
|
#include "core/libraries/companion/companion_httpd.h"
|
||||||
|
#include "core/libraries/error_codes.h"
|
||||||
|
#include "core/libraries/libs.h"
|
||||||
|
|
||||||
|
namespace Libraries::CompanionHttpd {
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdAddHeader(const char* key, const char* value,
|
||||||
|
OrbisCompanionHttpdResponse* response) {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI
|
||||||
|
sceCompanionHttpdGet2ndScreenStatus(Libraries::UserService::OrbisUserServiceUserId) {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdGetEvent(OrbisCompanionHttpdEvent* pEvent) {
|
||||||
|
pEvent->event = ORBIS_COMPANION_HTTPD_EVENT_DISCONNECT; // disconnected
|
||||||
|
LOG_DEBUG(Lib_CompanionHttpd, "device disconnected");
|
||||||
|
return ORBIS_COMPANION_HTTPD_ERROR_NO_EVENT; // No events to obtain
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI
|
||||||
|
sceCompanionHttpdGetUserId(u32 addr, Libraries::UserService::OrbisUserServiceUserId* userId) {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdInitialize() {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdInitialize2() {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdOptParamInitialize() {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdRegisterRequestBodyReceptionCallback(
|
||||||
|
OrbisCompanionHttpdRequestBodyReceptionCallback function, void* param) {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI
|
||||||
|
sceCompanionHttpdRegisterRequestCallback(OrbisCompanionHttpdRequestCallback function, void* param) {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdRegisterRequestCallback2(
|
||||||
|
OrbisCompanionHttpdRequestCallback function, void* param) {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdSetBody(const char* body, u64 bodySize,
|
||||||
|
OrbisCompanionHttpdResponse* response) {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdSetStatus(s32 status, OrbisCompanionHttpdResponse* response) {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdStart() {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdStop() {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdTerminate() {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdUnregisterRequestBodyReceptionCallback() {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdUnregisterRequestCallback() {
|
||||||
|
LOG_ERROR(Lib_CompanionHttpd, "(STUBBED) called");
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterlibSceCompanionHttpd(Core::Loader::SymbolsResolver* sym) {
|
||||||
|
LIB_FUNCTION("8pWltDG7h6A", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdAddHeader);
|
||||||
|
LIB_FUNCTION("B-QBMeFdNgY", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdGet2ndScreenStatus);
|
||||||
|
LIB_FUNCTION("Vku4big+IYM", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdGetEvent);
|
||||||
|
LIB_FUNCTION("0SySxcuVNG0", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdGetUserId);
|
||||||
|
LIB_FUNCTION("ykNpWs3ktLY", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdInitialize);
|
||||||
|
LIB_FUNCTION("OA6FbORefbo", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdInitialize2);
|
||||||
|
LIB_FUNCTION("r-2-a0c7Kfc", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdOptParamInitialize);
|
||||||
|
LIB_FUNCTION("fHNmij7kAUM", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdRegisterRequestBodyReceptionCallback);
|
||||||
|
LIB_FUNCTION("OaWw+IVEdbI", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdRegisterRequestCallback);
|
||||||
|
LIB_FUNCTION("-0c9TCTwnGs", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdRegisterRequestCallback2);
|
||||||
|
LIB_FUNCTION("h3OvVxzX4qM", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdSetBody);
|
||||||
|
LIB_FUNCTION("w7oz0AWHpT4", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdSetStatus);
|
||||||
|
LIB_FUNCTION("k7F0FcDM-Xc", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdStart);
|
||||||
|
LIB_FUNCTION("0SCgzfVQHpo", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdStop);
|
||||||
|
LIB_FUNCTION("+-du9tWgE9s", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdTerminate);
|
||||||
|
LIB_FUNCTION("ZSHiUfYK+QI", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdUnregisterRequestBodyReceptionCallback);
|
||||||
|
LIB_FUNCTION("xweOi2QT-BE", "libSceCompanionHttpd", 1, "libSceCompanionHttpd", 1, 1,
|
||||||
|
sceCompanionHttpdUnregisterRequestCallback);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Libraries::CompanionHttpd
|
91
src/core/libraries/companion/companion_httpd.h
Normal file
91
src/core/libraries/companion/companion_httpd.h
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/types.h"
|
||||||
|
#include "core/libraries/network/net.h"
|
||||||
|
#include "core/libraries/system/userservice.h"
|
||||||
|
|
||||||
|
namespace Core::Loader {
|
||||||
|
class SymbolsResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Libraries::CompanionHttpd {
|
||||||
|
|
||||||
|
// OrbisCompanionHttpdEvent event codes
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_EVENT_CONNECT = 0x10000001;
|
||||||
|
constexpr int ORBIS_COMPANION_HTTPD_EVENT_DISCONNECT = 0x10000002;
|
||||||
|
|
||||||
|
struct OrbisCompanionHttpdHeader {
|
||||||
|
char* key;
|
||||||
|
char* value;
|
||||||
|
struct OrbisCompanionHttpdHeader* header;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisCompanionHttpdRequest {
|
||||||
|
s32 method;
|
||||||
|
char* url;
|
||||||
|
OrbisCompanionHttpdHeader* header;
|
||||||
|
char* body;
|
||||||
|
u64 bodySize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisCompanionHttpdResponse {
|
||||||
|
s32 status;
|
||||||
|
OrbisCompanionHttpdHeader* header;
|
||||||
|
char* body;
|
||||||
|
u64 bodySize;
|
||||||
|
};
|
||||||
|
|
||||||
|
using OrbisCompanionHttpdRequestBodyReceptionCallback =
|
||||||
|
PS4_SYSV_ABI s32 (*)(s32 event, Libraries::UserService::OrbisUserServiceUserId userId,
|
||||||
|
const OrbisCompanionHttpdRequest* httpRequest, void* param);
|
||||||
|
|
||||||
|
using OrbisCompanionHttpdRequestCallback =
|
||||||
|
PS4_SYSV_ABI s32 (*)(Libraries::UserService::OrbisUserServiceUserId userId,
|
||||||
|
const OrbisCompanionHttpdRequest* httpRequest,
|
||||||
|
OrbisCompanionHttpdResponse* httpResponse, void* param);
|
||||||
|
|
||||||
|
struct OrbisCompanionUtilDeviceInfo {
|
||||||
|
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||||
|
Libraries::Net::OrbisNetSockaddrIn addr;
|
||||||
|
char reserved[236];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisCompanionHttpdEvent {
|
||||||
|
s32 event;
|
||||||
|
union {
|
||||||
|
OrbisCompanionUtilDeviceInfo deviceInfo;
|
||||||
|
Libraries::UserService::OrbisUserServiceUserId userId;
|
||||||
|
char reserved[256];
|
||||||
|
} data;
|
||||||
|
};
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdAddHeader(const char* key, const char* value,
|
||||||
|
OrbisCompanionHttpdResponse* response);
|
||||||
|
s32 PS4_SYSV_ABI
|
||||||
|
sceCompanionHttpdGet2ndScreenStatus(Libraries::UserService::OrbisUserServiceUserId userId);
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdGetEvent(OrbisCompanionHttpdEvent* pEvent);
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdGetUserId(u32 addr,
|
||||||
|
Libraries::UserService::OrbisUserServiceUserId* userId);
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdInitialize();
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdInitialize2();
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdOptParamInitialize();
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdRegisterRequestBodyReceptionCallback(
|
||||||
|
OrbisCompanionHttpdRequestBodyReceptionCallback function, void* param);
|
||||||
|
s32 PS4_SYSV_ABI
|
||||||
|
sceCompanionHttpdRegisterRequestCallback(OrbisCompanionHttpdRequestCallback function, void* param);
|
||||||
|
s32 PS4_SYSV_ABI
|
||||||
|
sceCompanionHttpdRegisterRequestCallback2(OrbisCompanionHttpdRequestCallback function, void* param);
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdSetBody(const char* body, u64 bodySize,
|
||||||
|
OrbisCompanionHttpdResponse* response);
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdSetStatus(s32 status, OrbisCompanionHttpdResponse* response);
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdStart();
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdStop();
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdTerminate();
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdUnregisterRequestBodyReceptionCallback();
|
||||||
|
s32 PS4_SYSV_ABI sceCompanionHttpdUnregisterRequestCallback();
|
||||||
|
|
||||||
|
void RegisterlibSceCompanionHttpd(Core::Loader::SymbolsResolver* sym);
|
||||||
|
} // namespace Libraries::CompanionHttpd
|
|
@ -179,7 +179,7 @@ s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t add
|
||||||
auto* wait_reg_mem = reinterpret_cast<PM4CmdWaitRegMem*>(cmdbuf);
|
auto* wait_reg_mem = reinterpret_cast<PM4CmdWaitRegMem*>(cmdbuf);
|
||||||
wait_reg_mem->header = PM4Type3Header{PM4ItOpcode::WaitRegMem, 5};
|
wait_reg_mem->header = PM4Type3Header{PM4ItOpcode::WaitRegMem, 5};
|
||||||
wait_reg_mem->raw = (is_mem << 4u) | (cmp_func & 7u);
|
wait_reg_mem->raw = (is_mem << 4u) | (cmp_func & 7u);
|
||||||
wait_reg_mem->poll_addr_lo = u32(addr & addr_mask);
|
wait_reg_mem->poll_addr_lo_raw = u32(addr & addr_mask);
|
||||||
wait_reg_mem->poll_addr_hi = u32(addr >> 32u);
|
wait_reg_mem->poll_addr_hi = u32(addr >> 32u);
|
||||||
wait_reg_mem->ref = ref;
|
wait_reg_mem->ref = ref;
|
||||||
wait_reg_mem->mask = mask;
|
wait_reg_mem->mask = mask;
|
||||||
|
|
|
@ -12,12 +12,25 @@
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
|
extern boost::asio::io_context io_context;
|
||||||
|
extern void KernelSignalRequest();
|
||||||
|
|
||||||
|
static constexpr auto HrTimerSpinlockThresholdUs = 1200u;
|
||||||
|
|
||||||
// Events are uniquely identified by id and filter.
|
// Events are uniquely identified by id and filter.
|
||||||
|
|
||||||
bool EqueueInternal::AddEvent(EqueueEvent& event) {
|
bool EqueueInternal::AddEvent(EqueueEvent& event) {
|
||||||
std::scoped_lock lock{m_mutex};
|
std::scoped_lock lock{m_mutex};
|
||||||
|
|
||||||
event.time_added = std::chrono::steady_clock::now();
|
event.time_added = std::chrono::steady_clock::now();
|
||||||
|
if (event.event.filter == SceKernelEvent::Filter::Timer ||
|
||||||
|
event.event.filter == SceKernelEvent::Filter::HrTimer) {
|
||||||
|
// HrTimer events are offset by the threshold of time at the end that we spinlock for
|
||||||
|
// greater accuracy.
|
||||||
|
const auto offset =
|
||||||
|
event.event.filter == SceKernelEvent::Filter::HrTimer ? HrTimerSpinlockThresholdUs : 0u;
|
||||||
|
event.timer_interval = std::chrono::microseconds(event.event.data - offset);
|
||||||
|
}
|
||||||
|
|
||||||
const auto& it = std::ranges::find(m_events, event);
|
const auto& it = std::ranges::find(m_events, event);
|
||||||
if (it != m_events.cend()) {
|
if (it != m_events.cend()) {
|
||||||
|
@ -29,6 +42,47 @@ bool EqueueInternal::AddEvent(EqueueEvent& event) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EqueueInternal::ScheduleEvent(u64 id, s16 filter,
|
||||||
|
void (*callback)(SceKernelEqueue, const SceKernelEvent&)) {
|
||||||
|
std::scoped_lock lock{m_mutex};
|
||||||
|
|
||||||
|
const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) {
|
||||||
|
return ev.event.ident == id && ev.event.filter == filter;
|
||||||
|
});
|
||||||
|
if (it == m_events.cend()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& event = *it;
|
||||||
|
ASSERT(event.event.filter == SceKernelEvent::Filter::Timer ||
|
||||||
|
event.event.filter == SceKernelEvent::Filter::HrTimer);
|
||||||
|
|
||||||
|
if (!it->timer) {
|
||||||
|
it->timer = std::make_unique<boost::asio::steady_timer>(io_context, event.timer_interval);
|
||||||
|
} else {
|
||||||
|
// If the timer already exists we are scheduling a reoccurrence after the next period.
|
||||||
|
// Set the expiration time to the previous occurrence plus the period.
|
||||||
|
it->timer->expires_at(it->timer->expiry() + event.timer_interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
it->timer->async_wait(
|
||||||
|
[this, event_data = event.event, callback](const boost::system::error_code& ec) {
|
||||||
|
if (ec) {
|
||||||
|
if (ec != boost::system::errc::operation_canceled) {
|
||||||
|
LOG_ERROR(Kernel_Event, "Timer callback error: {}", ec.message());
|
||||||
|
} else {
|
||||||
|
// Timer was cancelled (removed) before it triggered
|
||||||
|
LOG_DEBUG(Kernel_Event, "Timer cancelled");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(this, event_data);
|
||||||
|
});
|
||||||
|
KernelSignalRequest();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
|
bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
|
||||||
bool has_found = false;
|
bool has_found = false;
|
||||||
std::scoped_lock lock{m_mutex};
|
std::scoped_lock lock{m_mutex};
|
||||||
|
@ -152,18 +206,14 @@ int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern boost::asio::io_context io_context;
|
bool EqueueInternal::EventExists(u64 id, s16 filter) {
|
||||||
extern void KernelSignalRequest();
|
std::scoped_lock lock{m_mutex};
|
||||||
|
|
||||||
static constexpr auto HrTimerSpinlockThresholdUs = 1200u;
|
const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) {
|
||||||
|
return ev.event.ident == id && ev.event.filter == filter;
|
||||||
|
});
|
||||||
|
|
||||||
static void SmallTimerCallback(const boost::system::error_code& error, SceKernelEqueue eq,
|
return it != m_events.cend();
|
||||||
SceKernelEvent kevent) {
|
|
||||||
static EqueueEvent event;
|
|
||||||
event.event = kevent;
|
|
||||||
event.event.data = HrTimerSpinlockThresholdUs;
|
|
||||||
eq->AddSmallTimer(event);
|
|
||||||
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::HrTimer, kevent.udata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) {
|
int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) {
|
||||||
|
@ -243,6 +293,14 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void HrTimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) {
|
||||||
|
static EqueueEvent event;
|
||||||
|
event.event = kevent;
|
||||||
|
event.event.data = HrTimerSpinlockThresholdUs;
|
||||||
|
eq->AddSmallTimer(event);
|
||||||
|
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::HrTimer, kevent.udata);
|
||||||
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata) {
|
s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata) {
|
||||||
if (eq == nullptr) {
|
if (eq == nullptr) {
|
||||||
return ORBIS_KERNEL_ERROR_EBADF;
|
return ORBIS_KERNEL_ERROR_EBADF;
|
||||||
|
@ -273,17 +331,10 @@ s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec*
|
||||||
return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
|
return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
event.timer = std::make_unique<boost::asio::steady_timer>(
|
if (!eq->AddEvent(event) ||
|
||||||
io_context, std::chrono::microseconds(total_us - HrTimerSpinlockThresholdUs));
|
!eq->ScheduleEvent(id, SceKernelEvent::Filter::HrTimer, HrTimerCallback)) {
|
||||||
|
|
||||||
event.timer->async_wait(std::bind(SmallTimerCallback, std::placeholders::_1, eq, event.event));
|
|
||||||
|
|
||||||
if (!eq->AddEvent(event)) {
|
|
||||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
KernelSignalRequest();
|
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,6 +351,57 @@ int PS4_SYSV_ABI sceKernelDeleteHRTimerEvent(SceKernelEqueue eq, int id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void TimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) {
|
||||||
|
if (eq->EventExists(kevent.ident, kevent.filter)) {
|
||||||
|
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::Timer, kevent.udata);
|
||||||
|
|
||||||
|
if (!(kevent.flags & SceKernelEvent::Flags::OneShot)) {
|
||||||
|
// Reschedule the event for its next period.
|
||||||
|
eq->ScheduleEvent(kevent.ident, kevent.filter, TimerCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PS4_SYSV_ABI sceKernelAddTimerEvent(SceKernelEqueue eq, int id, SceKernelUseconds usec,
|
||||||
|
void* udata) {
|
||||||
|
if (eq == nullptr) {
|
||||||
|
return ORBIS_KERNEL_ERROR_EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
EqueueEvent event{};
|
||||||
|
event.event.ident = static_cast<u64>(id);
|
||||||
|
event.event.filter = SceKernelEvent::Filter::Timer;
|
||||||
|
event.event.flags = SceKernelEvent::Flags::Add;
|
||||||
|
event.event.fflags = 0;
|
||||||
|
event.event.data = usec;
|
||||||
|
event.event.udata = udata;
|
||||||
|
|
||||||
|
if (eq->EventExists(event.event.ident, event.event.filter)) {
|
||||||
|
eq->RemoveEvent(id, SceKernelEvent::Filter::Timer);
|
||||||
|
LOG_DEBUG(Kernel_Event,
|
||||||
|
"Timer event already exists, removing it: queue name={}, queue id={}",
|
||||||
|
eq->GetName(), event.event.ident);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(Kernel_Event, "Added timing event: queue name={}, queue id={}, usec={}, pointer={:x}",
|
||||||
|
eq->GetName(), event.event.ident, usec, reinterpret_cast<uintptr_t>(udata));
|
||||||
|
|
||||||
|
if (!eq->AddEvent(event) ||
|
||||||
|
!eq->ScheduleEvent(id, SceKernelEvent::Filter::Timer, TimerCallback)) {
|
||||||
|
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||||
|
}
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PS4_SYSV_ABI sceKernelDeleteTimerEvent(SceKernelEqueue eq, int id) {
|
||||||
|
if (eq == nullptr) {
|
||||||
|
return ORBIS_KERNEL_ERROR_EBADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return eq->RemoveEvent(id, SceKernelEvent::Filter::Timer) ? ORBIS_OK
|
||||||
|
: ORBIS_KERNEL_ERROR_ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) {
|
int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) {
|
||||||
if (eq == nullptr) {
|
if (eq == nullptr) {
|
||||||
return ORBIS_KERNEL_ERROR_EBADF;
|
return ORBIS_KERNEL_ERROR_EBADF;
|
||||||
|
@ -380,6 +482,8 @@ void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge);
|
LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge);
|
||||||
LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent);
|
LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent);
|
||||||
LIB_FUNCTION("J+LF6LwObXU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteHRTimerEvent);
|
LIB_FUNCTION("J+LF6LwObXU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteHRTimerEvent);
|
||||||
|
LIB_FUNCTION("57ZK+ODEXWY", "libkernel", 1, "libkernel", 1, 1, sceKernelAddTimerEvent);
|
||||||
|
LIB_FUNCTION("YWQFUyXIVdU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteTimerEvent);
|
||||||
LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent);
|
LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent);
|
||||||
LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent);
|
LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent);
|
||||||
LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId);
|
LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId);
|
||||||
|
|
|
@ -21,6 +21,9 @@ namespace Libraries::Kernel {
|
||||||
class EqueueInternal;
|
class EqueueInternal;
|
||||||
struct EqueueEvent;
|
struct EqueueEvent;
|
||||||
|
|
||||||
|
using SceKernelUseconds = u32;
|
||||||
|
using SceKernelEqueue = EqueueInternal*;
|
||||||
|
|
||||||
struct SceKernelEvent {
|
struct SceKernelEvent {
|
||||||
enum Filter : s16 {
|
enum Filter : s16 {
|
||||||
None = 0,
|
None = 0,
|
||||||
|
@ -77,6 +80,7 @@ struct EqueueEvent {
|
||||||
SceKernelEvent event;
|
SceKernelEvent event;
|
||||||
void* data = nullptr;
|
void* data = nullptr;
|
||||||
std::chrono::steady_clock::time_point time_added;
|
std::chrono::steady_clock::time_point time_added;
|
||||||
|
std::chrono::microseconds timer_interval;
|
||||||
std::unique_ptr<boost::asio::steady_timer> timer;
|
std::unique_ptr<boost::asio::steady_timer> timer;
|
||||||
|
|
||||||
void ResetTriggerState() {
|
void ResetTriggerState() {
|
||||||
|
@ -133,6 +137,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddEvent(EqueueEvent& event);
|
bool AddEvent(EqueueEvent& event);
|
||||||
|
bool ScheduleEvent(u64 id, s16 filter,
|
||||||
|
void (*callback)(SceKernelEqueue, const SceKernelEvent&));
|
||||||
bool RemoveEvent(u64 id, s16 filter);
|
bool RemoveEvent(u64 id, s16 filter);
|
||||||
int WaitForEvents(SceKernelEvent* ev, int num, u32 micros);
|
int WaitForEvents(SceKernelEvent* ev, int num, u32 micros);
|
||||||
bool TriggerEvent(u64 ident, s16 filter, void* trigger_data);
|
bool TriggerEvent(u64 ident, s16 filter, void* trigger_data);
|
||||||
|
@ -152,6 +158,8 @@ public:
|
||||||
|
|
||||||
int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros);
|
int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros);
|
||||||
|
|
||||||
|
bool EventExists(u64 id, s16 filter);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
|
@ -160,9 +168,6 @@ private:
|
||||||
std::condition_variable m_cond;
|
std::condition_variable m_cond;
|
||||||
};
|
};
|
||||||
|
|
||||||
using SceKernelUseconds = u32;
|
|
||||||
using SceKernelEqueue = EqueueInternal*;
|
|
||||||
|
|
||||||
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev);
|
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev);
|
||||||
|
|
||||||
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym);
|
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym);
|
||||||
|
|
|
@ -108,6 +108,9 @@ void SetPosixErrno(int e) {
|
||||||
case EACCES:
|
case EACCES:
|
||||||
g_posix_errno = POSIX_EACCES;
|
g_posix_errno = POSIX_EACCES;
|
||||||
break;
|
break;
|
||||||
|
case EFAULT:
|
||||||
|
g_posix_errno = POSIX_EFAULT;
|
||||||
|
break;
|
||||||
case EINVAL:
|
case EINVAL:
|
||||||
g_posix_errno = POSIX_EINVAL;
|
g_posix_errno = POSIX_EINVAL;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -209,6 +209,19 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
||||||
"anon");
|
"anon");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelMapDirectMemory2(void** addr, u64 len, s32 type, s32 prot, s32 flags,
|
||||||
|
s64 phys_addr, u64 alignment) {
|
||||||
|
LOG_INFO(Kernel_Vmm, "called, redirected to sceKernelMapNamedDirectMemory");
|
||||||
|
const s32 ret =
|
||||||
|
sceKernelMapNamedDirectMemory(addr, len, prot, flags, phys_addr, alignment, "anon");
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
auto* memory = Core::Memory::Instance();
|
||||||
|
memory->SetDirectMemoryType(phys_addr, type);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
|
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
|
||||||
int flags, const char* name) {
|
int flags, const char* name) {
|
||||||
|
|
||||||
|
@ -290,6 +303,12 @@ int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut
|
||||||
directMemoryEndOut);
|
directMemoryEndOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PS4_SYSV_ABI sceKernelIsStack(void* addr, void** start, void** end) {
|
||||||
|
LOG_DEBUG(Kernel_Vmm, "called, addr = {}", fmt::ptr(addr));
|
||||||
|
auto* memory = Core::Memory::Instance();
|
||||||
|
return memory->IsStack(std::bit_cast<VAddr>(addr), start, end);
|
||||||
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
|
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
|
||||||
int* numEntriesOut) {
|
int* numEntriesOut) {
|
||||||
return sceKernelBatchMap2(entries, numEntries, numEntriesOut,
|
return sceKernelBatchMap2(entries, numEntries, numEntriesOut,
|
||||||
|
@ -636,8 +655,10 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange);
|
LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange);
|
||||||
LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType);
|
LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType);
|
||||||
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize);
|
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize);
|
||||||
|
LIB_FUNCTION("yDBwVAolDgg", "libkernel", 1, "libkernel", 1, 1, sceKernelIsStack);
|
||||||
LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory);
|
LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory);
|
||||||
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory);
|
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory);
|
||||||
|
LIB_FUNCTION("BQQniolj9tQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory2);
|
||||||
LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection);
|
LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection);
|
||||||
LIB_FUNCTION("BHouLQzh0X0", "libkernel", 1, "libkernel", 1, 1, sceKernelDirectMemoryQuery);
|
LIB_FUNCTION("BHouLQzh0X0", "libkernel", 1, "libkernel", 1, 1, sceKernelDirectMemoryQuery);
|
||||||
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
|
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
|
||||||
|
|
|
@ -158,6 +158,7 @@ void PS4_SYSV_ABI _sceKernelRtldSetApplicationHeapAPI(void* func[]);
|
||||||
int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut,
|
int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut,
|
||||||
void** directMemoryStartOut,
|
void** directMemoryStartOut,
|
||||||
void** directMemoryEndOut);
|
void** directMemoryEndOut);
|
||||||
|
int PS4_SYSV_ABI sceKernelIsStack(void* addr, void** start, void** end);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
|
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
|
||||||
int* numEntriesOut);
|
int* numEntriesOut);
|
||||||
|
|
|
@ -315,7 +315,7 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
|
||||||
auto result = ef->Poll(bitPattern, wait, clear, pResultPat);
|
auto result = ef->Poll(bitPattern, wait, clear, pResultPat);
|
||||||
|
|
||||||
if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_EBUSY) {
|
if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_EBUSY) {
|
||||||
LOG_ERROR(Kernel_Event, "returned {}", result);
|
LOG_DEBUG(Kernel_Event, "returned {:#x}", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -361,7 +361,7 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
|
||||||
u32 result = ef->Wait(bitPattern, wait, clear, pResultPat, pTimeout);
|
u32 result = ef->Wait(bitPattern, wait, clear, pResultPat, pTimeout);
|
||||||
|
|
||||||
if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_ETIMEDOUT) {
|
if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_ETIMEDOUT) {
|
||||||
LOG_ERROR(Kernel_Event, "returned {:#x}", result);
|
LOG_DEBUG(Kernel_Event, "returned {:#x}", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -5,24 +5,23 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/native_clock.h"
|
#include "common/native_clock.h"
|
||||||
|
#include "common/thread.h"
|
||||||
#include "core/libraries/kernel/kernel.h"
|
#include "core/libraries/kernel/kernel.h"
|
||||||
#include "core/libraries/kernel/orbis_error.h"
|
#include "core/libraries/kernel/orbis_error.h"
|
||||||
|
#include "core/libraries/kernel/posix_error.h"
|
||||||
#include "core/libraries/kernel/time.h"
|
#include "core/libraries/kernel/time.h"
|
||||||
#include "core/libraries/libs.h"
|
#include "core/libraries/libs.h"
|
||||||
|
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
#include <pthread_time.h>
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include "common/ntapi.h"
|
#include "common/ntapi.h"
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
#include <date/tz.h>
|
#include <date/tz.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <ctime>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -52,88 +51,116 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() {
|
||||||
return clock->GetUptime();
|
return clock->GetUptime();
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
|
static s32 posix_nanosleep_impl(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp,
|
||||||
#ifdef _WIN64
|
const bool interruptible) {
|
||||||
const auto start_time = std::chrono::high_resolution_clock::now();
|
if (!rqtp || rqtp->tv_sec < 0 || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1'000'000'000) {
|
||||||
auto total_wait_time = std::chrono::microseconds(microseconds);
|
SetPosixErrno(EINVAL);
|
||||||
|
return -1;
|
||||||
while (total_wait_time.count() > 0) {
|
|
||||||
auto wait_time = std::chrono::ceil<std::chrono::milliseconds>(total_wait_time).count();
|
|
||||||
u64 res = SleepEx(static_cast<u64>(wait_time), true);
|
|
||||||
if (res == WAIT_IO_COMPLETION) {
|
|
||||||
auto elapsedTime = std::chrono::high_resolution_clock::now() - start_time;
|
|
||||||
auto elapsedMicroseconds =
|
|
||||||
std::chrono::duration_cast<std::chrono::microseconds>(elapsedTime).count();
|
|
||||||
total_wait_time = std::chrono::microseconds(microseconds - elapsedMicroseconds);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
const auto duration = std::chrono::nanoseconds(rqtp->tv_sec * 1'000'000'000 + rqtp->tv_nsec);
|
||||||
|
std::chrono::nanoseconds remain;
|
||||||
|
const auto uninterrupted = Common::AccurateSleep(duration, &remain, interruptible);
|
||||||
|
if (rmtp) {
|
||||||
|
rmtp->tv_sec = remain.count() / 1'000'000'000;
|
||||||
|
rmtp->tv_nsec = remain.count() % 1'000'000'000;
|
||||||
|
}
|
||||||
|
if (!uninterrupted) {
|
||||||
|
SetPosixErrno(EINTR);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
timespec start;
|
|
||||||
timespec remain;
|
|
||||||
start.tv_sec = microseconds / 1000000;
|
|
||||||
start.tv_nsec = (microseconds % 1000000) * 1000;
|
|
||||||
timespec* requested = &start;
|
|
||||||
int ret = 0;
|
|
||||||
do {
|
|
||||||
ret = nanosleep(requested, &remain);
|
|
||||||
requested = &remain;
|
|
||||||
} while (ret != 0);
|
|
||||||
return ret;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_usleep(u32 microseconds) {
|
|
||||||
return sceKernelUsleep(microseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(seconds));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN64
|
s32 PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
|
||||||
#ifndef CLOCK_REALTIME
|
return posix_nanosleep_impl(rqtp, rmtp, true);
|
||||||
#define CLOCK_REALTIME 0
|
|
||||||
#endif
|
|
||||||
#ifndef CLOCK_MONOTONIC
|
|
||||||
#define CLOCK_MONOTONIC 1
|
|
||||||
#endif
|
|
||||||
#ifndef CLOCK_PROCESS_CPUTIME_ID
|
|
||||||
#define CLOCK_PROCESS_CPUTIME_ID 2
|
|
||||||
#endif
|
|
||||||
#ifndef CLOCK_THREAD_CPUTIME_ID
|
|
||||||
#define CLOCK_THREAD_CPUTIME_ID 3
|
|
||||||
#endif
|
|
||||||
#ifndef CLOCK_REALTIME_COARSE
|
|
||||||
#define CLOCK_REALTIME_COARSE 5
|
|
||||||
#endif
|
|
||||||
#ifndef CLOCK_MONOTONIC_COARSE
|
|
||||||
#define CLOCK_MONOTONIC_COARSE 6
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DELTA_EPOCH_IN_100NS 116444736000000000ULL
|
|
||||||
|
|
||||||
static u64 FileTimeTo100Ns(FILETIME& ft) {
|
|
||||||
return *reinterpret_cast<u64*>(&ft);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static s32 clock_gettime(u32 clock_id, struct timespec* ts) {
|
s32 PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
|
||||||
|
if (const auto ret = posix_nanosleep_impl(rqtp, rmtp, false); ret < 0) {
|
||||||
|
return ErrnoToSceKernelError(*__Error());
|
||||||
|
}
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI posix_usleep(u32 microseconds) {
|
||||||
|
const OrbisKernelTimespec ts = {
|
||||||
|
.tv_sec = microseconds / 1'000'000,
|
||||||
|
.tv_nsec = (microseconds % 1'000'000) * 1'000,
|
||||||
|
};
|
||||||
|
return posix_nanosleep(&ts, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
|
||||||
|
const OrbisKernelTimespec ts = {
|
||||||
|
.tv_sec = microseconds / 1'000'000,
|
||||||
|
.tv_nsec = (microseconds % 1'000'000) * 1'000,
|
||||||
|
};
|
||||||
|
return sceKernelNanosleep(&ts, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 PS4_SYSV_ABI posix_sleep(u32 seconds) {
|
||||||
|
const OrbisKernelTimespec ts = {
|
||||||
|
.tv_sec = seconds,
|
||||||
|
.tv_nsec = 0,
|
||||||
|
};
|
||||||
|
OrbisKernelTimespec rm;
|
||||||
|
if (const auto ret = posix_nanosleep(&ts, &rm); ret < 0) {
|
||||||
|
return *__Error() == POSIX_EINTR ? rm.tv_sec + (rm.tv_nsec == 0 ? 0 : 1) : seconds;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) {
|
||||||
|
return sceKernelUsleep(seconds * 1'000'000);
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI posix_clock_gettime(u32 clock_id, OrbisKernelTimespec* ts) {
|
||||||
|
if (ts == nullptr) {
|
||||||
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clock_id == ORBIS_CLOCK_PROCTIME) {
|
||||||
|
const auto us = sceKernelGetProcessTime();
|
||||||
|
ts->tv_sec = static_cast<s64>(us / 1'000'000);
|
||||||
|
ts->tv_nsec = static_cast<s64>((us % 1'000'000) * 1000);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (clock_id == ORBIS_CLOCK_EXT_NETWORK || clock_id == ORBIS_CLOCK_EXT_DEBUG_NETWORK ||
|
||||||
|
clock_id == ORBIS_CLOCK_EXT_AD_NETWORK || clock_id == ORBIS_CLOCK_EXT_RAW_NETWORK) {
|
||||||
|
LOG_ERROR(Lib_Kernel, "Unsupported clock type {}, using CLOCK_MONOTONIC", clock_id);
|
||||||
|
clock_id = ORBIS_CLOCK_MONOTONIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static const auto FileTimeTo100Ns = [](FILETIME& ft) { return *reinterpret_cast<u64*>(&ft); };
|
||||||
switch (clock_id) {
|
switch (clock_id) {
|
||||||
case CLOCK_REALTIME:
|
case ORBIS_CLOCK_REALTIME:
|
||||||
case CLOCK_REALTIME_COARSE: {
|
case ORBIS_CLOCK_REALTIME_PRECISE: {
|
||||||
FILETIME ft;
|
FILETIME ft;
|
||||||
GetSystemTimeAsFileTime(&ft);
|
GetSystemTimePreciseAsFileTime(&ft);
|
||||||
const u64 ns = FileTimeTo100Ns(ft) - DELTA_EPOCH_IN_100NS;
|
static constexpr u64 DeltaEpochIn100ns = 116444736000000000ULL;
|
||||||
|
const u64 ns = FileTimeTo100Ns(ft) - DeltaEpochIn100ns;
|
||||||
ts->tv_sec = ns / 10'000'000;
|
ts->tv_sec = ns / 10'000'000;
|
||||||
ts->tv_nsec = (ns % 10'000'000) * 100;
|
ts->tv_nsec = (ns % 10'000'000) * 100;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case CLOCK_MONOTONIC:
|
case ORBIS_CLOCK_SECOND:
|
||||||
case CLOCK_MONOTONIC_COARSE: {
|
case ORBIS_CLOCK_REALTIME_FAST: {
|
||||||
|
FILETIME ft;
|
||||||
|
GetSystemTimeAsFileTime(&ft);
|
||||||
|
static constexpr u64 DeltaEpochIn100ns = 116444736000000000ULL;
|
||||||
|
const u64 ns = FileTimeTo100Ns(ft) - DeltaEpochIn100ns;
|
||||||
|
ts->tv_sec = ns / 10'000'000;
|
||||||
|
ts->tv_nsec = (ns % 10'000'000) * 100;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case ORBIS_CLOCK_UPTIME:
|
||||||
|
case ORBIS_CLOCK_UPTIME_PRECISE:
|
||||||
|
case ORBIS_CLOCK_MONOTONIC:
|
||||||
|
case ORBIS_CLOCK_MONOTONIC_PRECISE:
|
||||||
|
case ORBIS_CLOCK_UPTIME_FAST:
|
||||||
|
case ORBIS_CLOCK_MONOTONIC_FAST: {
|
||||||
static LARGE_INTEGER pf = [] {
|
static LARGE_INTEGER pf = [] {
|
||||||
LARGE_INTEGER res{};
|
LARGE_INTEGER res{};
|
||||||
QueryPerformanceFrequency(&pf);
|
QueryPerformanceFrequency(&pf);
|
||||||
|
@ -141,43 +168,53 @@ static s32 clock_gettime(u32 clock_id, struct timespec* ts) {
|
||||||
}();
|
}();
|
||||||
|
|
||||||
LARGE_INTEGER pc{};
|
LARGE_INTEGER pc{};
|
||||||
QueryPerformanceCounter(&pc);
|
if (!QueryPerformanceCounter(&pc)) {
|
||||||
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
ts->tv_sec = pc.QuadPart / pf.QuadPart;
|
ts->tv_sec = pc.QuadPart / pf.QuadPart;
|
||||||
ts->tv_nsec = ((pc.QuadPart % pf.QuadPart) * 1000'000'000) / pf.QuadPart;
|
ts->tv_nsec = ((pc.QuadPart % pf.QuadPart) * 1000'000'000) / pf.QuadPart;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case CLOCK_PROCESS_CPUTIME_ID: {
|
case ORBIS_CLOCK_THREAD_CPUTIME_ID: {
|
||||||
FILETIME ct, et, kt, ut;
|
FILETIME ct, et, kt, ut;
|
||||||
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
|
if (!GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut)) {
|
||||||
return EFAULT;
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt);
|
const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt);
|
||||||
ts->tv_sec = ns / 10'000'000;
|
ts->tv_sec = ns / 10'000'000;
|
||||||
ts->tv_nsec = (ns % 10'000'000) * 100;
|
ts->tv_nsec = (ns % 10'000'000) * 100;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case CLOCK_THREAD_CPUTIME_ID: {
|
case ORBIS_CLOCK_VIRTUAL: {
|
||||||
FILETIME ct, et, kt, ut;
|
FILETIME ct, et, kt, ut;
|
||||||
if (!GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut)) {
|
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
|
||||||
return EFAULT;
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt);
|
const u64 ns = FileTimeTo100Ns(ut);
|
||||||
|
ts->tv_sec = ns / 10'000'000;
|
||||||
|
ts->tv_nsec = (ns % 10'000'000) * 100;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case ORBIS_CLOCK_PROF: {
|
||||||
|
FILETIME ct, et, kt, ut;
|
||||||
|
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
|
||||||
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const u64 ns = FileTimeTo100Ns(kt);
|
||||||
ts->tv_sec = ns / 10'000'000;
|
ts->tv_sec = ns / 10'000'000;
|
||||||
ts->tv_nsec = (ns % 10'000'000) * 100;
|
ts->tv_nsec = (ns % 10'000'000) * 100;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return EINVAL;
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
#else
|
||||||
#endif
|
clockid_t pclock_id;
|
||||||
|
|
||||||
int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* ts) {
|
|
||||||
if (ts == nullptr) {
|
|
||||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
clockid_t pclock_id = CLOCK_MONOTONIC;
|
|
||||||
switch (clock_id) {
|
switch (clock_id) {
|
||||||
case ORBIS_CLOCK_REALTIME:
|
case ORBIS_CLOCK_REALTIME:
|
||||||
case ORBIS_CLOCK_REALTIME_PRECISE:
|
case ORBIS_CLOCK_REALTIME_PRECISE:
|
||||||
|
@ -185,7 +222,7 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
|
||||||
break;
|
break;
|
||||||
case ORBIS_CLOCK_SECOND:
|
case ORBIS_CLOCK_SECOND:
|
||||||
case ORBIS_CLOCK_REALTIME_FAST:
|
case ORBIS_CLOCK_REALTIME_FAST:
|
||||||
#ifndef __APPLE__
|
#ifdef CLOCK_REALTIME_COARSE
|
||||||
pclock_id = CLOCK_REALTIME_COARSE;
|
pclock_id = CLOCK_REALTIME_COARSE;
|
||||||
#else
|
#else
|
||||||
pclock_id = CLOCK_REALTIME;
|
pclock_id = CLOCK_REALTIME;
|
||||||
|
@ -199,7 +236,7 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
|
||||||
break;
|
break;
|
||||||
case ORBIS_CLOCK_UPTIME_FAST:
|
case ORBIS_CLOCK_UPTIME_FAST:
|
||||||
case ORBIS_CLOCK_MONOTONIC_FAST:
|
case ORBIS_CLOCK_MONOTONIC_FAST:
|
||||||
#ifndef __APPLE__
|
#ifdef CLOCK_MONOTONIC_COARSE
|
||||||
pclock_id = CLOCK_MONOTONIC_COARSE;
|
pclock_id = CLOCK_MONOTONIC_COARSE;
|
||||||
#else
|
#else
|
||||||
pclock_id = CLOCK_MONOTONIC;
|
pclock_id = CLOCK_MONOTONIC;
|
||||||
|
@ -208,102 +245,155 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
|
||||||
case ORBIS_CLOCK_THREAD_CPUTIME_ID:
|
case ORBIS_CLOCK_THREAD_CPUTIME_ID:
|
||||||
pclock_id = CLOCK_THREAD_CPUTIME_ID;
|
pclock_id = CLOCK_THREAD_CPUTIME_ID;
|
||||||
break;
|
break;
|
||||||
case ORBIS_CLOCK_PROCTIME: {
|
|
||||||
const auto us = sceKernelGetProcessTime();
|
|
||||||
ts->tv_sec = us / 1'000'000;
|
|
||||||
ts->tv_nsec = (us % 1'000'000) * 1000;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case ORBIS_CLOCK_VIRTUAL: {
|
case ORBIS_CLOCK_VIRTUAL: {
|
||||||
#ifdef _WIN64
|
rusage ru;
|
||||||
FILETIME ct, et, kt, ut;
|
|
||||||
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
|
|
||||||
return EFAULT;
|
|
||||||
}
|
|
||||||
const u64 ns = FileTimeTo100Ns(ut);
|
|
||||||
ts->tv_sec = ns / 10'000'000;
|
|
||||||
ts->tv_nsec = (ns % 10'000'000) * 100;
|
|
||||||
#else
|
|
||||||
struct rusage ru;
|
|
||||||
const auto res = getrusage(RUSAGE_SELF, &ru);
|
const auto res = getrusage(RUSAGE_SELF, &ru);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
return res;
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
ts->tv_sec = ru.ru_utime.tv_sec;
|
ts->tv_sec = ru.ru_utime.tv_sec;
|
||||||
ts->tv_nsec = ru.ru_utime.tv_usec * 1000;
|
ts->tv_nsec = ru.ru_utime.tv_usec * 1000;
|
||||||
#endif
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case ORBIS_CLOCK_PROF: {
|
case ORBIS_CLOCK_PROF: {
|
||||||
#ifdef _WIN64
|
rusage ru;
|
||||||
FILETIME ct, et, kt, ut;
|
|
||||||
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
|
|
||||||
return EFAULT;
|
|
||||||
}
|
|
||||||
const u64 ns = FileTimeTo100Ns(kt);
|
|
||||||
ts->tv_sec = ns / 10'000'000;
|
|
||||||
ts->tv_nsec = (ns % 10'000'000) * 100;
|
|
||||||
#else
|
|
||||||
struct rusage ru;
|
|
||||||
const auto res = getrusage(RUSAGE_SELF, &ru);
|
const auto res = getrusage(RUSAGE_SELF, &ru);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
return res;
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
ts->tv_sec = ru.ru_stime.tv_sec;
|
ts->tv_sec = ru.ru_stime.tv_sec;
|
||||||
ts->tv_nsec = ru.ru_stime.tv_usec * 1000;
|
ts->tv_nsec = ru.ru_stime.tv_usec * 1000;
|
||||||
#endif
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case ORBIS_CLOCK_EXT_NETWORK:
|
|
||||||
case ORBIS_CLOCK_EXT_DEBUG_NETWORK:
|
|
||||||
case ORBIS_CLOCK_EXT_AD_NETWORK:
|
|
||||||
case ORBIS_CLOCK_EXT_RAW_NETWORK:
|
|
||||||
pclock_id = CLOCK_MONOTONIC;
|
|
||||||
LOG_ERROR(Lib_Kernel, "unsupported = {} using CLOCK_MONOTONIC", clock_id);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return EINVAL;
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec t{};
|
timespec t{};
|
||||||
int result = clock_gettime(pclock_id, &t);
|
const auto result = clock_gettime(pclock_id, &t);
|
||||||
ts->tv_sec = t.tv_sec;
|
ts->tv_sec = t.tv_sec;
|
||||||
ts->tv_nsec = t.tv_nsec;
|
ts->tv_nsec = t.tv_nsec;
|
||||||
return result;
|
if (result < 0) {
|
||||||
|
SetPosixErrno(errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) {
|
s32 PS4_SYSV_ABI sceKernelClockGettime(const u32 clock_id, OrbisKernelTimespec* ts) {
|
||||||
const auto res = orbis_clock_gettime(clock_id, tp);
|
if (const auto ret = posix_clock_gettime(clock_id, ts); ret < 0) {
|
||||||
if (res < 0) {
|
return ErrnoToSceKernelError(*__Error());
|
||||||
return ErrnoToSceKernelError(res);
|
|
||||||
}
|
}
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
|
s32 PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
|
||||||
const auto* request = reinterpret_cast<const timespec*>(rqtp);
|
if (res == nullptr) {
|
||||||
auto* remain = reinterpret_cast<timespec*>(rmtp);
|
SetPosixErrno(EFAULT);
|
||||||
return nanosleep(request, remain);
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clock_id == ORBIS_CLOCK_EXT_NETWORK || clock_id == ORBIS_CLOCK_EXT_DEBUG_NETWORK ||
|
||||||
|
clock_id == ORBIS_CLOCK_EXT_AD_NETWORK || clock_id == ORBIS_CLOCK_EXT_RAW_NETWORK) {
|
||||||
|
LOG_ERROR(Lib_Kernel, "Unsupported clock type {}, using CLOCK_MONOTONIC", clock_id);
|
||||||
|
clock_id = ORBIS_CLOCK_MONOTONIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
switch (clock_id) {
|
||||||
|
case ORBIS_CLOCK_SECOND:
|
||||||
|
case ORBIS_CLOCK_REALTIME_FAST: {
|
||||||
|
DWORD timeAdjustment;
|
||||||
|
DWORD timeIncrement;
|
||||||
|
BOOL isTimeAdjustmentDisabled;
|
||||||
|
if (!GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled)) {
|
||||||
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
res->tv_sec = 0;
|
||||||
|
res->tv_nsec = timeIncrement * 100;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case ORBIS_CLOCK_REALTIME:
|
||||||
|
case ORBIS_CLOCK_REALTIME_PRECISE:
|
||||||
|
case ORBIS_CLOCK_UPTIME:
|
||||||
|
case ORBIS_CLOCK_UPTIME_PRECISE:
|
||||||
|
case ORBIS_CLOCK_MONOTONIC:
|
||||||
|
case ORBIS_CLOCK_MONOTONIC_PRECISE:
|
||||||
|
case ORBIS_CLOCK_UPTIME_FAST:
|
||||||
|
case ORBIS_CLOCK_MONOTONIC_FAST: {
|
||||||
|
LARGE_INTEGER pf;
|
||||||
|
if (!QueryPerformanceFrequency(&pf)) {
|
||||||
|
SetPosixErrno(EFAULT);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
res->tv_sec = 0;
|
||||||
|
res->tv_nsec =
|
||||||
|
std::max(static_cast<s32>((1000000000 + (pf.QuadPart >> 1)) / pf.QuadPart), 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
clockid_t pclock_id;
|
||||||
|
switch (clock_id) {
|
||||||
|
case ORBIS_CLOCK_REALTIME:
|
||||||
|
case ORBIS_CLOCK_REALTIME_PRECISE:
|
||||||
|
pclock_id = CLOCK_REALTIME;
|
||||||
|
break;
|
||||||
|
case ORBIS_CLOCK_SECOND:
|
||||||
|
case ORBIS_CLOCK_REALTIME_FAST:
|
||||||
|
#ifdef CLOCK_REALTIME_COARSE
|
||||||
|
pclock_id = CLOCK_REALTIME_COARSE;
|
||||||
|
#else
|
||||||
|
pclock_id = CLOCK_REALTIME;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case ORBIS_CLOCK_UPTIME:
|
||||||
|
case ORBIS_CLOCK_UPTIME_PRECISE:
|
||||||
|
case ORBIS_CLOCK_MONOTONIC:
|
||||||
|
case ORBIS_CLOCK_MONOTONIC_PRECISE:
|
||||||
|
pclock_id = CLOCK_MONOTONIC;
|
||||||
|
break;
|
||||||
|
case ORBIS_CLOCK_UPTIME_FAST:
|
||||||
|
case ORBIS_CLOCK_MONOTONIC_FAST:
|
||||||
|
#ifdef CLOCK_MONOTONIC_COARSE
|
||||||
|
pclock_id = CLOCK_MONOTONIC_COARSE;
|
||||||
|
#else
|
||||||
|
pclock_id = CLOCK_MONOTONIC;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
timespec t{};
|
||||||
|
const auto result = clock_getres(pclock_id, &t);
|
||||||
|
res->tv_sec = t.tv_sec;
|
||||||
|
res->tv_nsec = t.tv_nsec;
|
||||||
|
if (result < 0) {
|
||||||
|
SetPosixErrno(errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
|
s32 PS4_SYSV_ABI sceKernelClockGetres(const u32 clock_id, OrbisKernelTimespec* res) {
|
||||||
if (!rqtp || !rmtp) {
|
if (const auto ret = posix_clock_getres(clock_id, res); ret < 0) {
|
||||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
return ErrnoToSceKernelError(*__Error());
|
||||||
}
|
}
|
||||||
|
return ORBIS_OK;
|
||||||
if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0) {
|
|
||||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return posix_nanosleep(rqtp, rmtp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
|
s32 PS4_SYSV_ABI posix_gettimeofday(OrbisKernelTimeval* tp, OrbisKernelTimezone* tz) {
|
||||||
if (!tp) {
|
|
||||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
|
if (tp) {
|
||||||
FILETIME filetime;
|
FILETIME filetime;
|
||||||
GetSystemTimePreciseAsFileTime(&filetime);
|
GetSystemTimePreciseAsFileTime(&filetime);
|
||||||
|
|
||||||
|
@ -318,28 +408,8 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
|
||||||
|
|
||||||
tp->tv_sec = ticks / TICKS_PER_SECOND;
|
tp->tv_sec = ticks / TICKS_PER_SECOND;
|
||||||
tp->tv_usec = ticks % TICKS_PER_SECOND;
|
tp->tv_usec = ticks % TICKS_PER_SECOND;
|
||||||
#else
|
|
||||||
timeval tv;
|
|
||||||
gettimeofday(&tv, nullptr);
|
|
||||||
tp->tv_sec = tv.tv_sec;
|
|
||||||
tp->tv_usec = tv.tv_usec;
|
|
||||||
#endif
|
|
||||||
return ORBIS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PS4_SYSV_ABI gettimeofday(OrbisKernelTimeval* tp, OrbisKernelTimezone* tz) {
|
|
||||||
// FreeBSD docs mention that the kernel generally does not track these values
|
|
||||||
// and they are usually returned as zero.
|
|
||||||
if (tz) {
|
|
||||||
tz->tz_minuteswest = 0;
|
|
||||||
tz->tz_dsttime = 0;
|
|
||||||
}
|
}
|
||||||
return sceKernelGettimeofday(tp);
|
if (tz) {
|
||||||
}
|
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
|
|
||||||
#ifdef _WIN64
|
|
||||||
ASSERT(tz);
|
|
||||||
static int tzflag = 0;
|
static int tzflag = 0;
|
||||||
if (!tzflag) {
|
if (!tzflag) {
|
||||||
_tzset();
|
_tzset();
|
||||||
|
@ -347,57 +417,54 @@ s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
|
||||||
}
|
}
|
||||||
tz->tz_minuteswest = _timezone / 60;
|
tz->tz_minuteswest = _timezone / 60;
|
||||||
tz->tz_dsttime = _daylight;
|
tz->tz_dsttime = _daylight;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
#else
|
#else
|
||||||
struct timezone tzz;
|
struct timezone tzz;
|
||||||
struct timeval tv;
|
timeval tv;
|
||||||
gettimeofday(&tv, &tzz);
|
const auto ret = gettimeofday(&tv, &tzz);
|
||||||
|
if (tp) {
|
||||||
|
tp->tv_sec = tv.tv_sec;
|
||||||
|
tp->tv_usec = tv.tv_usec;
|
||||||
|
}
|
||||||
|
if (tz) {
|
||||||
tz->tz_dsttime = tzz.tz_dsttime;
|
tz->tz_dsttime = tzz.tz_dsttime;
|
||||||
tz->tz_minuteswest = tzz.tz_minuteswest;
|
tz->tz_minuteswest = tzz.tz_minuteswest;
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
SetPosixErrno(errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
|
||||||
|
if (const auto ret = posix_gettimeofday(tp, nullptr); ret < 0) {
|
||||||
|
return ErrnoToSceKernelError(*__Error());
|
||||||
|
}
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
|
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
|
||||||
if (res == nullptr) {
|
if (const auto ret = posix_gettimeofday(nullptr, tz); ret < 0) {
|
||||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
return ErrnoToSceKernelError(*__Error());
|
||||||
}
|
}
|
||||||
clockid_t pclock_id = CLOCK_REALTIME;
|
|
||||||
switch (clock_id) {
|
|
||||||
case ORBIS_CLOCK_REALTIME:
|
|
||||||
case ORBIS_CLOCK_REALTIME_PRECISE:
|
|
||||||
case ORBIS_CLOCK_REALTIME_FAST:
|
|
||||||
pclock_id = CLOCK_REALTIME;
|
|
||||||
break;
|
|
||||||
case ORBIS_CLOCK_SECOND:
|
|
||||||
case ORBIS_CLOCK_MONOTONIC:
|
|
||||||
case ORBIS_CLOCK_MONOTONIC_PRECISE:
|
|
||||||
case ORBIS_CLOCK_MONOTONIC_FAST:
|
|
||||||
pclock_id = CLOCK_MONOTONIC;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
timespec t{};
|
|
||||||
int result = clock_getres(pclock_id, &t);
|
|
||||||
res->tv_sec = t.tv_sec;
|
|
||||||
res->tv_nsec = t.tv_nsec;
|
|
||||||
if (result == 0) {
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
|
||||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
|
s32 PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
|
||||||
OrbisKernelTimezone* timezone, int* dst_seconds) {
|
OrbisKernelTimezone* timezone, s32* dst_seconds) {
|
||||||
LOG_INFO(Kernel, "called");
|
LOG_INFO(Kernel, "called");
|
||||||
if (timezone) {
|
if (timezone) {
|
||||||
sceKernelGettimezone(timezone);
|
sceKernelGettimezone(timezone);
|
||||||
param_1 -= (timezone->tz_minuteswest + timezone->tz_dsttime) * 60;
|
param_1 -= (timezone->tz_minuteswest + timezone->tz_dsttime) * 60;
|
||||||
if (seconds)
|
if (seconds) {
|
||||||
*seconds = param_1;
|
*seconds = param_1;
|
||||||
if (dst_seconds)
|
}
|
||||||
|
if (dst_seconds) {
|
||||||
*dst_seconds = timezone->tz_dsttime * 60;
|
*dst_seconds = timezone->tz_dsttime * 60;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -415,7 +482,7 @@ Common::NativeClock* GetClock() {
|
||||||
|
|
||||||
} // namespace Dev
|
} // namespace Dev
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
||||||
struct OrbisTimesec* st, u64* dst_sec) {
|
struct OrbisTimesec* st, u64* dst_sec) {
|
||||||
LOG_TRACE(Kernel, "Called");
|
LOG_TRACE(Kernel, "Called");
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
@ -444,28 +511,35 @@ int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
||||||
void RegisterTime(Core::Loader::SymbolsResolver* sym) {
|
void RegisterTime(Core::Loader::SymbolsResolver* sym) {
|
||||||
clock = std::make_unique<Common::NativeClock>();
|
clock = std::make_unique<Common::NativeClock>();
|
||||||
initial_ptc = clock->GetUptime();
|
initial_ptc = clock->GetUptime();
|
||||||
|
|
||||||
|
// POSIX
|
||||||
|
LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);
|
||||||
|
LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep);
|
||||||
|
LIB_FUNCTION("QcteRwbsnV0", "libkernel", 1, "libkernel", 1, 1, posix_usleep);
|
||||||
|
LIB_FUNCTION("QcteRwbsnV0", "libScePosix", 1, "libkernel", 1, 1, posix_usleep);
|
||||||
|
LIB_FUNCTION("0wu33hunNdE", "libkernel", 1, "libkernel", 1, 1, posix_sleep);
|
||||||
|
LIB_FUNCTION("0wu33hunNdE", "libScePosix", 1, "libkernel", 1, 1, posix_sleep);
|
||||||
|
LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, posix_clock_gettime);
|
||||||
|
LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime);
|
||||||
|
LIB_FUNCTION("smIj7eqzZE8", "libkernel", 1, "libkernel", 1, 1, posix_clock_getres);
|
||||||
|
LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
|
||||||
|
LIB_FUNCTION("n88vx3C5nW8", "libkernel", 1, "libkernel", 1, 1, posix_gettimeofday);
|
||||||
|
LIB_FUNCTION("n88vx3C5nW8", "libScePosix", 1, "libkernel", 1, 1, posix_gettimeofday);
|
||||||
|
|
||||||
|
// Orbis
|
||||||
LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime);
|
LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime);
|
||||||
LIB_FUNCTION("fgxnMeTNUtY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTimeCounter);
|
LIB_FUNCTION("fgxnMeTNUtY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTimeCounter);
|
||||||
LIB_FUNCTION("BNowx2l588E", "libkernel", 1, "libkernel", 1, 1,
|
LIB_FUNCTION("BNowx2l588E", "libkernel", 1, "libkernel", 1, 1,
|
||||||
sceKernelGetProcessTimeCounterFrequency);
|
sceKernelGetProcessTimeCounterFrequency);
|
||||||
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
|
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
|
||||||
LIB_FUNCTION("1j3S3n-tTW4", "libkernel", 1, "libkernel", 1, 1, sceKernelGetTscFrequency);
|
LIB_FUNCTION("1j3S3n-tTW4", "libkernel", 1, "libkernel", 1, 1, sceKernelGetTscFrequency);
|
||||||
LIB_FUNCTION("ejekcaNQNq0", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimeofday);
|
|
||||||
LIB_FUNCTION("n88vx3C5nW8", "libkernel", 1, "libkernel", 1, 1, gettimeofday);
|
|
||||||
LIB_FUNCTION("n88vx3C5nW8", "libScePosix", 1, "libkernel", 1, 1, gettimeofday);
|
|
||||||
LIB_FUNCTION("QvsZxomvUHs", "libkernel", 1, "libkernel", 1, 1, sceKernelNanosleep);
|
LIB_FUNCTION("QvsZxomvUHs", "libkernel", 1, "libkernel", 1, 1, sceKernelNanosleep);
|
||||||
LIB_FUNCTION("1jfXLRVzisc", "libkernel", 1, "libkernel", 1, 1, sceKernelUsleep);
|
LIB_FUNCTION("1jfXLRVzisc", "libkernel", 1, "libkernel", 1, 1, sceKernelUsleep);
|
||||||
LIB_FUNCTION("QcteRwbsnV0", "libkernel", 1, "libkernel", 1, 1, posix_usleep);
|
|
||||||
LIB_FUNCTION("QcteRwbsnV0", "libScePosix", 1, "libkernel", 1, 1, posix_usleep);
|
|
||||||
LIB_FUNCTION("-ZR+hG7aDHw", "libkernel", 1, "libkernel", 1, 1, sceKernelSleep);
|
LIB_FUNCTION("-ZR+hG7aDHw", "libkernel", 1, "libkernel", 1, 1, sceKernelSleep);
|
||||||
LIB_FUNCTION("0wu33hunNdE", "libScePosix", 1, "libkernel", 1, 1, sceKernelSleep);
|
|
||||||
LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);
|
|
||||||
LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep);
|
|
||||||
LIB_FUNCTION("QBi7HCK03hw", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGettime);
|
LIB_FUNCTION("QBi7HCK03hw", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGettime);
|
||||||
|
LIB_FUNCTION("wRYVA5Zolso", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGetres);
|
||||||
|
LIB_FUNCTION("ejekcaNQNq0", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimeofday);
|
||||||
LIB_FUNCTION("kOcnerypnQA", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimezone);
|
LIB_FUNCTION("kOcnerypnQA", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimezone);
|
||||||
LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, orbis_clock_gettime);
|
|
||||||
LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, orbis_clock_gettime);
|
|
||||||
LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
|
|
||||||
LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc);
|
LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc);
|
||||||
LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime);
|
LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,14 +75,14 @@ u64 PS4_SYSV_ABI sceKernelGetProcessTime();
|
||||||
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();
|
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();
|
||||||
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency();
|
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency();
|
||||||
u64 PS4_SYSV_ABI sceKernelReadTsc();
|
u64 PS4_SYSV_ABI sceKernelReadTsc();
|
||||||
int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp);
|
s32 PS4_SYSV_ABI sceKernelClockGettime(u32 clock_id, OrbisKernelTimespec* tp);
|
||||||
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz);
|
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz);
|
||||||
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
|
s32 PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
|
||||||
OrbisKernelTimezone* timezone, int* dst_seconds);
|
OrbisKernelTimezone* timezone, s32* dst_seconds);
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st,
|
s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st,
|
||||||
u64* dst_sec);
|
u64* dst_sec);
|
||||||
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds);
|
s32 PS4_SYSV_ABI sceKernelUsleep(u32 microseconds);
|
||||||
|
|
||||||
void RegisterTime(Core::Loader::SymbolsResolver* sym);
|
void RegisterTime(Core::Loader::SymbolsResolver* sym);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "core/libraries/audio3d/audio3d.h"
|
#include "core/libraries/audio3d/audio3d.h"
|
||||||
#include "core/libraries/avplayer/avplayer.h"
|
#include "core/libraries/avplayer/avplayer.h"
|
||||||
#include "core/libraries/camera/camera.h"
|
#include "core/libraries/camera/camera.h"
|
||||||
|
#include "core/libraries/companion/companion_httpd.h"
|
||||||
#include "core/libraries/disc_map/disc_map.h"
|
#include "core/libraries/disc_map/disc_map.h"
|
||||||
#include "core/libraries/game_live_streaming/gamelivestreaming.h"
|
#include "core/libraries/game_live_streaming/gamelivestreaming.h"
|
||||||
#include "core/libraries/gnmdriver/gnmdriver.h"
|
#include "core/libraries/gnmdriver/gnmdriver.h"
|
||||||
|
@ -124,6 +125,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
||||||
Libraries::Ulobjmgr::RegisterlibSceUlobjmgr(sym);
|
Libraries::Ulobjmgr::RegisterlibSceUlobjmgr(sym);
|
||||||
Libraries::SigninDialog::RegisterlibSceSigninDialog(sym);
|
Libraries::SigninDialog::RegisterlibSceSigninDialog(sym);
|
||||||
Libraries::Camera::RegisterlibSceCamera(sym);
|
Libraries::Camera::RegisterlibSceCamera(sym);
|
||||||
|
Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libraries
|
} // namespace Libraries
|
||||||
|
|
|
@ -380,8 +380,7 @@ s32 PS4_SYSV_ABI sceNgs2GeomApply(const OrbisNgs2GeomListenerWork* listener,
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceNgs2PanInit(OrbisNgs2PanWork* work, const float* aSpeakerAngle, float unitAngle,
|
s32 PS4_SYSV_ABI sceNgs2PanInit(OrbisNgs2PanWork* work, const float* aSpeakerAngle, float unitAngle,
|
||||||
u32 numSpeakers) {
|
u32 numSpeakers) {
|
||||||
LOG_ERROR(Lib_Ngs2, "aSpeakerAngle = {}, unitAngle = {}, numSpeakers = {}", *aSpeakerAngle,
|
LOG_ERROR(Lib_Ngs2, "unitAngle = {}, numSpeakers = {}", unitAngle, numSpeakers);
|
||||||
unitAngle, numSpeakers);
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -206,6 +206,10 @@ s32 PS4_SYSV_ABI sceNpTrophyDestroyHandle(OrbisNpTrophyHandle handle) {
|
||||||
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
|
if (handle == ORBIS_NP_TROPHY_INVALID_HANDLE)
|
||||||
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
|
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
|
||||||
|
|
||||||
|
if (handle >= trophy_handles.size()) {
|
||||||
|
LOG_ERROR(Lib_NpTrophy, "Invalid handle {}", handle);
|
||||||
|
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
|
||||||
|
}
|
||||||
if (!trophy_handles.is_allocated({static_cast<u32>(handle)})) {
|
if (!trophy_handles.is_allocated({static_cast<u32>(handle)})) {
|
||||||
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
|
return ORBIS_NP_TROPHY_ERROR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,7 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) {
|
||||||
|
|
||||||
if (item->focusPos != FocusPos::DIRNAME) {
|
if (item->focusPos != FocusPos::DIRNAME) {
|
||||||
this->focus_pos = item->focusPos;
|
this->focus_pos = item->focusPos;
|
||||||
} else {
|
} else if (item->focusPosDirName != nullptr) {
|
||||||
this->focus_pos = item->focusPosDirName->data.to_string();
|
this->focus_pos = item->focusPosDirName->data.to_string();
|
||||||
}
|
}
|
||||||
this->style = item->itemStyle;
|
this->style = item->itemStyle;
|
||||||
|
|
|
@ -10,15 +10,14 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include <core/libraries/system/msgdialog_ui.h>
|
#include "boost/icl/concept/interval.hpp"
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/elf_info.h"
|
#include "common/elf_info.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/path_util.h"
|
#include "common/path_util.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/file_sys/fs.h"
|
#include "core/file_sys/fs.h"
|
||||||
|
#include "core/libraries/system/msgdialog_ui.h"
|
||||||
#include "save_instance.h"
|
#include "save_instance.h"
|
||||||
|
|
||||||
using Common::FS::IOFile;
|
using Common::FS::IOFile;
|
||||||
|
@ -35,11 +34,12 @@ namespace Libraries::SaveData::SaveMemory {
|
||||||
static Core::FileSys::MntPoints* g_mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
static Core::FileSys::MntPoints* g_mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||||
|
|
||||||
struct SlotData {
|
struct SlotData {
|
||||||
OrbisUserServiceUserId user_id;
|
OrbisUserServiceUserId user_id{};
|
||||||
std::string game_serial;
|
std::string game_serial;
|
||||||
std::filesystem::path folder_path;
|
std::filesystem::path folder_path;
|
||||||
PSF sfo;
|
PSF sfo;
|
||||||
std::vector<u8> memory_cache;
|
std::vector<u8> memory_cache;
|
||||||
|
size_t memory_cache_size{};
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::mutex g_slot_mtx;
|
static std::mutex g_slot_mtx;
|
||||||
|
@ -97,7 +97,8 @@ std::filesystem::path GetSavePath(OrbisUserServiceUserId user_id, u32 slot_id,
|
||||||
return SaveInstance::MakeDirSavePath(user_id, Common::ElfInfo::Instance().GameSerial(), dir);
|
return SaveInstance::MakeDirSavePath(user_id, Common::ElfInfo::Instance().GameSerial(), dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial) {
|
size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial,
|
||||||
|
size_t memory_size) {
|
||||||
std::lock_guard lck{g_slot_mtx};
|
std::lock_guard lck{g_slot_mtx};
|
||||||
|
|
||||||
const auto save_dir = GetSavePath(user_id, slot_id, game_serial);
|
const auto save_dir = GetSavePath(user_id, slot_id, game_serial);
|
||||||
|
@ -109,6 +110,7 @@ size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_
|
||||||
.folder_path = save_dir,
|
.folder_path = save_dir,
|
||||||
.sfo = {},
|
.sfo = {},
|
||||||
.memory_cache = {},
|
.memory_cache = {},
|
||||||
|
.memory_cache_size = memory_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
SaveInstance::SetupDefaultParamSFO(data.sfo, GetSaveDir(slot_id), std::string{game_serial});
|
SaveInstance::SetupDefaultParamSFO(data.sfo, GetSaveDir(slot_id), std::string{game_serial});
|
||||||
|
@ -196,9 +198,9 @@ void ReadMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset) {
|
||||||
auto& data = g_attached_slots[slot_id];
|
auto& data = g_attached_slots[slot_id];
|
||||||
auto& memory = data.memory_cache;
|
auto& memory = data.memory_cache;
|
||||||
if (memory.empty()) { // Load file
|
if (memory.empty()) { // Load file
|
||||||
|
memory.resize(data.memory_cache_size);
|
||||||
IOFile f{data.folder_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Read};
|
IOFile f{data.folder_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Read};
|
||||||
if (f.IsOpen()) {
|
if (f.IsOpen()) {
|
||||||
memory.resize(f.GetSize());
|
|
||||||
f.Seek(0);
|
f.Seek(0);
|
||||||
f.ReadSpan(std::span{memory});
|
f.ReadSpan(std::span{memory});
|
||||||
}
|
}
|
||||||
|
@ -222,5 +224,4 @@ void WriteMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset) {
|
||||||
Backup::NewRequest(data.user_id, data.game_serial, GetSaveDir(slot_id),
|
Backup::NewRequest(data.user_id, data.game_serial, GetSaveDir(slot_id),
|
||||||
Backup::OrbisSaveDataEventType::__DO_NOT_SAVE);
|
Backup::OrbisSaveDataEventType::__DO_NOT_SAVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libraries::SaveData::SaveMemory
|
} // namespace Libraries::SaveData::SaveMemory
|
|
@ -4,13 +4,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "save_backup.h"
|
#include "core/libraries/save_data/save_backup.h"
|
||||||
|
|
||||||
class PSF;
|
class PSF;
|
||||||
|
|
||||||
namespace Libraries::SaveData {
|
namespace Libraries::SaveData {
|
||||||
using OrbisUserServiceUserId = s32;
|
using OrbisUserServiceUserId = s32;
|
||||||
}
|
} // namespace Libraries::SaveData
|
||||||
|
|
||||||
namespace Libraries::SaveData::SaveMemory {
|
namespace Libraries::SaveData::SaveMemory {
|
||||||
|
|
||||||
|
@ -22,7 +22,8 @@ void PersistMemory(u32 slot_id, bool lock = true);
|
||||||
std::string_view game_serial);
|
std::string_view game_serial);
|
||||||
|
|
||||||
// returns the size of the save memory if exists
|
// returns the size of the save memory if exists
|
||||||
size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial);
|
size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial,
|
||||||
|
size_t memory_size);
|
||||||
|
|
||||||
// Write the icon. Set buf to null to read the standard icon.
|
// Write the icon. Set buf to null to read the standard icon.
|
||||||
void SetIcon(u32 slot_id, void* buf = nullptr, size_t buf_size = 0);
|
void SetIcon(u32 slot_id, void* buf = nullptr, size_t buf_size = 0);
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <core/libraries/system/msgdialog_ui.h>
|
|
||||||
#include <magic_enum/magic_enum.hpp>
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/config.h"
|
||||||
#include "common/cstring.h"
|
#include "common/cstring.h"
|
||||||
#include "common/elf_info.h"
|
#include "common/elf_info.h"
|
||||||
#include "common/enum.h"
|
#include "common/enum.h"
|
||||||
|
@ -20,7 +20,9 @@
|
||||||
#include "core/libraries/error_codes.h"
|
#include "core/libraries/error_codes.h"
|
||||||
#include "core/libraries/libs.h"
|
#include "core/libraries/libs.h"
|
||||||
#include "core/libraries/save_data/savedata.h"
|
#include "core/libraries/save_data/savedata.h"
|
||||||
|
#include "core/libraries/save_data/savedata_error.h"
|
||||||
#include "core/libraries/system/msgdialog.h"
|
#include "core/libraries/system/msgdialog.h"
|
||||||
|
#include "core/libraries/system/msgdialog_ui.h"
|
||||||
#include "save_backup.h"
|
#include "save_backup.h"
|
||||||
#include "save_instance.h"
|
#include "save_instance.h"
|
||||||
#include "save_memory.h"
|
#include "save_memory.h"
|
||||||
|
@ -33,27 +35,6 @@ using Common::ElfInfo;
|
||||||
|
|
||||||
namespace Libraries::SaveData {
|
namespace Libraries::SaveData {
|
||||||
|
|
||||||
enum class Error : u32 {
|
|
||||||
OK = 0,
|
|
||||||
USER_SERVICE_NOT_INITIALIZED = 0x80960002,
|
|
||||||
PARAMETER = 0x809F0000,
|
|
||||||
NOT_INITIALIZED = 0x809F0001,
|
|
||||||
OUT_OF_MEMORY = 0x809F0002,
|
|
||||||
BUSY = 0x809F0003,
|
|
||||||
NOT_MOUNTED = 0x809F0004,
|
|
||||||
EXISTS = 0x809F0007,
|
|
||||||
NOT_FOUND = 0x809F0008,
|
|
||||||
NO_SPACE_FS = 0x809F000A,
|
|
||||||
INTERNAL = 0x809F000B,
|
|
||||||
MOUNT_FULL = 0x809F000C,
|
|
||||||
BAD_MOUNTED = 0x809F000D,
|
|
||||||
BROKEN = 0x809F000F,
|
|
||||||
INVALID_LOGIN_USER = 0x809F0011,
|
|
||||||
MEMORY_NOT_READY = 0x809F0012,
|
|
||||||
BACKUP_BUSY = 0x809F0013,
|
|
||||||
BUSY_FOR_SAVING = 0x809F0016,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OrbisSaveDataSaveDataMemoryOption : u32 {
|
enum class OrbisSaveDataSaveDataMemoryOption : u32 {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
SET_PARAM = 1 << 0,
|
SET_PARAM = 1 << 0,
|
||||||
|
@ -336,7 +317,9 @@ static std::array<std::optional<SaveInstance>, 16> g_mount_slots;
|
||||||
|
|
||||||
static void initialize() {
|
static void initialize() {
|
||||||
g_initialized = true;
|
g_initialized = true;
|
||||||
g_game_serial = ElfInfo::Instance().GameSerial();
|
g_game_serial = Common::Singleton<PSF>::Instance()
|
||||||
|
->GetString("INSTALL_DIR_SAVEDATA")
|
||||||
|
.value_or(ElfInfo::Instance().GameSerial());
|
||||||
g_fw_ver = ElfInfo::Instance().FirmwareVer();
|
g_fw_ver = ElfInfo::Instance().FirmwareVer();
|
||||||
Backup::StartThread();
|
Backup::StartThread();
|
||||||
}
|
}
|
||||||
|
@ -456,7 +439,7 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
|
||||||
LOG_INFO(Lib_SaveData, "called with invalid block size");
|
LOG_INFO(Lib_SaveData, "called with invalid block size");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto root_save = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir);
|
const auto root_save = Config::GetSaveDataPath();
|
||||||
fs::create_directories(root_save);
|
fs::create_directories(root_save);
|
||||||
const auto available = fs::space(root_save).available;
|
const auto available = fs::space(root_save).available;
|
||||||
|
|
||||||
|
@ -1593,8 +1576,8 @@ Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetu
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
size_t existed_size =
|
size_t existed_size = SaveMemory::SetupSaveMemory(setupParam->userId, slot_id,
|
||||||
SaveMemory::SetupSaveMemory(setupParam->userId, slot_id, g_game_serial);
|
g_game_serial, setupParam->memorySize);
|
||||||
if (existed_size == 0) { // Just created
|
if (existed_size == 0) { // Just created
|
||||||
if (g_fw_ver >= ElfInfo::FW_45 && setupParam->initParam != nullptr) {
|
if (g_fw_ver >= ElfInfo::FW_45 && setupParam->initParam != nullptr) {
|
||||||
auto& sfo = SaveMemory::GetParamSFO(slot_id);
|
auto& sfo = SaveMemory::GetParamSFO(slot_id);
|
||||||
|
|
27
src/core/libraries/save_data/savedata_error.h
Normal file
27
src/core/libraries/save_data/savedata_error.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Libraries::SaveData {
|
||||||
|
enum class Error : u32 {
|
||||||
|
OK = 0,
|
||||||
|
USER_SERVICE_NOT_INITIALIZED = 0x80960002,
|
||||||
|
PARAMETER = 0x809F0000,
|
||||||
|
NOT_INITIALIZED = 0x809F0001,
|
||||||
|
OUT_OF_MEMORY = 0x809F0002,
|
||||||
|
BUSY = 0x809F0003,
|
||||||
|
NOT_MOUNTED = 0x809F0004,
|
||||||
|
EXISTS = 0x809F0007,
|
||||||
|
NOT_FOUND = 0x809F0008,
|
||||||
|
NO_SPACE_FS = 0x809F000A,
|
||||||
|
INTERNAL = 0x809F000B,
|
||||||
|
MOUNT_FULL = 0x809F000C,
|
||||||
|
BAD_MOUNTED = 0x809F000D,
|
||||||
|
BROKEN = 0x809F000F,
|
||||||
|
INVALID_LOGIN_USER = 0x809F0011,
|
||||||
|
MEMORY_NOT_READY = 0x809F0012,
|
||||||
|
BACKUP_BUSY = 0x809F0013,
|
||||||
|
BUSY_FOR_SAVING = 0x809F0016,
|
||||||
|
};
|
||||||
|
} // namespace Libraries::SaveData
|
|
@ -12,6 +12,7 @@
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/aerolib/aerolib.h"
|
#include "core/aerolib/aerolib.h"
|
||||||
#include "core/aerolib/stubs.h"
|
#include "core/aerolib/stubs.h"
|
||||||
|
#include "core/devtools/widget/module_list.h"
|
||||||
#include "core/libraries/kernel/memory.h"
|
#include "core/libraries/kernel/memory.h"
|
||||||
#include "core/libraries/kernel/threads.h"
|
#include "core/libraries/kernel/threads.h"
|
||||||
#include "core/linker.h"
|
#include "core/linker.h"
|
||||||
|
@ -147,6 +148,9 @@ s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) {
|
||||||
|
|
||||||
num_static_modules += !is_dynamic;
|
num_static_modules += !is_dynamic;
|
||||||
m_modules.emplace_back(std::move(module));
|
m_modules.emplace_back(std::move(module));
|
||||||
|
|
||||||
|
Core::Devtools::Widget::ModuleList::AddModule(elf_name.filename().string(), elf_name);
|
||||||
|
|
||||||
return m_modules.size() - 1;
|
return m_modules.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,6 +329,9 @@ bool Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul
|
||||||
}
|
}
|
||||||
if (record) {
|
if (record) {
|
||||||
*return_info = *record;
|
*return_info = *record;
|
||||||
|
|
||||||
|
Core::Devtools::Widget::ModuleList::AddModule(sr.library);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -362,11 +362,8 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
|
||||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When virtual addr is zero, force it to virtual_base. The guest cannot pass Fixed
|
// Limit the minumum address to SystemManagedVirtualBase to prevent hardware-specific issues.
|
||||||
// flag so we will take the branch that searches for free (or reserved) mappings.
|
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
|
||||||
virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
|
|
||||||
alignment = alignment > 0 ? alignment : 16_KB;
|
|
||||||
VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
|
|
||||||
|
|
||||||
// Fixed mapping means the virtual address must exactly match the provided one.
|
// Fixed mapping means the virtual address must exactly match the provided one.
|
||||||
if (True(flags & MemoryMapFlags::Fixed)) {
|
if (True(flags & MemoryMapFlags::Fixed)) {
|
||||||
|
@ -376,24 +373,29 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
|
||||||
// To account for this, unmap any reserved areas within this mapping range first.
|
// To account for this, unmap any reserved areas within this mapping range first.
|
||||||
auto unmap_addr = mapped_addr;
|
auto unmap_addr = mapped_addr;
|
||||||
auto unmap_size = size;
|
auto unmap_size = size;
|
||||||
while (!vma.IsMapped() && unmap_addr < mapped_addr + size && remaining_size < size) {
|
// If flag NoOverwrite is provided, don't overwrite mapped VMAs.
|
||||||
|
// When it isn't provided, VMAs can be overwritten regardless of if they're mapped.
|
||||||
|
while ((False(flags & MemoryMapFlags::NoOverwrite) || !vma.IsMapped()) &&
|
||||||
|
unmap_addr < mapped_addr + size && remaining_size < size) {
|
||||||
auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size);
|
auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size);
|
||||||
unmap_addr += unmapped;
|
unmap_addr += unmapped;
|
||||||
unmap_size -= unmapped;
|
unmap_size -= unmapped;
|
||||||
vma = FindVMA(unmap_addr)->second;
|
vma = FindVMA(unmap_addr)->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should return SCE_KERNEL_ERROR_ENOMEM but rarely happens.
|
|
||||||
vma = FindVMA(mapped_addr)->second;
|
vma = FindVMA(mapped_addr)->second;
|
||||||
remaining_size = vma.base + vma.size - mapped_addr;
|
remaining_size = vma.base + vma.size - mapped_addr;
|
||||||
ASSERT_MSG(!vma.IsMapped() && remaining_size >= size,
|
if (vma.IsMapped() || remaining_size < size) {
|
||||||
"Memory region {:#x} to {:#x} isn't free enough to map region {:#x} to {:#x}",
|
LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr);
|
||||||
vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
|
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the first free area starting with provided virtual address.
|
// Find the first free area starting with provided virtual address.
|
||||||
if (False(flags & MemoryMapFlags::Fixed)) {
|
if (False(flags & MemoryMapFlags::Fixed)) {
|
||||||
mapped_addr = SearchFree(mapped_addr, size, alignment);
|
// Provided address needs to be aligned before we can map.
|
||||||
|
alignment = alignment > 0 ? alignment : 16_KB;
|
||||||
|
mapped_addr = SearchFree(Common::AlignUp(mapped_addr, alignment), size, alignment);
|
||||||
if (mapped_addr == -1) {
|
if (mapped_addr == -1) {
|
||||||
// No suitable memory areas to map to
|
// No suitable memory areas to map to
|
||||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||||
|
@ -783,6 +785,19 @@ int MemoryManager::DirectQueryAvailable(PAddr search_start, PAddr search_end, si
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32 MemoryManager::SetDirectMemoryType(s64 phys_addr, s32 memory_type) {
|
||||||
|
std::scoped_lock lk{mutex};
|
||||||
|
|
||||||
|
auto& dmem_area = FindDmemArea(phys_addr)->second;
|
||||||
|
|
||||||
|
ASSERT_MSG(phys_addr <= dmem_area.GetEnd() && !dmem_area.is_free,
|
||||||
|
"Direct memory area is not mapped");
|
||||||
|
|
||||||
|
dmem_area.memory_type = memory_type;
|
||||||
|
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) {
|
void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) {
|
||||||
auto it = FindVMA(virtual_addr);
|
auto it = FindVMA(virtual_addr);
|
||||||
|
|
||||||
|
@ -949,4 +964,33 @@ int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut,
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MemoryManager::IsStack(VAddr addr, void** start, void** end) {
|
||||||
|
auto vma_handle = FindVMA(addr);
|
||||||
|
if (vma_handle == vma_map.end()) {
|
||||||
|
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VirtualMemoryArea& vma = vma_handle->second;
|
||||||
|
if (!vma.Contains(addr, 0) || vma.IsFree()) {
|
||||||
|
return ORBIS_KERNEL_ERROR_EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto stack_start = 0ul;
|
||||||
|
auto stack_end = 0ul;
|
||||||
|
if (vma.type == VMAType::Stack) {
|
||||||
|
stack_start = vma.base;
|
||||||
|
stack_end = vma.base + vma.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start != nullptr) {
|
||||||
|
*start = reinterpret_cast<void*>(stack_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end != nullptr) {
|
||||||
|
*end = reinterpret_cast<void*>(stack_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
|
@ -219,10 +219,14 @@ public:
|
||||||
int GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut, void** directMemoryStartOut,
|
int GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut, void** directMemoryStartOut,
|
||||||
void** directMemoryEndOut);
|
void** directMemoryEndOut);
|
||||||
|
|
||||||
|
s32 SetDirectMemoryType(s64 phys_addr, s32 memory_type);
|
||||||
|
|
||||||
void NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name);
|
void NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name);
|
||||||
|
|
||||||
void InvalidateMemory(VAddr addr, u64 size) const;
|
void InvalidateMemory(VAddr addr, u64 size) const;
|
||||||
|
|
||||||
|
int IsStack(VAddr addr, void** start, void** end);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VMAHandle FindVMA(VAddr target) {
|
VMAHandle FindVMA(VAddr target) {
|
||||||
return std::prev(vma_map.upper_bound(target));
|
return std::prev(vma_map.upper_bound(target));
|
||||||
|
|
|
@ -53,7 +53,7 @@ template <class ReturnType, class... FuncArgs, class... CallArgs>
|
||||||
ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) {
|
ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) {
|
||||||
EnsureThreadInitialized();
|
EnsureThreadInitialized();
|
||||||
// clear stack to avoid trash from EnsureThreadInitialized
|
// clear stack to avoid trash from EnsureThreadInitialized
|
||||||
ClearStack<13_KB>();
|
ClearStack<12_KB>();
|
||||||
return func(std::forward<CallArgs>(args)...);
|
return func(std::forward<CallArgs>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/scm_rev.h"
|
#include "common/scm_rev.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
|
#include "core/devtools/widget/module_list.h"
|
||||||
#include "core/file_format/psf.h"
|
#include "core/file_format/psf.h"
|
||||||
#include "core/file_format/trp.h"
|
#include "core/file_format/trp.h"
|
||||||
#include "core/file_sys/fs.h"
|
#include "core/file_sys/fs.h"
|
||||||
|
@ -188,18 +189,28 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
|
||||||
game_info.splash_path = pic1_path;
|
game_info.splash_path = pic1_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
game_info.game_folder = game_folder;
|
||||||
|
|
||||||
std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version);
|
std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version);
|
||||||
std::string window_title = "";
|
std::string window_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_url(Common::g_scm_remote_url);
|
||||||
std::string remote_host;
|
std::string remote_host;
|
||||||
try {
|
try {
|
||||||
|
if (*remote_url.rbegin() == '/') {
|
||||||
|
remote_url.pop_back();
|
||||||
|
}
|
||||||
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
|
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
remote_host = "unknown";
|
remote_host = "unknown";
|
||||||
}
|
}
|
||||||
|
if (Common::g_is_release) {
|
||||||
|
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
||||||
|
window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title);
|
||||||
|
} else {
|
||||||
|
window_title =
|
||||||
|
fmt::format("shadPS4 {}/v{} | {}", remote_host, Common::g_version, game_title);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
||||||
window_title = fmt::format("shadPS4 v{} {} {} | {}", Common::g_version,
|
window_title = fmt::format("shadPS4 v{} {} {} | {}", Common::g_version,
|
||||||
Common::g_scm_branch, Common::g_scm_desc, game_title);
|
Common::g_scm_branch, Common::g_scm_desc, game_title);
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 133 KiB |
|
@ -79,6 +79,11 @@ public:
|
||||||
if (const auto play_time = psf.GetString("PLAY_TIME"); play_time.has_value()) {
|
if (const auto play_time = psf.GetString("PLAY_TIME"); play_time.has_value()) {
|
||||||
game.play_time = *play_time;
|
game.play_time = *play_time;
|
||||||
}
|
}
|
||||||
|
if (const auto save_dir = psf.GetString("INSTALL_DIR_SAVEDATA"); save_dir.has_value()) {
|
||||||
|
game.save_dir = *save_dir;
|
||||||
|
} else {
|
||||||
|
game.save_dir = game.serial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ struct GameInfo {
|
||||||
std::string version = "Unknown";
|
std::string version = "Unknown";
|
||||||
std::string region = "Unknown";
|
std::string region = "Unknown";
|
||||||
std::string fw = "Unknown";
|
std::string fw = "Unknown";
|
||||||
|
std::string save_dir = "Unknown";
|
||||||
|
|
||||||
std::string play_time = "Unknown";
|
std::string play_time = "Unknown";
|
||||||
CompatibilityEntry compatibility = CompatibilityEntry{CompatibilityStatus::Unknown};
|
CompatibilityEntry compatibility = CompatibilityEntry{CompatibilityStatus::Unknown};
|
||||||
|
|
|
@ -156,11 +156,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected == openSaveDataFolder) {
|
if (selected == openSaveDataFolder) {
|
||||||
QString userPath;
|
QString saveDataPath;
|
||||||
Common::FS::PathToQString(userPath,
|
Common::FS::PathToQString(saveDataPath,
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::UserDir));
|
Config::GetSaveDataPath() / "1" / m_games[itemID].save_dir);
|
||||||
QString saveDataPath =
|
|
||||||
userPath + "/savedata/1/" + QString::fromStdString(m_games[itemID].serial);
|
|
||||||
QDir(saveDataPath).mkpath(saveDataPath);
|
QDir(saveDataPath).mkpath(saveDataPath);
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(saveDataPath));
|
QDesktopServices::openUrl(QUrl::fromLocalFile(saveDataPath));
|
||||||
}
|
}
|
||||||
|
@ -485,8 +483,7 @@ public:
|
||||||
dlc_path, Config::getAddonInstallDir() /
|
dlc_path, Config::getAddonInstallDir() /
|
||||||
Common::FS::PathFromQString(folder_path).parent_path().filename());
|
Common::FS::PathFromQString(folder_path).parent_path().filename());
|
||||||
Common::FS::PathToQString(save_data_path,
|
Common::FS::PathToQString(save_data_path,
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::UserDir) /
|
Config::GetSaveDataPath() / "1" / m_games[itemID].save_dir);
|
||||||
"savedata/1" / m_games[itemID].serial);
|
|
||||||
|
|
||||||
Common::FS::PathToQString(trophy_data_path,
|
Common::FS::PathToQString(trophy_data_path,
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
|
Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
|
||||||
|
|
|
@ -825,6 +825,9 @@
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimumHeight">
|
||||||
|
<number>48</number>
|
||||||
|
</property>
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::FocusPolicy::NoFocus</enum>
|
<enum>Qt::FocusPolicy::NoFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
|
@ -841,6 +844,9 @@
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimumHeight">
|
||||||
|
<number>48</number>
|
||||||
|
</property>
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::FocusPolicy::NoFocus</enum>
|
<enum>Qt::FocusPolicy::NoFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
|
@ -983,8 +989,8 @@
|
||||||
<widget class="QLabel" name="l_controller">
|
<widget class="QLabel" name="l_controller">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>500</width>
|
<width>424</width>
|
||||||
<height>200</height>
|
<height>250</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="pixmap">
|
<property name="pixmap">
|
||||||
|
|
|
@ -55,16 +55,23 @@ bool MainWindow::Init() {
|
||||||
// show ui
|
// show ui
|
||||||
setMinimumSize(720, 405);
|
setMinimumSize(720, 405);
|
||||||
std::string window_title = "";
|
std::string window_title = "";
|
||||||
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_url(Common::g_scm_remote_url);
|
||||||
std::string remote_host;
|
std::string remote_host;
|
||||||
try {
|
try {
|
||||||
|
if (*remote_url.rbegin() == '/') {
|
||||||
|
remote_url.pop_back();
|
||||||
|
}
|
||||||
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
|
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
remote_host = "unknown";
|
remote_host = "unknown";
|
||||||
}
|
}
|
||||||
|
if (Common::g_is_release) {
|
||||||
|
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
||||||
|
window_title = fmt::format("shadPS4 v{}", Common::g_version);
|
||||||
|
} else {
|
||||||
|
window_title = fmt::format("shadPS4 {}/v{}", remote_host, Common::g_version);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
||||||
window_title = fmt::format("shadPS4 v{} {} {}", Common::g_version, Common::g_scm_branch,
|
window_title = fmt::format("shadPS4 v{} {} {}", Common::g_version, Common::g_scm_branch,
|
||||||
Common::g_scm_desc);
|
Common::g_scm_desc);
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>قائمة الألعاب</translation>
|
<translation>قائمة الألعاب</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * إصدار Vulkan غير مدعوم</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>تحميل الشفرات لجميع الألعاب المثبتة</translation>
|
<translation>تحميل الشفرات لجميع الألعاب المثبتة</translation>
|
||||||
|
@ -2051,6 +2047,10 @@ Nightly: نُسخ تحتوي على أحدث الميزات، لكنها أقل
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>افتح مجلد الصور/الأصوات الخاصة بالجوائز المخصصة:\nيمكنك إضافة صور مخصصة للجوائز وصوت مرفق.\nأضف الملفات إلى مجلد custom_trophy بالأسماء التالية:\ntrophy.wav أو trophy.mp3، bronze.png، gold.png، platinum.png، silver.png\nملاحظة: الصوت سيعمل فقط في الإصدارات التي تستخدم QT.</translation>
|
<translation>افتح مجلد الصور/الأصوات الخاصة بالجوائز المخصصة:\nيمكنك إضافة صور مخصصة للجوائز وصوت مرفق.\nأضف الملفات إلى مجلد custom_trophy بالأسماء التالية:\ntrophy.wav أو trophy.mp3، bronze.png، gold.png، platinum.png، silver.png\nملاحظة: الصوت سيعمل فقط في الإصدارات التي تستخدم QT.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Spiloversigt</translation>
|
<translation>Spiloversigt</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Ikke understøttet Vulkan-version</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Hent snyd til alle installerede spil</translation>
|
<translation>Hent snyd til alle installerede spil</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Spieleliste</translation>
|
<translation>Spieleliste</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Nicht unterstützte Vulkan-Version</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Cheats für alle installierten Spiele herunterladen</translation>
|
<translation>Cheats für alle installierten Spiele herunterladen</translation>
|
||||||
|
@ -2054,6 +2050,10 @@ Fügen Sie die Dateien dem Ordner custom_trophy mit folgenden Namen hinzu:\n
|
||||||
trophy.wav ODER trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\n
|
trophy.wav ODER trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\n
|
||||||
Hinweis: Der Sound funktioniert nur in Qt-Versionen.</translation>
|
Hinweis: Der Sound funktioniert nur in Qt-Versionen.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Λίστα παιχνιδιών</translation>
|
<translation>Λίστα παιχνιδιών</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Μη υποστηριζόμενη έκδοση Vulkan</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Λήψη Cheats για όλα τα εγκατεστημένα παιχνίδια</translation>
|
<translation>Λήψη Cheats για όλα τα εγκατεστημένα παιχνίδια</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Game List</translation>
|
<translation>Game List</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Unsupported Vulkan Version</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Download Cheats For All Installed Games</translation>
|
<translation>Download Cheats For All Installed Games</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Lista de Juegos</translation>
|
<translation>Lista de Juegos</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Versión de Vulkan no soportada</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Descargar trucos para todos los juegos instalados</translation>
|
<translation>Descargar trucos para todos los juegos instalados</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Abre la carpeta de trofeos/sonidos personalizados:\nPuedes añadir imágenes y un audio personalizados a los trofeos.\nAñade los archivos a custom_trophy con los siguientes nombres:\ntrophy.wav o trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNota: El sonido sólo funcionará en versiones QT.</translation>
|
<translation>Abre la carpeta de trofeos/sonidos personalizados:\nPuedes añadir imágenes y un audio personalizados a los trofeos.\nAñade los archivos a custom_trophy con los siguientes nombres:\ntrophy.wav o trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNota: El sonido sólo funcionará en versiones QT.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>لیست بازی</translation>
|
<translation>لیست بازی</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation>شما پشتیبانی نمیشود Vulkan ورژن *</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>دانلود چیت برای همه بازی ها</translation>
|
<translation>دانلود چیت برای همه بازی ها</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Pelilista</translation>
|
<translation>Pelilista</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Ei Tuettu Vulkan-versio</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Lataa Huijaukset Kaikille Asennetuille Peleille</translation>
|
<translation>Lataa Huijaukset Kaikille Asennetuille Peleille</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Liste de jeux</translation>
|
<translation>Liste de jeux</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Version de Vulkan non prise en charge</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Télécharger les Cheats pour tous les jeux installés</translation>
|
<translation>Télécharger les Cheats pour tous les jeux installés</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Ouvrez le dossier des images/sons des trophées personnalisés:\nVous pouvez ajouter des images personnalisées aux trophées et aux sons.\nAjoutez les fichiers à custom_trophy avec les noms suivants:\ntrophy.wav OU trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote : Le son ne fonctionnera que dans les versions QT.</translation>
|
<translation>Ouvrez le dossier des images/sons des trophées personnalisés:\nVous pouvez ajouter des images personnalisées aux trophées et aux sons.\nAjoutez les fichiers à custom_trophy avec les noms suivants:\ntrophy.wav OU trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote : Le son ne fonctionnera que dans les versions QT.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation> * Version de Vulkan non prise en charge</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Játéklista</translation>
|
<translation>Játéklista</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Nem támogatott Vulkan verzió</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Csalások letöltése minden telepített játékhoz</translation>
|
<translation>Csalások letöltése minden telepített játékhoz</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Daftar game</translation>
|
<translation>Daftar game</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Versi Vulkan Tidak Didukung</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Unduh Cheat Untuk Semua Game Yang Terpasang</translation>
|
<translation>Unduh Cheat Untuk Semua Game Yang Terpasang</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Elenco giochi</translation>
|
<translation>Elenco giochi</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Versione Vulkan non supportata</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Scarica Trucchi per tutti i giochi installati</translation>
|
<translation>Scarica Trucchi per tutti i giochi installati</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Apri la cartella personalizzata delle immagini/suoni trofei:\nÈ possibile aggiungere immagini personalizzate ai trofei e un audio.\nAggiungi i file a custom_trophy con i seguenti nomi:\ntrophy.wav OPPURE trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNota: Il suono funzionerà solo nelle versioni QT.</translation>
|
<translation>Apri la cartella personalizzata delle immagini/suoni trofei:\nÈ possibile aggiungere immagini personalizzate ai trofei e un audio.\nAggiungi i file a custom_trophy con i seguenti nomi:\ntrophy.wav OPPURE trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNota: Il suono funzionerà solo nelle versioni QT.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation> * Versione Vulkan non supportata</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>ゲームリスト</translation>
|
<translation>ゲームリスト</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * サポートされていないVulkanバージョン</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>すべてのインストール済みゲームのチートをダウンロード</translation>
|
<translation>すべてのインストール済みゲームのチートをダウンロード</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -7,15 +7,15 @@
|
||||||
<name>AboutDialog</name>
|
<name>AboutDialog</name>
|
||||||
<message>
|
<message>
|
||||||
<source>About shadPS4</source>
|
<source>About shadPS4</source>
|
||||||
<translation type="unfinished">About shadPS4</translation>
|
<translation>shadPS4에 관하여</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>shadPS4 is an experimental open-source emulator for the PlayStation 4.</source>
|
<source>shadPS4 is an experimental open-source emulator for the PlayStation 4.</source>
|
||||||
<translation type="unfinished">shadPS4 is an experimental open-source emulator for the PlayStation 4.</translation>
|
<translation>shadPS4는 PlayStation 4용 실험적인 오픈 소스 에뮬레이터입니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>This software should not be used to play games you have not legally obtained.</source>
|
<source>This software should not be used to play games you have not legally obtained.</source>
|
||||||
<translation type="unfinished">This software should not be used to play games you have not legally obtained.</translation>
|
<translation>이 소프트웨어는 합법적으로 얻지 않은 게임을 플레이하는 데 사용되어서는 안 됩니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -26,238 +26,238 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>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</source>
|
<source>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</source>
|
||||||
<translation type="unfinished">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</translation>
|
<translation>치트/패치는 실험적인 기능입니다.\n사용 시 주의하시기 바랍니다.\n\n치트를 개별적으로 다운로드하려면, 저장소를 선택한 후 다운로드 버튼을 클릭하세요.\n패치 탭에서는 모든 패치를 한 번에 다운로드할 수 있으며, 원하는 항목을 선택하고 저장할 수 있습니다.\n\n치트/패치는 저희가 개발한 것이 아니므로,\n문제가 발생하면 해당 치트 제작자에게 문의해 주세요.\n\n새로운 치트를 만들었나요? 방문해 주세요:\n</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No Image Available</source>
|
<source>No Image Available</source>
|
||||||
<translation type="unfinished">No Image Available</translation>
|
<translation>사용 가능한 이미지 없음</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Serial: </source>
|
<source>Serial: </source>
|
||||||
<translation type="unfinished">Serial: </translation>
|
<translation>시리얼: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Version: </source>
|
<source>Version: </source>
|
||||||
<translation type="unfinished">Version: </translation>
|
<translation>버전: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Size: </source>
|
<source>Size: </source>
|
||||||
<translation type="unfinished">Size: </translation>
|
<translation>사이즈: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Select Cheat File:</source>
|
<source>Select Cheat File:</source>
|
||||||
<translation type="unfinished">Select Cheat File:</translation>
|
<translation>치트 파일 선택:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Repository:</source>
|
<source>Repository:</source>
|
||||||
<translation type="unfinished">Repository:</translation>
|
<translation>저장소:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats</source>
|
<source>Download Cheats</source>
|
||||||
<translation type="unfinished">Download Cheats</translation>
|
<translation>치트 다운로드</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Delete File</source>
|
<source>Delete File</source>
|
||||||
<translation type="unfinished">Delete File</translation>
|
<translation>파일 삭제</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No files selected.</source>
|
<source>No files selected.</source>
|
||||||
<translation type="unfinished">No files selected.</translation>
|
<translation>파일 선택되지 않음.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>You can delete the cheats you don't want after downloading them.</source>
|
<source>You can delete the cheats you don't want after downloading them.</source>
|
||||||
<translation type="unfinished">You can delete the cheats you don't want after downloading them.</translation>
|
<translation>다운로드한 후 원하지 않는 치트는 삭제할 수 있습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Do you want to delete the selected file?\n%1</source>
|
<source>Do you want to delete the selected file?\n%1</source>
|
||||||
<translation type="unfinished">Do you want to delete the selected file?\n%1</translation>
|
<translation>선택한 파일을 삭제하시겠습니까?\n%1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Select Patch File:</source>
|
<source>Select Patch File:</source>
|
||||||
<translation type="unfinished">Select Patch File:</translation>
|
<translation>패치 파일 선택:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Download Patches</source>
|
<source>Download Patches</source>
|
||||||
<translation type="unfinished">Download Patches</translation>
|
<translation>패치 다운로드</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation type="unfinished">Save</translation>
|
<translation>저장</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Cheats</source>
|
<source>Cheats</source>
|
||||||
<translation type="unfinished">Cheats</translation>
|
<translation>치트</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Patches</source>
|
<source>Patches</source>
|
||||||
<translation type="unfinished">Patches</translation>
|
<translation>패치</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Error</source>
|
<source>Error</source>
|
||||||
<translation type="unfinished">Error</translation>
|
<translation>오류</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No patch selected.</source>
|
<source>No patch selected.</source>
|
||||||
<translation type="unfinished">No patch selected.</translation>
|
<translation>패치 선택되지 않음.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unable to open files.json for reading.</source>
|
<source>Unable to open files.json for reading.</source>
|
||||||
<translation type="unfinished">Unable to open files.json for reading.</translation>
|
<translation>Files.json을 읽기 위해 열 수 없습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No patch file found for the current serial.</source>
|
<source>No patch file found for the current serial.</source>
|
||||||
<translation type="unfinished">No patch file found for the current serial.</translation>
|
<translation>현재 시리얼에 해당하는 패치 파일을 찾을 수 없습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unable to open the file for reading.</source>
|
<source>Unable to open the file for reading.</source>
|
||||||
<translation type="unfinished">Unable to open the file for reading.</translation>
|
<translation>파일을 읽기 위해 열 수 없습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unable to open the file for writing.</source>
|
<source>Unable to open the file for writing.</source>
|
||||||
<translation type="unfinished">Unable to open the file for writing.</translation>
|
<translation>파일을 쓰기 위해 열 수 없습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to parse XML: </source>
|
<source>Failed to parse XML: </source>
|
||||||
<translation type="unfinished">Failed to parse XML: </translation>
|
<translation>XML 구문 분석에 실패했습니다: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Success</source>
|
<source>Success</source>
|
||||||
<translation type="unfinished">Success</translation>
|
<translation>성공</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Options saved successfully.</source>
|
<source>Options saved successfully.</source>
|
||||||
<translation type="unfinished">Options saved successfully.</translation>
|
<translation>옵션이 성공적으로 저장되었습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Invalid Source</source>
|
<source>Invalid Source</source>
|
||||||
<translation type="unfinished">Invalid Source</translation>
|
<translation>잘못된 출처</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The selected source is invalid.</source>
|
<source>The selected source is invalid.</source>
|
||||||
<translation type="unfinished">The selected source is invalid.</translation>
|
<translation>선택한 출처가 올바르지 않습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>File Exists</source>
|
<source>File Exists</source>
|
||||||
<translation type="unfinished">File Exists</translation>
|
<translation>파일이 이미 존재합니다</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>File already exists. Do you want to replace it?</source>
|
<source>File already exists. Do you want to replace it?</source>
|
||||||
<translation type="unfinished">File already exists. Do you want to replace it?</translation>
|
<translation>파일이 이미 존재합니다. 덮어쓰시겠습니까?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to save file:</source>
|
<source>Failed to save file:</source>
|
||||||
<translation type="unfinished">Failed to save file:</translation>
|
<translation>파일 저장 실패:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to download file:</source>
|
<source>Failed to download file:</source>
|
||||||
<translation type="unfinished">Failed to download file:</translation>
|
<translation>파일 다운로드 실패:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Cheats Not Found</source>
|
<source>Cheats Not Found</source>
|
||||||
<translation type="unfinished">Cheats Not Found</translation>
|
<translation>치트 찾을 수 없음</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game.</source>
|
<source>No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game.</source>
|
||||||
<translation type="unfinished">No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game.</translation>
|
<translation>선택한 저장소의 이 버전에서 해당 게임에 대한 치트를 찾을 수 없습니다. 다른 저장소나 게임의 다른 버전을 시도해 보세요.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Cheats Downloaded Successfully</source>
|
<source>Cheats Downloaded Successfully</source>
|
||||||
<translation type="unfinished">Cheats Downloaded Successfully</translation>
|
<translation>치트가 성공적으로 다운로드되었습니다</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation>선택한 저장소에서 이 게임 버전의 치트를 성공적으로 다운로드했습니다. 다른 저장소에서 다운로드할 수 있는 경우, 목록에서 파일을 선택하여 사용할 수도 있습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to save:</source>
|
<source>Failed to save:</source>
|
||||||
<translation type="unfinished">Failed to save:</translation>
|
<translation>저장 실패:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to download:</source>
|
<source>Failed to download:</source>
|
||||||
<translation type="unfinished">Failed to download:</translation>
|
<translation>다운로드 실패:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Download Complete</source>
|
<source>Download Complete</source>
|
||||||
<translation type="unfinished">Download Complete</translation>
|
<translation>다운로드 완료</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation>패치가 성공적으로 다운로드되었습니다! 모든 게임에 적용 가능한 모든 패치가 다운로드되었으므로, 치트처럼 각 게임마다 개별적으로 다운로드할 필요가 없습니다. 만약 패치가 나타나지 않는다면, 해당 게임의 특정 시리얼 및 버전에 해당 패치가 없기 때문일 수 있습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to parse JSON data from HTML.</source>
|
<source>Failed to parse JSON data from HTML.</source>
|
||||||
<translation type="unfinished">Failed to parse JSON data from HTML.</translation>
|
<translation>HTML에서 JSON 데이터를 구문 분석하는 데 실패했습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to retrieve HTML page.</source>
|
<source>Failed to retrieve HTML page.</source>
|
||||||
<translation type="unfinished">Failed to retrieve HTML page.</translation>
|
<translation>HTML 페이지를 가져오지 못했습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The game is in version: %1</source>
|
<source>The game is in version: %1</source>
|
||||||
<translation type="unfinished">The game is in version: %1</translation>
|
<translation>게임 버전: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The downloaded patch only works on version: %1</source>
|
<source>The downloaded patch only works on version: %1</source>
|
||||||
<translation type="unfinished">The downloaded patch only works on version: %1</translation>
|
<translation>다운로드한 패치는 버전 %1 에서만 작동합니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>You may need to update your game.</source>
|
<source>You may need to update your game.</source>
|
||||||
<translation type="unfinished">You may need to update your game.</translation>
|
<translation>게임을 업데이트해야 할 수도 있습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Incompatibility Notice</source>
|
<source>Incompatibility Notice</source>
|
||||||
<translation type="unfinished">Incompatibility Notice</translation>
|
<translation>호환성 경고</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to open file:</source>
|
<source>Failed to open file:</source>
|
||||||
<translation type="unfinished">Failed to open file:</translation>
|
<translation>파일을 열지 못했습니다:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>XML ERROR:</source>
|
<source>XML ERROR:</source>
|
||||||
<translation type="unfinished">XML ERROR:</translation>
|
<translation>XML 오류:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to open files.json for writing</source>
|
<source>Failed to open files.json for writing</source>
|
||||||
<translation type="unfinished">Failed to open files.json for writing</translation>
|
<translation>files.json 파일을 쓰기 위해 열지 못했습니다</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Author: </source>
|
<source>Author: </source>
|
||||||
<translation type="unfinished">Author: </translation>
|
<translation>제작자: </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Directory does not exist:</source>
|
<source>Directory does not exist:</source>
|
||||||
<translation type="unfinished">Directory does not exist:</translation>
|
<translation>디렉터리가 존재하지 않습니다:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to open files.json for reading.</source>
|
<source>Failed to open files.json for reading.</source>
|
||||||
<translation type="unfinished">Failed to open files.json for reading.</translation>
|
<translation>files.json 파일을 읽기 위해 열지 못했습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Name:</source>
|
<source>Name:</source>
|
||||||
<translation type="unfinished">Name:</translation>
|
<translation>이름:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Can't apply cheats before the game is started</source>
|
<source>Can't apply cheats before the game is started</source>
|
||||||
<translation type="unfinished">Can't apply cheats before the game is started</translation>
|
<translation>게임이 시작되기 전에 치트를 적용할 수 없습니다</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Close</source>
|
<source>Close</source>
|
||||||
<translation type="unfinished">Close</translation>
|
<translation>닫기</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>CheckUpdate</name>
|
<name>CheckUpdate</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Auto Updater</source>
|
<source>Auto Updater</source>
|
||||||
<translation type="unfinished">Auto Updater</translation>
|
<translation>자동 업데이트</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Error</source>
|
<source>Error</source>
|
||||||
<translation type="unfinished">Error</translation>
|
<translation>오류</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Network error:</source>
|
<source>Network error:</source>
|
||||||
<translation type="unfinished">Network error:</translation>
|
<translation>네트워크 오류:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The Auto Updater allows up to 60 update checks per hour.\nYou have reached this limit. Please try again later.</source>
|
<source>The Auto Updater allows up to 60 update checks per hour.\nYou have reached this limit. Please try again later.</source>
|
||||||
|
@ -265,91 +265,91 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to parse update information.</source>
|
<source>Failed to parse update information.</source>
|
||||||
<translation type="unfinished">Failed to parse update information.</translation>
|
<translation>업데이트 정보 구문 분석에 실패했습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No pre-releases found.</source>
|
<source>No pre-releases found.</source>
|
||||||
<translation type="unfinished">No pre-releases found.</translation>
|
<translation>사전 릴리스가 없습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Invalid release data.</source>
|
<source>Invalid release data.</source>
|
||||||
<translation type="unfinished">Invalid release data.</translation>
|
<translation>잘못된 릴리스 데이터입니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No download URL found for the specified asset.</source>
|
<source>No download URL found for the specified asset.</source>
|
||||||
<translation type="unfinished">No download URL found for the specified asset.</translation>
|
<translation>지정된 자산에 대한 다운로드 URL을 찾을 수 없습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Your version is already up to date!</source>
|
<source>Your version is already up to date!</source>
|
||||||
<translation type="unfinished">Your version is already up to date!</translation>
|
<translation>버전이 이미 최신입니다!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Update Available</source>
|
<source>Update Available</source>
|
||||||
<translation type="unfinished">Update Available</translation>
|
<translation>업데이트 가능</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Update Channel</source>
|
<source>Update Channel</source>
|
||||||
<translation type="unfinished">Update Channel</translation>
|
<translation>업데이트 채널</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Current Version</source>
|
<source>Current Version</source>
|
||||||
<translation type="unfinished">Current Version</translation>
|
<translation>현재 버전</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Latest Version</source>
|
<source>Latest Version</source>
|
||||||
<translation type="unfinished">Latest Version</translation>
|
<translation>최신 버전</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Do you want to update?</source>
|
<source>Do you want to update?</source>
|
||||||
<translation type="unfinished">Do you want to update?</translation>
|
<translation>업데이트하시겠습니까?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Show Changelog</source>
|
<source>Show Changelog</source>
|
||||||
<translation type="unfinished">Show Changelog</translation>
|
<translation>변경 사항 보기</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Check for Updates at Startup</source>
|
<source>Check for Updates at Startup</source>
|
||||||
<translation type="unfinished">Check for Updates at Startup</translation>
|
<translation>시작할 때 업데이트 확인</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Update</source>
|
<source>Update</source>
|
||||||
<translation type="unfinished">Update</translation>
|
<translation>업데이트</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>No</source>
|
<source>No</source>
|
||||||
<translation type="unfinished">No</translation>
|
<translation>아니요</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hide Changelog</source>
|
<source>Hide Changelog</source>
|
||||||
<translation type="unfinished">Hide Changelog</translation>
|
<translation>변경 사항 숨기기</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Changes</source>
|
<source>Changes</source>
|
||||||
<translation type="unfinished">Changes</translation>
|
<translation>변경 사항</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Network error occurred while trying to access the URL</source>
|
<source>Network error occurred while trying to access the URL</source>
|
||||||
<translation type="unfinished">Network error occurred while trying to access the URL</translation>
|
<translation>URL에 접근하는 동안 네트워크 오류가 발생했습니다</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Download Complete</source>
|
<source>Download Complete</source>
|
||||||
<translation type="unfinished">Download Complete</translation>
|
<translation>다운로드 완료</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>The update has been downloaded, press OK to install.</source>
|
<source>The update has been downloaded, press OK to install.</source>
|
||||||
<translation type="unfinished">The update has been downloaded, press OK to install.</translation>
|
<translation>업데이트가 다운로드 되었습니다. 설치하려면 확인을 눌러주세요.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to save the update file at</source>
|
<source>Failed to save the update file at</source>
|
||||||
<translation type="unfinished">Failed to save the update file at</translation>
|
<translation>업데이트 파일을 다음 위치에 저장하지 못했습니다</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Starting Update...</source>
|
<source>Starting Update...</source>
|
||||||
<translation type="unfinished">Starting Update...</translation>
|
<translation>업데이트를 시작합니다...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to create the update script file</source>
|
<source>Failed to create the update script file</source>
|
||||||
<translation type="unfinished">Failed to create the update script file</translation>
|
<translation>업데이트 스크립트 파일을 생성하지 못했습니다</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -407,83 +407,83 @@
|
||||||
<name>ControlSettings</name>
|
<name>ControlSettings</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Configure Controls</source>
|
<source>Configure Controls</source>
|
||||||
<translation type="unfinished">Configure Controls</translation>
|
<translation>컨트롤 설정</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>D-Pad</source>
|
<source>D-Pad</source>
|
||||||
<translation type="unfinished">D-Pad</translation>
|
<translation>D-패드</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Up</source>
|
<source>Up</source>
|
||||||
<translation type="unfinished">Up</translation>
|
<translation>위</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Left</source>
|
<source>Left</source>
|
||||||
<translation type="unfinished">Left</translation>
|
<translation>왼쪽</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Right</source>
|
<source>Right</source>
|
||||||
<translation type="unfinished">Right</translation>
|
<translation>오른쪽</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Down</source>
|
<source>Down</source>
|
||||||
<translation type="unfinished">Down</translation>
|
<translation>아래</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Left Stick Deadzone (def:2 max:127)</source>
|
<source>Left Stick Deadzone (def:2 max:127)</source>
|
||||||
<translation type="unfinished">Left Stick Deadzone (def:2 max:127)</translation>
|
<translation>왼쪽 스틱 데드존 (기본값:2 최대값:127)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Left Deadzone</source>
|
<source>Left Deadzone</source>
|
||||||
<translation type="unfinished">Left Deadzone</translation>
|
<translation>왼쪽 데드존</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Left Stick</source>
|
<source>Left Stick</source>
|
||||||
<translation type="unfinished">Left Stick</translation>
|
<translation>왼쪽 스틱</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Config Selection</source>
|
<source>Config Selection</source>
|
||||||
<translation type="unfinished">Config Selection</translation>
|
<translation>설정 선택</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Common Config</source>
|
<source>Common Config</source>
|
||||||
<translation type="unfinished">Common Config</translation>
|
<translation>공통 설정</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Use per-game configs</source>
|
<source>Use per-game configs</source>
|
||||||
<translation type="unfinished">Use per-game configs</translation>
|
<translation>게임 별 설정 사용</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>L1 / LB</source>
|
<source>L1 / LB</source>
|
||||||
<translation type="unfinished">L1 / LB</translation>
|
<translation>L1 / LB</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>L2 / LT</source>
|
<source>L2 / LT</source>
|
||||||
<translation type="unfinished">L2 / LT</translation>
|
<translation>L2 / LT</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Back</source>
|
<source>Back</source>
|
||||||
<translation type="unfinished">Back</translation>
|
<translation>뒤로</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>R1 / RB</source>
|
<source>R1 / RB</source>
|
||||||
<translation type="unfinished">R1 / RB</translation>
|
<translation>R1 / RB</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>R2 / RT</source>
|
<source>R2 / RT</source>
|
||||||
<translation type="unfinished">R2 / RT</translation>
|
<translation>R2 / RT</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>L3</source>
|
<source>L3</source>
|
||||||
<translation type="unfinished">L3</translation>
|
<translation>L3</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Options / Start</source>
|
<source>Options / Start</source>
|
||||||
<translation type="unfinished">Options / Start</translation>
|
<translation>옵션 / 시작</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>R3</source>
|
<source>R3</source>
|
||||||
<translation type="unfinished">R3</translation>
|
<translation>R3</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Face Buttons</source>
|
<source>Face Buttons</source>
|
||||||
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation type="unfinished">Game List</translation>
|
<translation type="unfinished">Game List</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation type="unfinished">Download Cheats For All Installed Games</translation>
|
<translation type="unfinished">Download Cheats For All Installed Games</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Žaidimų sąrašas</translation>
|
<translation>Žaidimų sąrašas</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Nepalaikoma Vulkan versija</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Atsisiųsti sukčiavimus visiems įdiegtiems žaidimams</translation>
|
<translation>Atsisiųsti sukčiavimus visiems įdiegtiems žaidimams</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1253,11 +1253,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>List View</source>
|
<source>List View</source>
|
||||||
<translation>Liste-visning</translation>
|
<translation>Listevisning</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Grid View</source>
|
<source>Grid View</source>
|
||||||
<translation>Rute-visning</translation>
|
<translation>Rutenettvisning</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Elf Viewer</source>
|
<source>Elf Viewer</source>
|
||||||
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Spilliste</translation>
|
<translation>Spilliste</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Ustøttet Vulkan-versjon</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Last ned juks for alle installerte spill</translation>
|
<translation>Last ned juks for alle installerte spill</translation>
|
||||||
|
@ -1760,7 +1756,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Loggfilter:\nFiltrerer loggen for å kun skrive ut spesifikk informasjon.\nEksempler: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical" \nNivåer: Trace, Debug, Info, Warning, Error, Critical - i denne rekkefølgen, et spesifikt nivå demper alle tidligere nivåer i lista og loggfører alle nivåer etter det.</translation>
|
<translation>Loggfilter:\nFiltrerer loggen for å kun skrive ut spesifikk informasjon.\nEksempler: «Core:Trace» «Lib.Pad:Debug Common.Filesystem:Error» «*:Critical» \nNivåer: Trace, Debug, Info, Warning, Error, Critical - i denne rekkefølgen, et spesifikt nivå demper alle tidligere nivåer i lista og loggfører alle nivåer etter det.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
|
@ -1792,7 +1788,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</source>
|
<source>Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information.</source>
|
||||||
<translation>Vis kompatibilitets-data:\nViser informasjon om spillkompatibilitet i tabellvisning. Bruk "Oppdater kompatibilitets-data ved oppstart" for oppdatert informasjon.</translation>
|
<translation>Vis kompatibilitets-data:\nViser informasjon om spillkompatibilitet i tabellvisning. Bruk «Oppdater database ved oppstart» for oppdatert informasjon.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</source>
|
<source>Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts.</source>
|
||||||
|
@ -1832,7 +1828,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Grafikkenhet:\nSystemer med flere GPU-er, kan emulatoren velge hvilken enhet som skal brukes fra rullegardinlista,\neller velg "Velg automatisk".</translation>
|
<translation>Grafikkenhet:\nSystemer med flere GPU-er, kan emulatoren velge hvilken enhet som skal brukes fra rullegardinlista,\neller bruk «Velg automatisk».</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Åpne mappa med tilpassede bilder og lyder for trofé:\nDu kan legge til tilpassede bilder til trofeer og en lyd.\nLegg filene til custom_trophy med følgende navn:\ntrophy.wav ELLER trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nMerk: Lyden avspilles kun i Qt-versjonen.</translation>
|
<translation>Åpne mappa med tilpassede bilder og lyder for trofé:\nDu kan legge til tilpassede bilder til trofeer og en lyd.\nLegg filene til custom_trophy med følgende navn:\ntrophy.wav ELLER trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nMerk: Lyden avspilles kun i Qt-versjonen.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation> *Ustøttet Vulkan-versjon</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Lijst met spellen</translation>
|
<translation>Lijst met spellen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Niet ondersteunde Vulkan-versie</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Download cheats voor alle geïnstalleerde spellen</translation>
|
<translation>Download cheats voor alle geïnstalleerde spellen</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Lista gier</translation>
|
<translation>Lista gier</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Nieobsługiwana wersja Vulkan</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Pobierz kody do wszystkich zainstalowanych gier</translation>
|
<translation>Pobierz kody do wszystkich zainstalowanych gier</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Otwórz niestandardowy folder obrazów/dźwięków:\nMożesz dodać własne obrazy dla trofeów i ich dźwięki.\nDodaj pliki do custom_trophy o następujących nazwach:\ntrophy.wav LUB trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nUwaga: Dźwięki działają tylko w wersji QT.</translation>
|
<translation>Otwórz niestandardowy folder obrazów/dźwięków:\nMożesz dodać własne obrazy dla trofeów i ich dźwięki.\nDodaj pliki do custom_trophy o następujących nazwach:\ntrophy.wav LUB trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nUwaga: Dźwięki działają tylko w wersji QT.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Lista de Jogos</translation>
|
<translation>Lista de Jogos</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Versão Vulkan não suportada</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Baixar Trapaças para Todos os Jogos Instalados</translation>
|
<translation>Baixar Trapaças para Todos os Jogos Instalados</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Abrir a pasta de imagens e sons de troféus personalizados:\nVocê pode adicionar imagens personalizadas aos troféus e um áudio.\nAdicione os arquivos na pasta custom_trophy com os seguintes nomes:\ntrophy.wav OU trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nObservação: O som funcionará apenas em versões Qt.</translation>
|
<translation>Abrir a pasta de imagens e sons de troféus personalizados:\nVocê pode adicionar imagens personalizadas aos troféus e um áudio.\nAdicione os arquivos na pasta custom_trophy com os seguintes nomes:\ntrophy.wav OU trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nObservação: O som funcionará apenas em versões Qt.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -543,7 +543,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Não é possível salvar</translation>
|
<translation>Não foi possível guardar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Cannot bind axis values more than once</source>
|
<source>Cannot bind axis values more than once</source>
|
||||||
|
@ -551,7 +551,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>Salvar</translation>
|
<translation>Guardar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Apply</source>
|
<source>Apply</source>
|
||||||
|
@ -559,7 +559,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Restore Defaults</source>
|
<source>Restore Defaults</source>
|
||||||
<translation>Restaurar o Padrão</translation>
|
<translation>Restaurar Predefinições</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
|
@ -570,11 +570,11 @@
|
||||||
<name>EditorDialog</name>
|
<name>EditorDialog</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Edit Keyboard + Mouse and Controller input bindings</source>
|
<source>Edit Keyboard + Mouse and Controller input bindings</source>
|
||||||
<translation>Editar comandos do Teclado + Mouse e do Controle</translation>
|
<translation>Editar configurações de entrada do Teclado + Rato e do Comando</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Use Per-Game configs</source>
|
<source>Use Per-Game configs</source>
|
||||||
<translation>Use uma configuração para cada jogo</translation>
|
<translation>Utilizar configurações por jogo</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Error</source>
|
<source>Error</source>
|
||||||
|
@ -582,19 +582,19 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not open the file for reading</source>
|
<source>Could not open the file for reading</source>
|
||||||
<translation>Não foi possível abrir o arquivo para ler</translation>
|
<translation>Não foi possível abrir o ficheiro para leitura</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Could not open the file for writing</source>
|
<source>Could not open the file for writing</source>
|
||||||
<translation>Não foi possível abrir o arquivo para escrever</translation>
|
<translation>Não foi possível abrir o ficheiro para escrita</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Save Changes</source>
|
<source>Save Changes</source>
|
||||||
<translation>Salvar mudanças</translation>
|
<translation>Guardar as alterações</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Do you want to save changes?</source>
|
<source>Do you want to save changes?</source>
|
||||||
<translation>Salvar as mudanças?</translation>
|
<translation>Pretende guardar as alterações?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Help</source>
|
<source>Help</source>
|
||||||
|
@ -610,7 +610,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Reset to Default</source>
|
<source>Reset to Default</source>
|
||||||
<translation>Resetar ao Padrão</translation>
|
<translation>Repor para o Padrão</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -1150,7 +1150,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unable to Save</source>
|
<source>Unable to Save</source>
|
||||||
<translation>Não é possível salvar</translation>
|
<translation>Não foi possível guardar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Cannot bind any unique input more than once</source>
|
<source>Cannot bind any unique input more than once</source>
|
||||||
|
@ -1166,11 +1166,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Mousewheel cannot be mapped to stick outputs</source>
|
<source>Mousewheel cannot be mapped to stick outputs</source>
|
||||||
<translation>Roda do rato não pode ser mapeada para saídas empates</translation>
|
<translation>Roda do rato não pode ser mapeada para saídas dos manípulos</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<translation>Salvar</translation>
|
<translation>Guardar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Apply</source>
|
<source>Apply</source>
|
||||||
|
@ -1178,7 +1178,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Restore Defaults</source>
|
<source>Restore Defaults</source>
|
||||||
<translation>Restaurar Definições</translation>
|
<translation>Restaurar Predefinições</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Lista de Jogos</translation>
|
<translation>Lista de Jogos</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Versão do Vulkan não suportada</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Transferir Cheats para Todos os Jogos Instalados</translation>
|
<translation>Transferir Cheats para Todos os Jogos Instalados</translation>
|
||||||
|
@ -1409,43 +1405,43 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Play</source>
|
<source>Play</source>
|
||||||
<translation type="unfinished">Play</translation>
|
<translation>Reproduzir</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Pause</source>
|
<source>Pause</source>
|
||||||
<translation type="unfinished">Pause</translation>
|
<translation>Pausa</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Stop</source>
|
<source>Stop</source>
|
||||||
<translation type="unfinished">Stop</translation>
|
<translation>Parar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Restart</source>
|
<source>Restart</source>
|
||||||
<translation type="unfinished">Restart</translation>
|
<translation>Reiniciar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Full Screen</source>
|
<source>Full Screen</source>
|
||||||
<translation type="unfinished">Full Screen</translation>
|
<translation>Ecrã Inteiro</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Controllers</source>
|
<source>Controllers</source>
|
||||||
<translation type="unfinished">Controllers</translation>
|
<translation>Comandos</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Keyboard</source>
|
<source>Keyboard</source>
|
||||||
<translation type="unfinished">Keyboard</translation>
|
<translation>Teclado</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Refresh List</source>
|
<source>Refresh List</source>
|
||||||
<translation type="unfinished">Refresh List</translation>
|
<translation>Atualizar Lista</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Resume</source>
|
<source>Resume</source>
|
||||||
<translation type="unfinished">Resume</translation>
|
<translation>Continuar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Show Labels Under Icons</source>
|
<source>Show Labels Under Icons</source>
|
||||||
<translation type="unfinished">Show Labels Under Icons</translation>
|
<translation>Mostrar Etiquetas Debaixo dos Ícones</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -2032,7 +2028,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Cannot create portable user folder</source>
|
<source>Cannot create portable user folder</source>
|
||||||
<translation>Não é possível criar pasta de utilizador portátil</translation>
|
<translation>Não foi possível criar pasta de utilizador portátil</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>%1 already exists</source>
|
<source>%1 already exists</source>
|
||||||
|
@ -2048,7 +2044,11 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Abra a pasta de imagens/sons de troféus personalizados:\nPoderá adicionar imagens personalizadas aos troféus e um áudio.\nAdicione os arquivos na pasta custom_trophy com os seguintes nomes:\ntrophy.mp3 ou trophy.wav, bronze.png, gold.png, platinum.png, silver.png\nObservação: O som funcionará apenas nas versões Qt.</translation>
|
<translation>Abra a pasta de imagens/sons de troféus personalizados:\nPoderá adicionar imagens personalizadas aos troféus e um áudio.\nAdicione os ficheiros na pasta custom_trophy com os seguintes nomes:\ntrophy.mp3 ou trophy.wav, bronze.png, gold.png, platinum.png, silver.png\nObservação: O som funcionará apenas nas versões Qt.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation> * Versão do Vulkan não suportada</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Lista jocurilor</translation>
|
<translation>Lista jocurilor</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Versiune Vulkan nesuportată</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Descarcă Cheats pentru toate jocurile instalate</translation>
|
<translation>Descarcă Cheats pentru toate jocurile instalate</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Список игр</translation>
|
<translation>Список игр</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Неподдерживаемая версия Vulkan</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Скачать читы для всех установленных игр</translation>
|
<translation>Скачать читы для всех установленных игр</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Открыть папку с пользовательскими изображениями/звуками трофеев:\nВы можете добавить пользовательские изображения к трофеям и аудио.\nДобавьте файлы в custom_trophy со следующими именами:\ntrophy.wav ИЛИ trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nПримечание: звук будет работать только в QT-версии.</translation>
|
<translation>Открыть папку с пользовательскими изображениями/звуками трофеев:\nВы можете добавить пользовательские изображения к трофеям и аудио.\nДобавьте файлы в custom_trophy со следующими именами:\ntrophy.wav ИЛИ trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nПримечание: звук будет работать только в QT-версии.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation> * Неподдерживаемая версия Vulkan</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation type="unfinished">Game List</translation>
|
<translation type="unfinished">Game List</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation type="unfinished">Download Cheats For All Installed Games</translation>
|
<translation type="unfinished">Download Cheats For All Installed Games</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Lista e lojërave</translation>
|
<translation>Lista e lojërave</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Version i pambështetur i Vulkan</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Shkarko mashtrime për të gjitha lojërat e instaluara</translation>
|
<translation>Shkarko mashtrime për të gjitha lojërat e instaluara</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Hap dosjen e imazheve/tingujve të trofeve të personalizuar:\nMund të shtosh imazhe të personalizuara për trofetë dhe një audio.\nShto skedarët në dosjen custom_trophy me emrat që vijojnë:\ntrophy.wav ose trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nShënim: Tingulli do të punojë vetëm në versionet QT.</translation>
|
<translation>Hap dosjen e imazheve/tingujve të trofeve të personalizuar:\nMund të shtosh imazhe të personalizuara për trofetë dhe një audio.\nShto skedarët në dosjen custom_trophy me emrat që vijojnë:\ntrophy.wav ose trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nShënim: Tingulli do të punojë vetëm në versionet QT.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation> * Version i pambështetur i Vulkan</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Spellista</translation>
|
<translation>Spellista</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Vulkan-versionen stöds inte</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Hämta fusk för alla installerade spel</translation>
|
<translation>Hämta fusk för alla installerade spel</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Öppna mappen för anpassade trofébilder/ljud:\nDu kan lägga till egna bilder till troféerna och ett ljud.\nLägg till filerna i custom_trophy med följande namn:\ntrophy.wav ELLER trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nObservera: Ljudet fungerar endast i QT-versioner.</translation>
|
<translation>Öppna mappen för anpassade trofébilder/ljud:\nDu kan lägga till egna bilder till troféerna och ett ljud.\nLägg till filerna i custom_trophy med följande namn:\ntrophy.wav ELLER trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nObservera: Ljudet fungerar endast i QT-versioner.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation> * Versionen av Vulkan stöds inte</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Oyun Listesi</translation>
|
<translation>Oyun Listesi</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Desteklenmeyen Vulkan Sürümü</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Tüm Yüklenmiş Oyunlar İçin Hileleri İndir</translation>
|
<translation>Tüm Yüklenmiş Oyunlar İçin Hileleri İndir</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Özel kupa görüntüleri/sesleri klasörünü aç:\nKupalara özel görüntüler ve sesler ekleyebilirsiniz.\nDosyaları aşağıdaki adlarla custom_trophy'ye ekleyin:\ntrophy.wav ya da trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNot: Ses yalnızca QT sürümlerinde çalışacaktır.</translation>
|
<translation>Özel kupa görüntüleri/sesleri klasörünü aç:\nKupalara özel görüntüler ve sesler ekleyebilirsiniz.\nDosyaları aşağıdaki adlarla custom_trophy'ye ekleyin:\ntrophy.wav ya da trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNot: Ses yalnızca QT sürümlerinde çalışacaktır.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Список ігор</translation>
|
<translation>Список ігор</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Непідтримувана версія Vulkan</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Завантажити чити для усіх встановлених ігор</translation>
|
<translation>Завантажити чити для усіх встановлених ігор</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>Відкрити папку користувацьких зображень трофеїв/звуків:\nВи можете додати користувацькі зображення до трофеїв та звук.\nДодайте файли до теки custom_trophy з такими назвами:\ntrophy.wav АБО trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nПримітка: Звук буде працювати лише у версіях ShadPS4 з графічним інтерфейсом.</translation>
|
<translation>Відкрити папку користувацьких зображень трофеїв/звуків:\nВи можете додати користувацькі зображення до трофеїв та звук.\nДодайте файли до теки custom_trophy з такими назвами:\ntrophy.wav АБО trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nПримітка: Звук буде працювати лише у версіях ShadPS4 з графічним інтерфейсом.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>Danh sách trò chơi</translation>
|
<translation>Danh sách trò chơi</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * Phiên bản Vulkan không được hỗ trợ</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>Tải xuống cheat cho tất cả các trò chơi đã cài đặt</translation>
|
<translation>Tải xuống cheat cho tất cả các trò chơi đã cài đặt</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation type="unfinished">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.</translation>
|
<translation type="unfinished">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.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>游戏列表</translation>
|
<translation>游戏列表</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * 不支持的 Vulkan 版本</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>下载所有已安装游戏的作弊码</translation>
|
<translation>下载所有已安装游戏的作弊码</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>打开自定义奖杯图像/声音文件夹:\n您可以自定义奖杯图像和声音。\n将文件添加到 custom_trophy 文件夹中,文件名如下:\ntrophy.wav 或 trophy.mp3、bronze.png、gold.png、platinum.png、silver.png。\n注意:自定义声音只能在 QT 版本中生效。</translation>
|
<translation>打开自定义奖杯图像/声音文件夹:\n您可以自定义奖杯图像和声音。\n将文件添加到 custom_trophy 文件夹中,文件名如下:\ntrophy.wav 或 trophy.mp3、bronze.png、gold.png、platinum.png、silver.png。\n注意:自定义声音只能在 QT 版本中生效。</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation> * 不支持的 Vulkan 版本</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -1347,10 +1347,6 @@
|
||||||
<source>Game List</source>
|
<source>Game List</source>
|
||||||
<translation>遊戲列表</translation>
|
<translation>遊戲列表</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source> * Unsupported Vulkan Version</source>
|
|
||||||
<translation> * 不支援的 Vulkan 版本</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Download Cheats For All Installed Games</source>
|
<source>Download Cheats For All Installed Games</source>
|
||||||
<translation>下載所有已安裝遊戲的金手指</translation>
|
<translation>下載所有已安裝遊戲的金手指</translation>
|
||||||
|
@ -2050,6 +2046,10 @@
|
||||||
<source>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.</source>
|
<source>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.</source>
|
||||||
<translation>開啟自訂獎盃影像/聲音資料夾:\n您可以將自訂影像新增至獎盃和音訊。 \n將檔案加入 custom_tropy,名稱如下:\ntropy.wav OR trophy.mp3、bronze.png、gold.png、platinum.png、silver.png\n注意:聲音僅在 QT 版本中有效。</translation>
|
<translation>開啟自訂獎盃影像/聲音資料夾:\n您可以將自訂影像新增至獎盃和音訊。 \n將檔案加入 custom_tropy,名稱如下:\ntropy.wav OR trophy.mp3、bronze.png、gold.png、platinum.png、silver.png\n注意:聲音僅在 QT 版本中有效。</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source> * Unsupported Vulkan Version</source>
|
||||||
|
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>TrophyViewer</name>
|
<name>TrophyViewer</name>
|
||||||
|
|
|
@ -154,6 +154,7 @@ void Traverse(EmitContext& ctx, const IR::Program& program) {
|
||||||
for (IR::Inst& inst : node.data.block->Instructions()) {
|
for (IR::Inst& inst : node.data.block->Instructions()) {
|
||||||
EmitInst(ctx, &inst);
|
EmitInst(ctx, &inst);
|
||||||
}
|
}
|
||||||
|
ctx.first_to_last_label_map[label.value] = ctx.last_label;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IR::AbstractSyntaxNode::Type::If: {
|
case IR::AbstractSyntaxNode::Type::If: {
|
||||||
|
@ -298,6 +299,10 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct
|
||||||
if (stage == LogicalStage::TessellationControl || stage == LogicalStage::TessellationEval) {
|
if (stage == LogicalStage::TessellationControl || stage == LogicalStage::TessellationEval) {
|
||||||
ctx.AddCapability(spv::Capability::Tessellation);
|
ctx.AddCapability(spv::Capability::Tessellation);
|
||||||
}
|
}
|
||||||
|
if (info.dma_types != IR::Type::Void) {
|
||||||
|
ctx.AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
|
||||||
|
ctx.AddExtension("SPV_KHR_physical_storage_buffer");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefineEntryPoint(const Info& info, EmitContext& ctx, Id main) {
|
void DefineEntryPoint(const Info& info, EmitContext& ctx, Id main) {
|
||||||
|
@ -387,7 +392,7 @@ void SetupFloatMode(EmitContext& ctx, const Profile& profile, const RuntimeInfo&
|
||||||
void PatchPhiNodes(const IR::Program& program, EmitContext& ctx) {
|
void PatchPhiNodes(const IR::Program& program, EmitContext& ctx) {
|
||||||
auto inst{program.blocks.front()->begin()};
|
auto inst{program.blocks.front()->begin()};
|
||||||
size_t block_index{0};
|
size_t block_index{0};
|
||||||
ctx.PatchDeferredPhi([&](size_t phi_arg) {
|
ctx.PatchDeferredPhi([&](u32 phi_arg, Id first_parent) {
|
||||||
if (phi_arg == 0) {
|
if (phi_arg == 0) {
|
||||||
++inst;
|
++inst;
|
||||||
if (inst == program.blocks[block_index]->end() ||
|
if (inst == program.blocks[block_index]->end() ||
|
||||||
|
@ -398,7 +403,9 @@ void PatchPhiNodes(const IR::Program& program, EmitContext& ctx) {
|
||||||
} while (inst->GetOpcode() != IR::Opcode::Phi);
|
} while (inst->GetOpcode() != IR::Opcode::Phi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ctx.Def(inst->Arg(phi_arg));
|
const Id arg = ctx.Def(inst->Arg(phi_arg));
|
||||||
|
const Id parent = ctx.first_to_last_label_map[first_parent.value];
|
||||||
|
return std::make_pair(arg, parent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
|
@ -60,7 +60,7 @@ Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id
|
||||||
address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset);
|
address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset);
|
||||||
}
|
}
|
||||||
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
|
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
|
||||||
const auto [id, pointer_type] = buffer[EmitContext::BufferAlias::U32];
|
const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32];
|
||||||
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index);
|
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index);
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||||
return BufferAtomicU32BoundsCheck(ctx, index, buffer.size_dwords, [&] {
|
return BufferAtomicU32BoundsCheck(ctx, index, buffer.size_dwords, [&] {
|
||||||
|
@ -257,7 +257,7 @@ Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id co
|
||||||
|
|
||||||
Id EmitDataAppend(EmitContext& ctx, u32 gds_addr, u32 binding) {
|
Id EmitDataAppend(EmitContext& ctx, u32 gds_addr, u32 binding) {
|
||||||
const auto& buffer = ctx.buffers[binding];
|
const auto& buffer = ctx.buffers[binding];
|
||||||
const auto [id, pointer_type] = buffer[EmitContext::BufferAlias::U32];
|
const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32];
|
||||||
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, ctx.ConstU32(gds_addr));
|
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, ctx.ConstU32(gds_addr));
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||||
return ctx.OpAtomicIIncrement(ctx.U32[1], ptr, scope, semantics);
|
return ctx.OpAtomicIIncrement(ctx.U32[1], ptr, scope, semantics);
|
||||||
|
@ -265,7 +265,7 @@ Id EmitDataAppend(EmitContext& ctx, u32 gds_addr, u32 binding) {
|
||||||
|
|
||||||
Id EmitDataConsume(EmitContext& ctx, u32 gds_addr, u32 binding) {
|
Id EmitDataConsume(EmitContext& ctx, u32 gds_addr, u32 binding) {
|
||||||
const auto& buffer = ctx.buffers[binding];
|
const auto& buffer = ctx.buffers[binding];
|
||||||
const auto [id, pointer_type] = buffer[EmitContext::BufferAlias::U32];
|
const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32];
|
||||||
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, ctx.ConstU32(gds_addr));
|
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, ctx.ConstU32(gds_addr));
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||||
return ctx.OpAtomicIDecrement(ctx.U32[1], ptr, scope, semantics);
|
return ctx.OpAtomicIDecrement(ctx.U32[1], ptr, scope, semantics);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||||
#include "shader_recompiler/ir/attribute.h"
|
#include "shader_recompiler/ir/attribute.h"
|
||||||
|
@ -160,32 +161,37 @@ void EmitGetGotoVariable(EmitContext&) {
|
||||||
UNREACHABLE_MSG("Unreachable instruction");
|
UNREACHABLE_MSG("Unreachable instruction");
|
||||||
}
|
}
|
||||||
|
|
||||||
using BufferAlias = EmitContext::BufferAlias;
|
using PointerType = EmitContext::PointerType;
|
||||||
|
|
||||||
Id EmitReadConst(EmitContext& ctx, IR::Inst* inst) {
|
Id EmitReadConst(EmitContext& ctx, IR::Inst* inst, Id addr, Id offset) {
|
||||||
const u32 flatbuf_off_dw = inst->Flags<u32>();
|
const u32 flatbuf_off_dw = inst->Flags<u32>();
|
||||||
const auto& srt_flatbuf = ctx.buffers.back();
|
// We can only provide a fallback for immediate offsets.
|
||||||
ASSERT(srt_flatbuf.binding >= 0 && flatbuf_off_dw > 0 &&
|
if (flatbuf_off_dw == 0) {
|
||||||
srt_flatbuf.buffer_type == BufferType::ReadConstUbo);
|
return ctx.OpFunctionCall(ctx.U32[1], ctx.read_const_dynamic, addr, offset);
|
||||||
const auto [id, pointer_type] = srt_flatbuf[BufferAlias::U32];
|
} else {
|
||||||
const Id ptr{
|
return ctx.OpFunctionCall(ctx.U32[1], ctx.read_const, addr, offset,
|
||||||
ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, ctx.ConstU32(flatbuf_off_dw))};
|
ctx.ConstU32(flatbuf_off_dw));
|
||||||
return ctx.OpLoad(ctx.U32[1], ptr);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitReadConstBuffer(EmitContext& ctx, u32 handle, Id index) {
|
template <PointerType type>
|
||||||
|
Id ReadConstBuffer(EmitContext& ctx, u32 handle, Id index) {
|
||||||
const auto& buffer = ctx.buffers[handle];
|
const auto& buffer = ctx.buffers[handle];
|
||||||
index = ctx.OpIAdd(ctx.U32[1], index, buffer.offset_dwords);
|
index = ctx.OpIAdd(ctx.U32[1], index, buffer.offset_dwords);
|
||||||
const auto [id, pointer_type] = buffer[BufferAlias::U32];
|
const auto [id, pointer_type] = buffer[type];
|
||||||
|
const auto value_type = type == PointerType::U32 ? ctx.U32[1] : ctx.F32[1];
|
||||||
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)};
|
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)};
|
||||||
const Id result{ctx.OpLoad(ctx.U32[1], ptr)};
|
const Id result{ctx.OpLoad(value_type, ptr)};
|
||||||
|
|
||||||
if (Sirit::ValidId(buffer.size_dwords)) {
|
if (Sirit::ValidId(buffer.size_dwords)) {
|
||||||
const Id in_bounds = ctx.OpULessThan(ctx.U1[1], index, buffer.size_dwords);
|
const Id in_bounds = ctx.OpULessThan(ctx.U1[1], index, buffer.size_dwords);
|
||||||
return ctx.OpSelect(ctx.U32[1], in_bounds, result, ctx.u32_zero_value);
|
return ctx.OpSelect(value_type, in_bounds, result, ctx.u32_zero_value);
|
||||||
} else {
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Id EmitReadConstBuffer(EmitContext& ctx, u32 handle, Id index) {
|
||||||
|
return ReadConstBuffer<PointerType::U32>(ctx, handle, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitReadStepRate(EmitContext& ctx, int rate_idx) {
|
Id EmitReadStepRate(EmitContext& ctx, int rate_idx) {
|
||||||
|
@ -244,7 +250,7 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index) {
|
||||||
ctx.OpUDiv(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id), step_rate),
|
ctx.OpUDiv(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id), step_rate),
|
||||||
ctx.ConstU32(param.num_components)),
|
ctx.ConstU32(param.num_components)),
|
||||||
ctx.ConstU32(comp));
|
ctx.ConstU32(comp));
|
||||||
return EmitReadConstBuffer(ctx, param.buffer_handle, offset);
|
return ReadConstBuffer<PointerType::F32>(ctx, param.buffer_handle, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id result;
|
Id result;
|
||||||
|
@ -430,7 +436,7 @@ static Id EmitLoadBufferBoundsCheck(EmitContext& ctx, Id index, Id buffer_size,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <u32 N, BufferAlias alias>
|
template <u32 N, PointerType alias>
|
||||||
static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
const auto flags = inst->Flags<IR::BufferInstInfo>();
|
const auto flags = inst->Flags<IR::BufferInstInfo>();
|
||||||
const auto& spv_buffer = ctx.buffers[handle];
|
const auto& spv_buffer = ctx.buffers[handle];
|
||||||
|
@ -438,7 +444,7 @@ static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id a
|
||||||
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
||||||
}
|
}
|
||||||
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
|
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
|
||||||
const auto& data_types = alias == BufferAlias::U32 ? ctx.U32 : ctx.F32;
|
const auto& data_types = alias == PointerType::U32 ? ctx.U32 : ctx.F32;
|
||||||
const auto [id, pointer_type] = spv_buffer[alias];
|
const auto [id, pointer_type] = spv_buffer[alias];
|
||||||
|
|
||||||
boost::container::static_vector<Id, N> ids;
|
boost::container::static_vector<Id, N> ids;
|
||||||
|
@ -449,7 +455,7 @@ static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id a
|
||||||
if (!flags.typed) {
|
if (!flags.typed) {
|
||||||
// Untyped loads have bounds checking per-component.
|
// Untyped loads have bounds checking per-component.
|
||||||
ids.push_back(EmitLoadBufferBoundsCheck<1>(ctx, index_i, spv_buffer.size_dwords,
|
ids.push_back(EmitLoadBufferBoundsCheck<1>(ctx, index_i, spv_buffer.size_dwords,
|
||||||
result_i, alias == BufferAlias::F32));
|
result_i, alias == PointerType::F32));
|
||||||
} else {
|
} else {
|
||||||
ids.push_back(result_i);
|
ids.push_back(result_i);
|
||||||
}
|
}
|
||||||
|
@ -459,7 +465,7 @@ static Id EmitLoadBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id a
|
||||||
if (flags.typed) {
|
if (flags.typed) {
|
||||||
// Typed loads have single bounds check for the whole load.
|
// Typed loads have single bounds check for the whole load.
|
||||||
return EmitLoadBufferBoundsCheck<N>(ctx, index, spv_buffer.size_dwords, result,
|
return EmitLoadBufferBoundsCheck<N>(ctx, index, spv_buffer.size_dwords, result,
|
||||||
alias == BufferAlias::F32);
|
alias == PointerType::F32);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -469,7 +475,7 @@ Id EmitLoadBufferU8(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
if (Sirit::ValidId(spv_buffer.offset)) {
|
if (Sirit::ValidId(spv_buffer.offset)) {
|
||||||
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
||||||
}
|
}
|
||||||
const auto [id, pointer_type] = spv_buffer[BufferAlias::U8];
|
const auto [id, pointer_type] = spv_buffer[PointerType::U8];
|
||||||
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)};
|
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)};
|
||||||
const Id result{ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, ptr))};
|
const Id result{ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, ptr))};
|
||||||
return EmitLoadBufferBoundsCheck<1>(ctx, address, spv_buffer.size, result, false);
|
return EmitLoadBufferBoundsCheck<1>(ctx, address, spv_buffer.size, result, false);
|
||||||
|
@ -480,7 +486,7 @@ Id EmitLoadBufferU16(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
if (Sirit::ValidId(spv_buffer.offset)) {
|
if (Sirit::ValidId(spv_buffer.offset)) {
|
||||||
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
||||||
}
|
}
|
||||||
const auto [id, pointer_type] = spv_buffer[BufferAlias::U16];
|
const auto [id, pointer_type] = spv_buffer[PointerType::U16];
|
||||||
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(1u));
|
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(1u));
|
||||||
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)};
|
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)};
|
||||||
const Id result{ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, ptr))};
|
const Id result{ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, ptr))};
|
||||||
|
@ -488,35 +494,35 @@ Id EmitLoadBufferU16(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
Id EmitLoadBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
return EmitLoadBufferB32xN<1, BufferAlias::U32>(ctx, inst, handle, address);
|
return EmitLoadBufferB32xN<1, PointerType::U32>(ctx, inst, handle, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadBufferU32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
Id EmitLoadBufferU32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
return EmitLoadBufferB32xN<2, BufferAlias::U32>(ctx, inst, handle, address);
|
return EmitLoadBufferB32xN<2, PointerType::U32>(ctx, inst, handle, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadBufferU32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
Id EmitLoadBufferU32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
return EmitLoadBufferB32xN<3, BufferAlias::U32>(ctx, inst, handle, address);
|
return EmitLoadBufferB32xN<3, PointerType::U32>(ctx, inst, handle, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
Id EmitLoadBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
return EmitLoadBufferB32xN<4, BufferAlias::U32>(ctx, inst, handle, address);
|
return EmitLoadBufferB32xN<4, PointerType::U32>(ctx, inst, handle, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
return EmitLoadBufferB32xN<1, BufferAlias::F32>(ctx, inst, handle, address);
|
return EmitLoadBufferB32xN<1, PointerType::F32>(ctx, inst, handle, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
Id EmitLoadBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
return EmitLoadBufferB32xN<2, BufferAlias::F32>(ctx, inst, handle, address);
|
return EmitLoadBufferB32xN<2, PointerType::F32>(ctx, inst, handle, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
Id EmitLoadBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
return EmitLoadBufferB32xN<3, BufferAlias::F32>(ctx, inst, handle, address);
|
return EmitLoadBufferB32xN<3, PointerType::F32>(ctx, inst, handle, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadBufferF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
Id EmitLoadBufferF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
return EmitLoadBufferB32xN<4, BufferAlias::F32>(ctx, inst, handle, address);
|
return EmitLoadBufferB32xN<4, PointerType::F32>(ctx, inst, handle, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
Id EmitLoadBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||||
|
@ -546,7 +552,7 @@ void EmitStoreBufferBoundsCheck(EmitContext& ctx, Id index, Id buffer_size, auto
|
||||||
emit_func();
|
emit_func();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <u32 N, BufferAlias alias>
|
template <u32 N, PointerType alias>
|
||||||
static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address,
|
static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address,
|
||||||
Id value) {
|
Id value) {
|
||||||
const auto flags = inst->Flags<IR::BufferInstInfo>();
|
const auto flags = inst->Flags<IR::BufferInstInfo>();
|
||||||
|
@ -555,7 +561,7 @@ static void EmitStoreBufferB32xN(EmitContext& ctx, IR::Inst* inst, u32 handle, I
|
||||||
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
||||||
}
|
}
|
||||||
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
|
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
|
||||||
const auto& data_types = alias == BufferAlias::U32 ? ctx.U32 : ctx.F32;
|
const auto& data_types = alias == PointerType::U32 ? ctx.U32 : ctx.F32;
|
||||||
const auto [id, pointer_type] = spv_buffer[alias];
|
const auto [id, pointer_type] = spv_buffer[alias];
|
||||||
|
|
||||||
auto store = [&] {
|
auto store = [&] {
|
||||||
|
@ -586,7 +592,7 @@ void EmitStoreBufferU8(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id v
|
||||||
if (Sirit::ValidId(spv_buffer.offset)) {
|
if (Sirit::ValidId(spv_buffer.offset)) {
|
||||||
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
||||||
}
|
}
|
||||||
const auto [id, pointer_type] = spv_buffer[BufferAlias::U8];
|
const auto [id, pointer_type] = spv_buffer[PointerType::U8];
|
||||||
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)};
|
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, address)};
|
||||||
const Id result{ctx.OpUConvert(ctx.U8, value)};
|
const Id result{ctx.OpUConvert(ctx.U8, value)};
|
||||||
EmitStoreBufferBoundsCheck<1>(ctx, address, spv_buffer.size, [&] { ctx.OpStore(ptr, result); });
|
EmitStoreBufferBoundsCheck<1>(ctx, address, spv_buffer.size, [&] { ctx.OpStore(ptr, result); });
|
||||||
|
@ -597,7 +603,7 @@ void EmitStoreBufferU16(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id
|
||||||
if (Sirit::ValidId(spv_buffer.offset)) {
|
if (Sirit::ValidId(spv_buffer.offset)) {
|
||||||
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
address = ctx.OpIAdd(ctx.U32[1], address, spv_buffer.offset);
|
||||||
}
|
}
|
||||||
const auto [id, pointer_type] = spv_buffer[BufferAlias::U16];
|
const auto [id, pointer_type] = spv_buffer[PointerType::U16];
|
||||||
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(1u));
|
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(1u));
|
||||||
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)};
|
const Id ptr{ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index)};
|
||||||
const Id result{ctx.OpUConvert(ctx.U16, value)};
|
const Id result{ctx.OpUConvert(ctx.U16, value)};
|
||||||
|
@ -606,35 +612,35 @@ void EmitStoreBufferU16(EmitContext& ctx, IR::Inst*, u32 handle, Id address, Id
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitStoreBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
void EmitStoreBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
EmitStoreBufferB32xN<1, BufferAlias::U32>(ctx, inst, handle, address, value);
|
EmitStoreBufferB32xN<1, PointerType::U32>(ctx, inst, handle, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitStoreBufferU32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
void EmitStoreBufferU32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
EmitStoreBufferB32xN<2, BufferAlias::U32>(ctx, inst, handle, address, value);
|
EmitStoreBufferB32xN<2, PointerType::U32>(ctx, inst, handle, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitStoreBufferU32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
void EmitStoreBufferU32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
EmitStoreBufferB32xN<3, BufferAlias::U32>(ctx, inst, handle, address, value);
|
EmitStoreBufferB32xN<3, PointerType::U32>(ctx, inst, handle, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitStoreBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
void EmitStoreBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
EmitStoreBufferB32xN<4, BufferAlias::U32>(ctx, inst, handle, address, value);
|
EmitStoreBufferB32xN<4, PointerType::U32>(ctx, inst, handle, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitStoreBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
void EmitStoreBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
EmitStoreBufferB32xN<1, BufferAlias::F32>(ctx, inst, handle, address, value);
|
EmitStoreBufferB32xN<1, PointerType::F32>(ctx, inst, handle, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitStoreBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
void EmitStoreBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
EmitStoreBufferB32xN<2, BufferAlias::F32>(ctx, inst, handle, address, value);
|
EmitStoreBufferB32xN<2, PointerType::F32>(ctx, inst, handle, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitStoreBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
void EmitStoreBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
EmitStoreBufferB32xN<3, BufferAlias::F32>(ctx, inst, handle, address, value);
|
EmitStoreBufferB32xN<3, PointerType::F32>(ctx, inst, handle, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitStoreBufferF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
void EmitStoreBufferF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
EmitStoreBufferB32xN<4, BufferAlias::F32>(ctx, inst, handle, address, value);
|
EmitStoreBufferB32xN<4, PointerType::F32>(ctx, inst, handle, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitStoreBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
void EmitStoreBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value) {
|
||||||
|
|
|
@ -154,7 +154,7 @@ Id EmitFPRecip32(EmitContext& ctx, Id value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitFPRecip64(EmitContext& ctx, Id value) {
|
Id EmitFPRecip64(EmitContext& ctx, Id value) {
|
||||||
return ctx.OpFDiv(ctx.F64[1], ctx.Constant(ctx.F64[1], 1.0f), value);
|
return ctx.OpFDiv(ctx.F64[1], ctx.Constant(ctx.F64[1], f64{1.0}), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitFPRecipSqrt32(EmitContext& ctx, Id value) {
|
Id EmitFPRecipSqrt32(EmitContext& ctx, Id value) {
|
||||||
|
|
|
@ -61,7 +61,7 @@ void EmitSetVectorRegister(EmitContext& ctx);
|
||||||
void EmitSetGotoVariable(EmitContext& ctx);
|
void EmitSetGotoVariable(EmitContext& ctx);
|
||||||
void EmitGetGotoVariable(EmitContext& ctx);
|
void EmitGetGotoVariable(EmitContext& ctx);
|
||||||
void EmitSetScc(EmitContext& ctx);
|
void EmitSetScc(EmitContext& ctx);
|
||||||
Id EmitReadConst(EmitContext& ctx, IR::Inst* inst);
|
Id EmitReadConst(EmitContext& ctx, IR::Inst* inst, Id addr, Id offset);
|
||||||
Id EmitReadConstBuffer(EmitContext& ctx, u32 handle, Id index);
|
Id EmitReadConstBuffer(EmitContext& ctx, u32 handle, Id index);
|
||||||
Id EmitLoadBufferU8(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
|
Id EmitLoadBufferU8(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
|
||||||
Id EmitLoadBufferU16(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
|
Id EmitLoadBufferU16(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "shader_recompiler/frontend/fetch_shader.h"
|
#include "shader_recompiler/frontend/fetch_shader.h"
|
||||||
#include "shader_recompiler/runtime_info.h"
|
#include "shader_recompiler/runtime_info.h"
|
||||||
#include "video_core/amdgpu/types.h"
|
#include "video_core/amdgpu/types.h"
|
||||||
|
#include "video_core/buffer_cache/buffer_cache.h"
|
||||||
|
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
@ -70,6 +71,12 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
|
||||||
Bindings& binding_)
|
Bindings& binding_)
|
||||||
: Sirit::Module(profile_.supported_spirv), info{info_}, runtime_info{runtime_info_},
|
: Sirit::Module(profile_.supported_spirv), info{info_}, runtime_info{runtime_info_},
|
||||||
profile{profile_}, stage{info.stage}, l_stage{info.l_stage}, binding{binding_} {
|
profile{profile_}, stage{info.stage}, l_stage{info.l_stage}, binding{binding_} {
|
||||||
|
if (info.dma_types != IR::Type::Void) {
|
||||||
|
SetMemoryModel(spv::AddressingModel::PhysicalStorageBuffer64, spv::MemoryModel::GLSL450);
|
||||||
|
} else {
|
||||||
|
SetMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::GLSL450);
|
||||||
|
}
|
||||||
|
|
||||||
AddCapability(spv::Capability::Shader);
|
AddCapability(spv::Capability::Shader);
|
||||||
DefineArithmeticTypes();
|
DefineArithmeticTypes();
|
||||||
DefineInterfaces();
|
DefineInterfaces();
|
||||||
|
@ -137,9 +144,13 @@ void EmitContext::DefineArithmeticTypes() {
|
||||||
|
|
||||||
true_value = ConstantTrue(U1[1]);
|
true_value = ConstantTrue(U1[1]);
|
||||||
false_value = ConstantFalse(U1[1]);
|
false_value = ConstantFalse(U1[1]);
|
||||||
|
u8_one_value = Constant(U8, 1U);
|
||||||
|
u8_zero_value = Constant(U8, 0U);
|
||||||
u32_one_value = ConstU32(1U);
|
u32_one_value = ConstU32(1U);
|
||||||
u32_zero_value = ConstU32(0U);
|
u32_zero_value = ConstU32(0U);
|
||||||
f32_zero_value = ConstF32(0.0f);
|
f32_zero_value = ConstF32(0.0f);
|
||||||
|
u64_one_value = Constant(U64, 1ULL);
|
||||||
|
u64_zero_value = Constant(U64, 0ULL);
|
||||||
|
|
||||||
pi_x2 = ConstF32(2.0f * float{std::numbers::pi});
|
pi_x2 = ConstF32(2.0f * float{std::numbers::pi});
|
||||||
|
|
||||||
|
@ -157,6 +168,35 @@ void EmitContext::DefineArithmeticTypes() {
|
||||||
if (info.uses_fp64) {
|
if (info.uses_fp64) {
|
||||||
frexp_result_f64 = Name(TypeStruct(F64[1], S32[1]), "frexp_result_f64");
|
frexp_result_f64 = Name(TypeStruct(F64[1], S32[1]), "frexp_result_f64");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (True(info.dma_types & IR::Type::F64)) {
|
||||||
|
physical_pointer_types[PointerType::F64] =
|
||||||
|
TypePointer(spv::StorageClass::PhysicalStorageBuffer, F64[1]);
|
||||||
|
}
|
||||||
|
if (True(info.dma_types & IR::Type::U64)) {
|
||||||
|
physical_pointer_types[PointerType::U64] =
|
||||||
|
TypePointer(spv::StorageClass::PhysicalStorageBuffer, U64);
|
||||||
|
}
|
||||||
|
if (True(info.dma_types & IR::Type::F32)) {
|
||||||
|
physical_pointer_types[PointerType::F32] =
|
||||||
|
TypePointer(spv::StorageClass::PhysicalStorageBuffer, F32[1]);
|
||||||
|
}
|
||||||
|
if (True(info.dma_types & IR::Type::U32)) {
|
||||||
|
physical_pointer_types[PointerType::U32] =
|
||||||
|
TypePointer(spv::StorageClass::PhysicalStorageBuffer, U32[1]);
|
||||||
|
}
|
||||||
|
if (True(info.dma_types & IR::Type::F16)) {
|
||||||
|
physical_pointer_types[PointerType::F16] =
|
||||||
|
TypePointer(spv::StorageClass::PhysicalStorageBuffer, F16[1]);
|
||||||
|
}
|
||||||
|
if (True(info.dma_types & IR::Type::U16)) {
|
||||||
|
physical_pointer_types[PointerType::U16] =
|
||||||
|
TypePointer(spv::StorageClass::PhysicalStorageBuffer, U16);
|
||||||
|
}
|
||||||
|
if (True(info.dma_types & IR::Type::U8)) {
|
||||||
|
physical_pointer_types[PointerType::U8] =
|
||||||
|
TypePointer(spv::StorageClass::PhysicalStorageBuffer, U8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitContext::DefineInterfaces() {
|
void EmitContext::DefineInterfaces() {
|
||||||
|
@ -195,9 +235,10 @@ EmitContext::SpirvAttribute EmitContext::GetAttributeInfo(AmdGpu::NumberFormat f
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitContext::GetBufferSize(const u32 sharp_idx) {
|
Id EmitContext::GetBufferSize(const u32 sharp_idx) {
|
||||||
const auto& srt_flatbuf = buffers.back();
|
// Can this be done with memory access? Like we do now with ReadConst
|
||||||
ASSERT(srt_flatbuf.buffer_type == BufferType::ReadConstUbo);
|
const auto& srt_flatbuf = buffers[flatbuf_index];
|
||||||
const auto [id, pointer_type] = srt_flatbuf[BufferAlias::U32];
|
ASSERT(srt_flatbuf.buffer_type == BufferType::Flatbuf);
|
||||||
|
const auto [id, pointer_type] = srt_flatbuf[PointerType::U32];
|
||||||
|
|
||||||
const auto rsrc1{
|
const auto rsrc1{
|
||||||
OpLoad(U32[1], OpAccessChain(pointer_type, id, u32_zero_value, ConstU32(sharp_idx + 1)))};
|
OpLoad(U32[1], OpAccessChain(pointer_type, id, u32_zero_value, ConstU32(sharp_idx + 1)))};
|
||||||
|
@ -690,8 +731,14 @@ EmitContext::BufferSpv EmitContext::DefineBuffer(bool is_storage, bool is_writte
|
||||||
case Shader::BufferType::GdsBuffer:
|
case Shader::BufferType::GdsBuffer:
|
||||||
Name(id, "gds_buffer");
|
Name(id, "gds_buffer");
|
||||||
break;
|
break;
|
||||||
case Shader::BufferType::ReadConstUbo:
|
case Shader::BufferType::Flatbuf:
|
||||||
Name(id, "srt_flatbuf_ubo");
|
Name(id, "srt_flatbuf");
|
||||||
|
break;
|
||||||
|
case Shader::BufferType::BdaPagetable:
|
||||||
|
Name(id, "bda_pagetable");
|
||||||
|
break;
|
||||||
|
case Shader::BufferType::FaultBuffer:
|
||||||
|
Name(id, "fault_buffer");
|
||||||
break;
|
break;
|
||||||
case Shader::BufferType::SharedMemory:
|
case Shader::BufferType::SharedMemory:
|
||||||
Name(id, "ssbo_shmem");
|
Name(id, "ssbo_shmem");
|
||||||
|
@ -705,35 +752,53 @@ EmitContext::BufferSpv EmitContext::DefineBuffer(bool is_storage, bool is_writte
|
||||||
};
|
};
|
||||||
|
|
||||||
void EmitContext::DefineBuffers() {
|
void EmitContext::DefineBuffers() {
|
||||||
if (!profile.supports_robust_buffer_access && !info.has_readconst) {
|
if (!profile.supports_robust_buffer_access &&
|
||||||
// In case ReadConstUbo has not already been bound by IR and is needed
|
info.readconst_types == Info::ReadConstType::None) {
|
||||||
|
// In case Flatbuf has not already been bound by IR and is needed
|
||||||
// to query buffer sizes, bind it now.
|
// to query buffer sizes, bind it now.
|
||||||
info.buffers.push_back({
|
info.buffers.push_back({
|
||||||
.used_types = IR::Type::U32,
|
.used_types = IR::Type::U32,
|
||||||
.inline_cbuf = AmdGpu::Buffer::Null(),
|
// We can't guarantee that flatbuf will not grow past UBO
|
||||||
.buffer_type = BufferType::ReadConstUbo,
|
// limit if there are a lot of ReadConsts. (We could specialize)
|
||||||
|
.inline_cbuf = AmdGpu::Buffer::Placeholder(std::numeric_limits<u32>::max()),
|
||||||
|
.buffer_type = BufferType::Flatbuf,
|
||||||
});
|
});
|
||||||
|
// In the future we may want to read buffer sizes from GPU memory if available.
|
||||||
|
// info.readconst_types |= Info::ReadConstType::Immediate;
|
||||||
}
|
}
|
||||||
for (const auto& desc : info.buffers) {
|
for (const auto& desc : info.buffers) {
|
||||||
const auto buf_sharp = desc.GetSharp(info);
|
const auto buf_sharp = desc.GetSharp(info);
|
||||||
const bool is_storage = desc.IsStorage(buf_sharp, profile);
|
const bool is_storage = desc.IsStorage(buf_sharp, profile);
|
||||||
|
|
||||||
|
// Set indexes for special buffers.
|
||||||
|
if (desc.buffer_type == BufferType::Flatbuf) {
|
||||||
|
flatbuf_index = buffers.size();
|
||||||
|
} else if (desc.buffer_type == BufferType::BdaPagetable) {
|
||||||
|
bda_pagetable_index = buffers.size();
|
||||||
|
} else if (desc.buffer_type == BufferType::FaultBuffer) {
|
||||||
|
fault_buffer_index = buffers.size();
|
||||||
|
}
|
||||||
|
|
||||||
// Define aliases depending on the shader usage.
|
// Define aliases depending on the shader usage.
|
||||||
auto& spv_buffer = buffers.emplace_back(binding.buffer++, desc.buffer_type);
|
auto& spv_buffer = buffers.emplace_back(binding.buffer++, desc.buffer_type);
|
||||||
|
if (True(desc.used_types & IR::Type::U64)) {
|
||||||
|
spv_buffer[PointerType::U64] =
|
||||||
|
DefineBuffer(is_storage, desc.is_written, 3, desc.buffer_type, U64);
|
||||||
|
}
|
||||||
if (True(desc.used_types & IR::Type::U32)) {
|
if (True(desc.used_types & IR::Type::U32)) {
|
||||||
spv_buffer[BufferAlias::U32] =
|
spv_buffer[PointerType::U32] =
|
||||||
DefineBuffer(is_storage, desc.is_written, 2, desc.buffer_type, U32[1]);
|
DefineBuffer(is_storage, desc.is_written, 2, desc.buffer_type, U32[1]);
|
||||||
}
|
}
|
||||||
if (True(desc.used_types & IR::Type::F32)) {
|
if (True(desc.used_types & IR::Type::F32)) {
|
||||||
spv_buffer[BufferAlias::F32] =
|
spv_buffer[PointerType::F32] =
|
||||||
DefineBuffer(is_storage, desc.is_written, 2, desc.buffer_type, F32[1]);
|
DefineBuffer(is_storage, desc.is_written, 2, desc.buffer_type, F32[1]);
|
||||||
}
|
}
|
||||||
if (True(desc.used_types & IR::Type::U16)) {
|
if (True(desc.used_types & IR::Type::U16)) {
|
||||||
spv_buffer[BufferAlias::U16] =
|
spv_buffer[PointerType::U16] =
|
||||||
DefineBuffer(is_storage, desc.is_written, 1, desc.buffer_type, U16);
|
DefineBuffer(is_storage, desc.is_written, 1, desc.buffer_type, U16);
|
||||||
}
|
}
|
||||||
if (True(desc.used_types & IR::Type::U8)) {
|
if (True(desc.used_types & IR::Type::U8)) {
|
||||||
spv_buffer[BufferAlias::U8] =
|
spv_buffer[PointerType::U8] =
|
||||||
DefineBuffer(is_storage, desc.is_written, 0, desc.buffer_type, U8);
|
DefineBuffer(is_storage, desc.is_written, 0, desc.buffer_type, U8);
|
||||||
}
|
}
|
||||||
++binding.unified;
|
++binding.unified;
|
||||||
|
@ -1003,6 +1068,101 @@ Id EmitContext::DefineUfloatM5ToFloat32(u32 mantissa_bits, const std::string_vie
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Id EmitContext::DefineGetBdaPointer() {
|
||||||
|
const auto caching_pagebits{
|
||||||
|
Constant(U64, static_cast<u64>(VideoCore::BufferCache::CACHING_PAGEBITS))};
|
||||||
|
const auto caching_pagemask{Constant(U64, VideoCore::BufferCache::CACHING_PAGESIZE - 1)};
|
||||||
|
|
||||||
|
const auto func_type{TypeFunction(U64, U64)};
|
||||||
|
const auto func{OpFunction(U64, spv::FunctionControlMask::MaskNone, func_type)};
|
||||||
|
const auto address{OpFunctionParameter(U64)};
|
||||||
|
Name(func, "get_bda_pointer");
|
||||||
|
AddLabel();
|
||||||
|
|
||||||
|
const auto fault_label{OpLabel()};
|
||||||
|
const auto available_label{OpLabel()};
|
||||||
|
const auto merge_label{OpLabel()};
|
||||||
|
|
||||||
|
// Get page BDA
|
||||||
|
const auto page{OpShiftRightLogical(U64, address, caching_pagebits)};
|
||||||
|
const auto page32{OpUConvert(U32[1], page)};
|
||||||
|
const auto& bda_buffer{buffers[bda_pagetable_index]};
|
||||||
|
const auto [bda_buffer_id, bda_pointer_type] = bda_buffer[PointerType::U64];
|
||||||
|
const auto bda_ptr{OpAccessChain(bda_pointer_type, bda_buffer_id, u32_zero_value, page32)};
|
||||||
|
const auto bda{OpLoad(U64, bda_ptr)};
|
||||||
|
|
||||||
|
// Check if page is GPU cached
|
||||||
|
const auto is_fault{OpIEqual(U1[1], bda, u64_zero_value)};
|
||||||
|
OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone);
|
||||||
|
OpBranchConditional(is_fault, fault_label, available_label);
|
||||||
|
|
||||||
|
// First time acces, mark as fault
|
||||||
|
AddLabel(fault_label);
|
||||||
|
const auto& fault_buffer{buffers[fault_buffer_index]};
|
||||||
|
const auto [fault_buffer_id, fault_pointer_type] = fault_buffer[PointerType::U8];
|
||||||
|
const auto page_div8{OpShiftRightLogical(U32[1], page32, ConstU32(3U))};
|
||||||
|
const auto page_mod8{OpBitwiseAnd(U32[1], page32, ConstU32(7U))};
|
||||||
|
const auto page_mask{OpShiftLeftLogical(U8, u8_one_value, page_mod8)};
|
||||||
|
const auto fault_ptr{
|
||||||
|
OpAccessChain(fault_pointer_type, fault_buffer_id, u32_zero_value, page_div8)};
|
||||||
|
const auto fault_value{OpLoad(U8, fault_ptr)};
|
||||||
|
const auto fault_value_masked{OpBitwiseOr(U8, fault_value, page_mask)};
|
||||||
|
OpStore(fault_ptr, fault_value_masked);
|
||||||
|
|
||||||
|
// Return null pointer
|
||||||
|
const auto fallback_result{u64_zero_value};
|
||||||
|
OpBranch(merge_label);
|
||||||
|
|
||||||
|
// Value is available, compute address
|
||||||
|
AddLabel(available_label);
|
||||||
|
const auto offset_in_bda{OpBitwiseAnd(U64, address, caching_pagemask)};
|
||||||
|
const auto addr{OpIAdd(U64, bda, offset_in_bda)};
|
||||||
|
OpBranch(merge_label);
|
||||||
|
|
||||||
|
// Merge
|
||||||
|
AddLabel(merge_label);
|
||||||
|
const auto result{OpPhi(U64, addr, available_label, fallback_result, fault_label)};
|
||||||
|
OpReturnValue(result);
|
||||||
|
OpFunctionEnd();
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
Id EmitContext::DefineReadConst(bool dynamic) {
|
||||||
|
const auto func_type{!dynamic ? TypeFunction(U32[1], U32[2], U32[1], U32[1])
|
||||||
|
: TypeFunction(U32[1], U32[2], U32[1])};
|
||||||
|
const auto func{OpFunction(U32[1], spv::FunctionControlMask::MaskNone, func_type)};
|
||||||
|
const auto base{OpFunctionParameter(U32[2])};
|
||||||
|
const auto offset{OpFunctionParameter(U32[1])};
|
||||||
|
const auto flatbuf_offset{!dynamic ? OpFunctionParameter(U32[1]) : Id{}};
|
||||||
|
Name(func, dynamic ? "read_const_dynamic" : "read_const");
|
||||||
|
AddLabel();
|
||||||
|
|
||||||
|
const auto base_lo{OpUConvert(U64, OpCompositeExtract(U32[1], base, 0))};
|
||||||
|
const auto base_hi{OpUConvert(U64, OpCompositeExtract(U32[1], base, 1))};
|
||||||
|
const auto base_shift{OpShiftLeftLogical(U64, base_hi, ConstU32(32U))};
|
||||||
|
const auto base_addr{OpBitwiseOr(U64, base_lo, base_shift)};
|
||||||
|
const auto offset_bytes{OpShiftLeftLogical(U32[1], offset, ConstU32(2U))};
|
||||||
|
const auto addr{OpIAdd(U64, base_addr, OpUConvert(U64, offset_bytes))};
|
||||||
|
|
||||||
|
const auto result = EmitMemoryRead(U32[1], addr, [&]() {
|
||||||
|
if (dynamic) {
|
||||||
|
return u32_zero_value;
|
||||||
|
} else {
|
||||||
|
const auto& flatbuf_buffer{buffers[flatbuf_index]};
|
||||||
|
ASSERT(flatbuf_buffer.binding >= 0 &&
|
||||||
|
flatbuf_buffer.buffer_type == BufferType::Flatbuf);
|
||||||
|
const auto [flatbuf_buffer_id, flatbuf_pointer_type] = flatbuf_buffer[PointerType::U32];
|
||||||
|
const auto ptr{OpAccessChain(flatbuf_pointer_type, flatbuf_buffer_id, u32_zero_value,
|
||||||
|
flatbuf_offset)};
|
||||||
|
return OpLoad(U32[1], ptr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
OpReturnValue(result);
|
||||||
|
OpFunctionEnd();
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
void EmitContext::DefineFunctions() {
|
void EmitContext::DefineFunctions() {
|
||||||
if (info.uses_pack_10_11_11) {
|
if (info.uses_pack_10_11_11) {
|
||||||
f32_to_uf11 = DefineFloat32ToUfloatM5(6, "f32_to_uf11");
|
f32_to_uf11 = DefineFloat32ToUfloatM5(6, "f32_to_uf11");
|
||||||
|
@ -1012,6 +1172,18 @@ void EmitContext::DefineFunctions() {
|
||||||
uf11_to_f32 = DefineUfloatM5ToFloat32(6, "uf11_to_f32");
|
uf11_to_f32 = DefineUfloatM5ToFloat32(6, "uf11_to_f32");
|
||||||
uf10_to_f32 = DefineUfloatM5ToFloat32(5, "uf10_to_f32");
|
uf10_to_f32 = DefineUfloatM5ToFloat32(5, "uf10_to_f32");
|
||||||
}
|
}
|
||||||
|
if (info.dma_types != IR::Type::Void) {
|
||||||
|
get_bda_pointer = DefineGetBdaPointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (True(info.readconst_types & Info::ReadConstType::Immediate)) {
|
||||||
|
LOG_DEBUG(Render_Recompiler, "Shader {:#x} uses immediate ReadConst", info.pgm_hash);
|
||||||
|
read_const = DefineReadConst(false);
|
||||||
|
}
|
||||||
|
if (True(info.readconst_types & Info::ReadConstType::Dynamic)) {
|
||||||
|
LOG_DEBUG(Render_Recompiler, "Shader {:#x} uses dynamic ReadConst", info.pgm_hash);
|
||||||
|
read_const_dynamic = DefineReadConst(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::Backend::SPIRV
|
} // namespace Shader::Backend::SPIRV
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <unordered_map>
|
||||||
#include <sirit/sirit.h>
|
#include <sirit/sirit.h>
|
||||||
|
|
||||||
#include "shader_recompiler/backend/bindings.h"
|
#include "shader_recompiler/backend/bindings.h"
|
||||||
|
@ -41,6 +42,17 @@ public:
|
||||||
Bindings& binding);
|
Bindings& binding);
|
||||||
~EmitContext();
|
~EmitContext();
|
||||||
|
|
||||||
|
enum class PointerType : u32 {
|
||||||
|
U8,
|
||||||
|
U16,
|
||||||
|
F16,
|
||||||
|
U32,
|
||||||
|
F32,
|
||||||
|
U64,
|
||||||
|
F64,
|
||||||
|
NumAlias,
|
||||||
|
};
|
||||||
|
|
||||||
Id Def(const IR::Value& value);
|
Id Def(const IR::Value& value);
|
||||||
|
|
||||||
void DefineBufferProperties();
|
void DefineBufferProperties();
|
||||||
|
@ -133,12 +145,72 @@ public:
|
||||||
return ConstantComposite(type, constituents);
|
return ConstantComposite(type, constituents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Id AddLabel() {
|
||||||
|
last_label = Module::AddLabel();
|
||||||
|
return last_label;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Id AddLabel(Id label) {
|
||||||
|
last_label = Module::AddLabel(label);
|
||||||
|
return last_label;
|
||||||
|
}
|
||||||
|
|
||||||
|
PointerType PointerTypeFromType(Id type) {
|
||||||
|
if (type.value == U8.value)
|
||||||
|
return PointerType::U8;
|
||||||
|
if (type.value == U16.value)
|
||||||
|
return PointerType::U16;
|
||||||
|
if (type.value == F16[1].value)
|
||||||
|
return PointerType::F16;
|
||||||
|
if (type.value == U32[1].value)
|
||||||
|
return PointerType::U32;
|
||||||
|
if (type.value == F32[1].value)
|
||||||
|
return PointerType::F32;
|
||||||
|
if (type.value == U64.value)
|
||||||
|
return PointerType::U64;
|
||||||
|
if (type.value == F64[1].value)
|
||||||
|
return PointerType::F64;
|
||||||
|
UNREACHABLE_MSG("Unknown type for pointer");
|
||||||
|
}
|
||||||
|
|
||||||
|
Id EmitMemoryRead(Id type, Id address, auto&& fallback) {
|
||||||
|
const Id available_label = OpLabel();
|
||||||
|
const Id fallback_label = OpLabel();
|
||||||
|
const Id merge_label = OpLabel();
|
||||||
|
|
||||||
|
const Id addr = OpFunctionCall(U64, get_bda_pointer, address);
|
||||||
|
const Id is_available = OpINotEqual(U1[1], addr, u64_zero_value);
|
||||||
|
OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone);
|
||||||
|
OpBranchConditional(is_available, available_label, fallback_label);
|
||||||
|
|
||||||
|
// Available
|
||||||
|
AddLabel(available_label);
|
||||||
|
const auto pointer_type = PointerTypeFromType(type);
|
||||||
|
const Id pointer_type_id = physical_pointer_types[pointer_type];
|
||||||
|
const Id addr_ptr = OpConvertUToPtr(pointer_type_id, addr);
|
||||||
|
const Id result = OpLoad(type, addr_ptr, spv::MemoryAccessMask::Aligned, 4u);
|
||||||
|
OpBranch(merge_label);
|
||||||
|
|
||||||
|
// Fallback
|
||||||
|
AddLabel(fallback_label);
|
||||||
|
const Id fallback_result = fallback();
|
||||||
|
OpBranch(merge_label);
|
||||||
|
|
||||||
|
// Merge
|
||||||
|
AddLabel(merge_label);
|
||||||
|
const Id final_result =
|
||||||
|
OpPhi(type, fallback_result, fallback_label, result, available_label);
|
||||||
|
return final_result;
|
||||||
|
}
|
||||||
|
|
||||||
Info& info;
|
Info& info;
|
||||||
const RuntimeInfo& runtime_info;
|
const RuntimeInfo& runtime_info;
|
||||||
const Profile& profile;
|
const Profile& profile;
|
||||||
Stage stage;
|
Stage stage;
|
||||||
LogicalStage l_stage{};
|
LogicalStage l_stage{};
|
||||||
|
|
||||||
|
Id last_label{};
|
||||||
|
|
||||||
Id void_id{};
|
Id void_id{};
|
||||||
Id U8{};
|
Id U8{};
|
||||||
Id S8{};
|
Id S8{};
|
||||||
|
@ -161,9 +233,13 @@ public:
|
||||||
|
|
||||||
Id true_value{};
|
Id true_value{};
|
||||||
Id false_value{};
|
Id false_value{};
|
||||||
|
Id u8_one_value{};
|
||||||
|
Id u8_zero_value{};
|
||||||
Id u32_one_value{};
|
Id u32_one_value{};
|
||||||
Id u32_zero_value{};
|
Id u32_zero_value{};
|
||||||
Id f32_zero_value{};
|
Id f32_zero_value{};
|
||||||
|
Id u64_one_value{};
|
||||||
|
Id u64_zero_value{};
|
||||||
|
|
||||||
Id shared_u8{};
|
Id shared_u8{};
|
||||||
Id shared_u16{};
|
Id shared_u16{};
|
||||||
|
@ -231,14 +307,6 @@ public:
|
||||||
bool is_storage = false;
|
bool is_storage = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class BufferAlias : u32 {
|
|
||||||
U8,
|
|
||||||
U16,
|
|
||||||
U32,
|
|
||||||
F32,
|
|
||||||
NumAlias,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BufferSpv {
|
struct BufferSpv {
|
||||||
Id id;
|
Id id;
|
||||||
Id pointer_type;
|
Id pointer_type;
|
||||||
|
@ -252,22 +320,40 @@ public:
|
||||||
Id size;
|
Id size;
|
||||||
Id size_shorts;
|
Id size_shorts;
|
||||||
Id size_dwords;
|
Id size_dwords;
|
||||||
std::array<BufferSpv, u32(BufferAlias::NumAlias)> aliases;
|
std::array<BufferSpv, u32(PointerType::NumAlias)> aliases;
|
||||||
|
|
||||||
const BufferSpv& operator[](BufferAlias alias) const {
|
const BufferSpv& operator[](PointerType alias) const {
|
||||||
return aliases[u32(alias)];
|
return aliases[u32(alias)];
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferSpv& operator[](BufferAlias alias) {
|
BufferSpv& operator[](PointerType alias) {
|
||||||
return aliases[u32(alias)];
|
return aliases[u32(alias)];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PhysicalPointerTypes {
|
||||||
|
std::array<Id, u32(PointerType::NumAlias)> types;
|
||||||
|
|
||||||
|
const Id& operator[](PointerType type) const {
|
||||||
|
return types[u32(type)];
|
||||||
|
}
|
||||||
|
|
||||||
|
Id& operator[](PointerType type) {
|
||||||
|
return types[u32(type)];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Bindings& binding;
|
Bindings& binding;
|
||||||
boost::container::small_vector<Id, 16> buf_type_ids;
|
boost::container::small_vector<Id, 16> buf_type_ids;
|
||||||
boost::container::small_vector<BufferDefinition, 16> buffers;
|
boost::container::small_vector<BufferDefinition, 16> buffers;
|
||||||
boost::container::small_vector<TextureDefinition, 8> images;
|
boost::container::small_vector<TextureDefinition, 8> images;
|
||||||
boost::container::small_vector<Id, 4> samplers;
|
boost::container::small_vector<Id, 4> samplers;
|
||||||
|
PhysicalPointerTypes physical_pointer_types;
|
||||||
|
std::unordered_map<u32, Id> first_to_last_label_map;
|
||||||
|
|
||||||
|
size_t flatbuf_index{};
|
||||||
|
size_t bda_pagetable_index{};
|
||||||
|
size_t fault_buffer_index{};
|
||||||
|
|
||||||
Id sampler_type{};
|
Id sampler_type{};
|
||||||
Id sampler_pointer_type{};
|
Id sampler_pointer_type{};
|
||||||
|
@ -292,6 +378,11 @@ public:
|
||||||
Id uf10_to_f32{};
|
Id uf10_to_f32{};
|
||||||
Id f32_to_uf10{};
|
Id f32_to_uf10{};
|
||||||
|
|
||||||
|
Id get_bda_pointer{};
|
||||||
|
|
||||||
|
Id read_const{};
|
||||||
|
Id read_const_dynamic{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DefineArithmeticTypes();
|
void DefineArithmeticTypes();
|
||||||
void DefineInterfaces();
|
void DefineInterfaces();
|
||||||
|
@ -312,6 +403,10 @@ private:
|
||||||
Id DefineFloat32ToUfloatM5(u32 mantissa_bits, std::string_view name);
|
Id DefineFloat32ToUfloatM5(u32 mantissa_bits, std::string_view name);
|
||||||
Id DefineUfloatM5ToFloat32(u32 mantissa_bits, std::string_view name);
|
Id DefineUfloatM5ToFloat32(u32 mantissa_bits, std::string_view name);
|
||||||
|
|
||||||
|
Id DefineGetBdaPointer();
|
||||||
|
|
||||||
|
Id DefineReadConst(bool dynamic);
|
||||||
|
|
||||||
Id GetBufferSize(u32 sharp_idx);
|
Id GetBufferSize(u32 sharp_idx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,21 +39,22 @@ void Translator::EmitScalarMemory(const GcnInst& inst) {
|
||||||
|
|
||||||
void Translator::S_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
|
void Translator::S_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
|
||||||
const auto& smrd = inst.control.smrd;
|
const auto& smrd = inst.control.smrd;
|
||||||
const u32 dword_offset = [&] -> u32 {
|
const IR::ScalarReg sbase{inst.src[0].code * 2};
|
||||||
|
const IR::U32 dword_offset = [&] -> IR::U32 {
|
||||||
if (smrd.imm) {
|
if (smrd.imm) {
|
||||||
return smrd.offset;
|
return ir.Imm32(smrd.offset);
|
||||||
}
|
}
|
||||||
if (smrd.offset == SQ_SRC_LITERAL) {
|
if (smrd.offset == SQ_SRC_LITERAL) {
|
||||||
return inst.src[1].code;
|
return ir.Imm32(inst.src[1].code);
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
return ir.ShiftRightLogical(ir.GetScalarReg(IR::ScalarReg(smrd.offset)), ir.Imm32(2));
|
||||||
}();
|
}();
|
||||||
const IR::ScalarReg sbase{inst.src[0].code * 2};
|
|
||||||
const IR::Value base =
|
const IR::Value base =
|
||||||
ir.CompositeConstruct(ir.GetScalarReg(sbase), ir.GetScalarReg(sbase + 1));
|
ir.CompositeConstruct(ir.GetScalarReg(sbase), ir.GetScalarReg(sbase + 1));
|
||||||
IR::ScalarReg dst_reg{inst.dst[0].code};
|
IR::ScalarReg dst_reg{inst.dst[0].code};
|
||||||
for (u32 i = 0; i < num_dwords; i++) {
|
for (u32 i = 0; i < num_dwords; i++) {
|
||||||
ir.SetScalarReg(dst_reg++, ir.ReadConst(base, ir.Imm32(dword_offset + i)));
|
IR::U32 index = ir.IAdd(dword_offset, ir.Imm32(i));
|
||||||
|
ir.SetScalarReg(dst_reg + i, ir.ReadConst(base, index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ void Translator::S_BUFFER_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
|
||||||
IR::ScalarReg dst_reg{inst.dst[0].code};
|
IR::ScalarReg dst_reg{inst.dst[0].code};
|
||||||
for (u32 i = 0; i < num_dwords; i++) {
|
for (u32 i = 0; i < num_dwords; i++) {
|
||||||
const IR::U32 index = ir.IAdd(dword_offset, ir.Imm32(i));
|
const IR::U32 index = ir.IAdd(dword_offset, ir.Imm32(i));
|
||||||
ir.SetScalarReg(dst_reg++, ir.ReadConstBuffer(vsharp, index));
|
ir.SetScalarReg(dst_reg + i, ir.ReadConstBuffer(vsharp, index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -989,13 +989,22 @@ void Translator::V_CMP_NE_U64(const GcnInst& inst) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const IR::U1 src0{get_src(inst.src[0])};
|
const IR::U1 src0{get_src(inst.src[0])};
|
||||||
ASSERT(inst.src[1].field == OperandField::ConstZero); // src0 != 0
|
auto op = [&inst, this](auto x) {
|
||||||
|
switch (inst.src[1].field) {
|
||||||
|
case OperandField::ConstZero:
|
||||||
|
return x;
|
||||||
|
case OperandField::SignedConstIntNeg:
|
||||||
|
return ir.LogicalNot(x);
|
||||||
|
default:
|
||||||
|
UNREACHABLE_MSG("unhandled V_CMP_NE_U64 source argument {}", u32(inst.src[1].field));
|
||||||
|
}
|
||||||
|
};
|
||||||
switch (inst.dst[1].field) {
|
switch (inst.dst[1].field) {
|
||||||
case OperandField::VccLo:
|
case OperandField::VccLo:
|
||||||
ir.SetVcc(src0);
|
ir.SetVcc(op(src0));
|
||||||
break;
|
break;
|
||||||
case OperandField::ScalarGPR:
|
case OperandField::ScalarGPR:
|
||||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), src0);
|
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), op(src0));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
|
|
@ -41,7 +41,9 @@ constexpr u32 NUM_TEXTURE_TYPES = 7;
|
||||||
|
|
||||||
enum class BufferType : u32 {
|
enum class BufferType : u32 {
|
||||||
Guest,
|
Guest,
|
||||||
ReadConstUbo,
|
Flatbuf,
|
||||||
|
BdaPagetable,
|
||||||
|
FaultBuffer,
|
||||||
GdsBuffer,
|
GdsBuffer,
|
||||||
SharedMemory,
|
SharedMemory,
|
||||||
};
|
};
|
||||||
|
@ -215,11 +217,18 @@ struct Info {
|
||||||
bool stores_tess_level_outer{};
|
bool stores_tess_level_outer{};
|
||||||
bool stores_tess_level_inner{};
|
bool stores_tess_level_inner{};
|
||||||
bool translation_failed{};
|
bool translation_failed{};
|
||||||
bool has_readconst{};
|
|
||||||
u8 mrt_mask{0u};
|
u8 mrt_mask{0u};
|
||||||
bool has_fetch_shader{false};
|
bool has_fetch_shader{false};
|
||||||
u32 fetch_shader_sgpr_base{0u};
|
u32 fetch_shader_sgpr_base{0u};
|
||||||
|
|
||||||
|
enum class ReadConstType {
|
||||||
|
None = 0,
|
||||||
|
Immediate = 1 << 0,
|
||||||
|
Dynamic = 1 << 1,
|
||||||
|
};
|
||||||
|
ReadConstType readconst_types{};
|
||||||
|
IR::Type dma_types{IR::Type::Void};
|
||||||
|
|
||||||
explicit Info(Stage stage_, LogicalStage l_stage_, ShaderParams params)
|
explicit Info(Stage stage_, LogicalStage l_stage_, ShaderParams params)
|
||||||
: stage{stage_}, l_stage{l_stage_}, pgm_hash{params.hash}, pgm_base{params.Base()},
|
: stage{stage_}, l_stage{l_stage_}, pgm_hash{params.hash}, pgm_base{params.Base()},
|
||||||
user_data{params.user_data} {}
|
user_data{params.user_data} {}
|
||||||
|
@ -277,6 +286,7 @@ struct Info {
|
||||||
sizeof(tess_constants));
|
sizeof(tess_constants));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
DECLARE_ENUM_FLAG_OPERATORS(Info::ReadConstType);
|
||||||
|
|
||||||
constexpr AmdGpu::Buffer BufferResource::GetSharp(const Info& info) const noexcept {
|
constexpr AmdGpu::Buffer BufferResource::GetSharp(const Info& info) const noexcept {
|
||||||
return inline_cbuf ? inline_cbuf : info.ReadUdSharp<AmdGpu::Buffer>(sharp_idx);
|
return inline_cbuf ? inline_cbuf : info.ReadUdSharp<AmdGpu::Buffer>(sharp_idx);
|
||||||
|
|
44
src/shader_recompiler/ir/abstract_syntax_list.cpp
Normal file
44
src/shader_recompiler/ir/abstract_syntax_list.cpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "abstract_syntax_list.h"
|
||||||
|
|
||||||
|
namespace Shader::IR {
|
||||||
|
|
||||||
|
std::string DumpASLNode(const AbstractSyntaxNode& node,
|
||||||
|
const std::map<const Block*, size_t>& block_to_index,
|
||||||
|
const std::map<const Inst*, size_t>& inst_to_index) {
|
||||||
|
switch (node.type) {
|
||||||
|
case AbstractSyntaxNode::Type::Block:
|
||||||
|
return fmt::format("Block: ${}", block_to_index.at(node.data.block));
|
||||||
|
case AbstractSyntaxNode::Type::If:
|
||||||
|
return fmt::format("If: cond = %{}, body = ${}, merge = ${}",
|
||||||
|
inst_to_index.at(node.data.if_node.cond.Inst()),
|
||||||
|
block_to_index.at(node.data.if_node.body),
|
||||||
|
block_to_index.at(node.data.if_node.merge));
|
||||||
|
case AbstractSyntaxNode::Type::EndIf:
|
||||||
|
return fmt::format("EndIf: merge = ${}", block_to_index.at(node.data.end_if.merge));
|
||||||
|
case AbstractSyntaxNode::Type::Loop:
|
||||||
|
return fmt::format("Loop: body = ${}, continue = ${}, merge = ${}",
|
||||||
|
block_to_index.at(node.data.loop.body),
|
||||||
|
block_to_index.at(node.data.loop.continue_block),
|
||||||
|
block_to_index.at(node.data.loop.merge));
|
||||||
|
case AbstractSyntaxNode::Type::Repeat:
|
||||||
|
return fmt::format("Repeat: cond = %{}, header = ${}, merge = ${}",
|
||||||
|
inst_to_index.at(node.data.repeat.cond.Inst()),
|
||||||
|
block_to_index.at(node.data.repeat.loop_header),
|
||||||
|
block_to_index.at(node.data.repeat.merge));
|
||||||
|
case AbstractSyntaxNode::Type::Break:
|
||||||
|
return fmt::format("Break: cond = %{}, merge = ${}, skip = ${}",
|
||||||
|
inst_to_index.at(node.data.break_node.cond.Inst()),
|
||||||
|
block_to_index.at(node.data.break_node.merge),
|
||||||
|
block_to_index.at(node.data.break_node.skip));
|
||||||
|
case AbstractSyntaxNode::Type::Return:
|
||||||
|
return "Return";
|
||||||
|
case AbstractSyntaxNode::Type::Unreachable:
|
||||||
|
return "Unreachable";
|
||||||
|
};
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Shader::IR
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "shader_recompiler/ir/value.h"
|
#include "shader_recompiler/ir/value.h"
|
||||||
|
|
||||||
|
@ -53,4 +54,8 @@ struct AbstractSyntaxNode {
|
||||||
};
|
};
|
||||||
using AbstractSyntaxList = std::vector<AbstractSyntaxNode>;
|
using AbstractSyntaxList = std::vector<AbstractSyntaxNode>;
|
||||||
|
|
||||||
|
std::string DumpASLNode(const AbstractSyntaxNode& node,
|
||||||
|
const std::map<const Block*, size_t>& block_to_index,
|
||||||
|
const std::map<const Inst*, size_t>& inst_to_index);
|
||||||
|
|
||||||
} // namespace Shader::IR
|
} // namespace Shader::IR
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "shader_recompiler/ir/program.h"
|
#include "shader_recompiler/ir/program.h"
|
||||||
|
#include "video_core/buffer_cache/buffer_cache.h"
|
||||||
|
|
||||||
namespace Shader::Optimization {
|
namespace Shader::Optimization {
|
||||||
|
|
||||||
|
@ -79,14 +80,21 @@ void Visit(Info& info, const IR::Inst& inst) {
|
||||||
info.uses_lane_id = true;
|
info.uses_lane_id = true;
|
||||||
break;
|
break;
|
||||||
case IR::Opcode::ReadConst:
|
case IR::Opcode::ReadConst:
|
||||||
if (!info.has_readconst) {
|
if (info.readconst_types == Info::ReadConstType::None) {
|
||||||
info.buffers.push_back({
|
info.buffers.push_back({
|
||||||
.used_types = IR::Type::U32,
|
.used_types = IR::Type::U32,
|
||||||
.inline_cbuf = AmdGpu::Buffer::Null(),
|
// We can't guarantee that flatbuf will not grow past UBO
|
||||||
.buffer_type = BufferType::ReadConstUbo,
|
// limit if there are a lot of ReadConsts. (We could specialize)
|
||||||
|
.inline_cbuf = AmdGpu::Buffer::Placeholder(std::numeric_limits<u32>::max()),
|
||||||
|
.buffer_type = BufferType::Flatbuf,
|
||||||
});
|
});
|
||||||
info.has_readconst = true;
|
|
||||||
}
|
}
|
||||||
|
if (inst.Flags<u32>() != 0) {
|
||||||
|
info.readconst_types |= Info::ReadConstType::Immediate;
|
||||||
|
} else {
|
||||||
|
info.readconst_types |= Info::ReadConstType::Dynamic;
|
||||||
|
}
|
||||||
|
info.dma_types |= IR::Type::U32;
|
||||||
break;
|
break;
|
||||||
case IR::Opcode::PackUfloat10_11_11:
|
case IR::Opcode::PackUfloat10_11_11:
|
||||||
info.uses_pack_10_11_11 = true;
|
info.uses_pack_10_11_11 = true;
|
||||||
|
@ -105,6 +113,21 @@ void CollectShaderInfoPass(IR::Program& program) {
|
||||||
Visit(program.info, inst);
|
Visit(program.info, inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (program.info.dma_types != IR::Type::Void) {
|
||||||
|
program.info.buffers.push_back({
|
||||||
|
.used_types = IR::Type::U64,
|
||||||
|
.inline_cbuf = AmdGpu::Buffer::Placeholder(VideoCore::BufferCache::BDA_PAGETABLE_SIZE),
|
||||||
|
.buffer_type = BufferType::BdaPagetable,
|
||||||
|
.is_written = true,
|
||||||
|
});
|
||||||
|
program.info.buffers.push_back({
|
||||||
|
.used_types = IR::Type::U8,
|
||||||
|
.inline_cbuf = AmdGpu::Buffer::Placeholder(VideoCore::BufferCache::FAULT_BUFFER_SIZE),
|
||||||
|
.buffer_type = BufferType::FaultBuffer,
|
||||||
|
.is_written = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::Optimization
|
} // namespace Shader::Optimization
|
||||||
|
|
|
@ -6,13 +6,30 @@
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "common/config.h"
|
||||||
|
#include "common/io_file.h"
|
||||||
|
#include "common/path_util.h"
|
||||||
#include "shader_recompiler/ir/basic_block.h"
|
#include "shader_recompiler/ir/basic_block.h"
|
||||||
#include "shader_recompiler/ir/program.h"
|
#include "shader_recompiler/ir/program.h"
|
||||||
#include "shader_recompiler/ir/value.h"
|
#include "shader_recompiler/ir/value.h"
|
||||||
|
|
||||||
namespace Shader::IR {
|
namespace Shader::IR {
|
||||||
|
|
||||||
std::string DumpProgram(const Program& program) {
|
void DumpProgram(const Program& program, const Info& info, const std::string& type) {
|
||||||
|
using namespace Common::FS;
|
||||||
|
|
||||||
|
if (!Config::dumpShaders()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto dump_dir = GetUserPath(PathType::ShaderDir) / "dumps";
|
||||||
|
if (!std::filesystem::exists(dump_dir)) {
|
||||||
|
std::filesystem::create_directories(dump_dir);
|
||||||
|
}
|
||||||
|
const auto ir_filename =
|
||||||
|
fmt::format("{}_{:#018x}.{}irprogram.txt", info.stage, info.pgm_hash, type);
|
||||||
|
const auto ir_file = IOFile{dump_dir / ir_filename, FileAccessMode::Write, FileType::TextFile};
|
||||||
|
|
||||||
size_t index{0};
|
size_t index{0};
|
||||||
std::map<const IR::Inst*, size_t> inst_to_index;
|
std::map<const IR::Inst*, size_t> inst_to_index;
|
||||||
std::map<const IR::Block*, size_t> block_to_index;
|
std::map<const IR::Block*, size_t> block_to_index;
|
||||||
|
@ -21,11 +38,20 @@ std::string DumpProgram(const Program& program) {
|
||||||
block_to_index.emplace(block, index);
|
block_to_index.emplace(block, index);
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
std::string ret;
|
|
||||||
for (const auto& block : program.blocks) {
|
for (const auto& block : program.blocks) {
|
||||||
ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
|
std::string s = IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
|
||||||
|
ir_file.WriteString(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto asl_filename = fmt::format("{}_{:#018x}.{}asl.txt", info.stage, info.pgm_hash, type);
|
||||||
|
const auto asl_file =
|
||||||
|
IOFile{dump_dir / asl_filename, FileAccessMode::Write, FileType::TextFile};
|
||||||
|
|
||||||
|
for (const auto& node : program.syntax_list) {
|
||||||
|
std::string s = IR::DumpASLNode(node, block_to_index, inst_to_index) + '\n';
|
||||||
|
asl_file.WriteString(s);
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::IR
|
} // namespace Shader::IR
|
||||||
|
|
|
@ -21,6 +21,6 @@ struct Program {
|
||||||
Info& info;
|
Info& info;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] std::string DumpProgram(const Program& program);
|
void DumpProgram(const Program& program, const Info& info, const std::string& type = "");
|
||||||
|
|
||||||
} // namespace Shader::IR
|
} // namespace Shader::IR
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue