diff --git a/.gitmodules b/.gitmodules index 3d0d21c5b..ca229bedd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,10 +6,6 @@ path = externals/cryptopp url = https://github.com/shadps4-emu/ext-cryptopp.git shallow = true -[submodule "externals/cryptoppwin"] - path = externals/cryptoppwin - url = https://github.com/shadps4-emu/ext-cryptoppwin.git - shallow = true [submodule "externals/zlib-ng"] path = externals/zlib-ng url = https://github.com/shadps4-emu/ext-zlib-ng.git diff --git a/CMakeLists.txt b/CMakeLists.txt index ccb09695f..43e479155 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,8 +37,10 @@ option(ENABLE_UPDATER "Enables the options to updater" ON) # First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR. if (APPLE AND CMAKE_OSX_ARCHITECTURES) set(BASE_ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}") -else() +elseif (CMAKE_SYSTEM_PROCESSOR) set(BASE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}") +else() + set(BASE_ARCHITECTURE "${CMAKE_HOST_SYSTEM_PROCESSOR}") endif() # Next, match common architecture strings down to a known common value. @@ -50,7 +52,12 @@ else() message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}") endif() -if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") +if (ARCHITECTURE STREQUAL "x86_64") + # Set x86_64 target level to Sandy Bridge to generally match what is supported for PS4 guest code with CPU patches. + add_compile_options(-march=sandybridge) +endif() + +if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") # Exclude ARM homebrew path to avoid conflicts when cross compiling. list(APPEND CMAKE_IGNORE_PREFIX_PATH "/opt/homebrew") @@ -298,6 +305,7 @@ set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp set(NETWORK_LIBS src/core/libraries/network/http.cpp src/core/libraries/network/http.h + src/core/libraries/network/http_error.h src/core/libraries/network/http2.cpp src/core/libraries/network/http2.h src/core/libraries/network/net.cpp @@ -370,6 +378,24 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/ngs2/ngs2_error.h src/core/libraries/ngs2/ngs2_impl.cpp src/core/libraries/ngs2/ngs2_impl.h + src/core/libraries/ngs2/ngs2_custom.cpp + src/core/libraries/ngs2/ngs2_custom.h + src/core/libraries/ngs2/ngs2_reverb.cpp + src/core/libraries/ngs2/ngs2_reverb.h + src/core/libraries/ngs2/ngs2_geom.cpp + src/core/libraries/ngs2/ngs2_geom.h + src/core/libraries/ngs2/ngs2_pan.cpp + src/core/libraries/ngs2/ngs2_pan.h + src/core/libraries/ngs2/ngs2_report.cpp + src/core/libraries/ngs2/ngs2_report.h + src/core/libraries/ngs2/ngs2_eq.cpp + src/core/libraries/ngs2/ngs2_eq.h + src/core/libraries/ngs2/ngs2_mastering.cpp + src/core/libraries/ngs2/ngs2_mastering.h + src/core/libraries/ngs2/ngs2_sampler.cpp + src/core/libraries/ngs2/ngs2_sampler.h + src/core/libraries/ngs2/ngs2_submixer.cpp + src/core/libraries/ngs2/ngs2_submixer.h src/core/libraries/ajm/ajm_error.h src/core/libraries/audio3d/audio3d.cpp src/core/libraries/audio3d/audio3d.h @@ -581,6 +607,7 @@ set(COMMON src/common/logging/backend.cpp src/common/spin_lock.h src/common/stb.cpp src/common/stb.h + src/common/string_literal.h src/common/string_util.cpp src/common/string_util.h src/common/thread.cpp @@ -640,8 +667,6 @@ set(CORE src/core/aerolib/stubs.cpp src/core/file_format/playgo_chunk.h src/core/file_format/trp.cpp src/core/file_format/trp.h - src/core/file_format/splash.h - src/core/file_format/splash.cpp src/core/file_sys/fs.cpp src/core/file_sys/fs.h src/core/loader.cpp @@ -762,6 +787,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/ir/passes/identity_removal_pass.cpp src/shader_recompiler/ir/passes/ir_passes.h src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp + src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp src/shader_recompiler/ir/passes/resource_tracking_pass.cpp src/shader_recompiler/ir/passes/ring_access_elimination.cpp src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -842,6 +868,10 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp src/video_core/renderer_vulkan/vk_shader_util.h src/video_core/renderer_vulkan/vk_swapchain.cpp src/video_core/renderer_vulkan/vk_swapchain.h + src/video_core/renderer_vulkan/host_passes/fsr_pass.cpp + src/video_core/renderer_vulkan/host_passes/fsr_pass.h + src/video_core/renderer_vulkan/host_passes/pp_pass.cpp + src/video_core/renderer_vulkan/host_passes/pp_pass.h src/video_core/texture_cache/image.cpp src/video_core/texture_cache/image.h src/video_core/texture_cache/image_info.cpp @@ -914,6 +944,9 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/control_settings.cpp src/qt_gui/control_settings.h src/qt_gui/control_settings.ui + src/qt_gui/kbm_gui.cpp + src/qt_gui/kbm_gui.h + src/qt_gui/kbm_gui.ui src/qt_gui/main_window_ui.h src/qt_gui/main_window.cpp src/qt_gui/main_window.h @@ -986,7 +1019,7 @@ endif() create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers cryptopp::cryptopp) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") @@ -1035,12 +1068,6 @@ if (NOT ENABLE_QT_GUI) target_link_libraries(shadps4 PRIVATE SDL3::SDL3) endif() -if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC) - target_link_libraries(shadps4 PRIVATE cryptoppwin) -else() - target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp) -endif() - if (ENABLE_QT_GUI) target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia) add_definitions(-DENABLE_QT_GUI) @@ -1111,7 +1138,6 @@ cmrc_add_resource_library(embedded-resources src/images/gold.png src/images/platinum.png src/images/silver.png) - target_link_libraries(shadps4 PRIVATE res::embedded) # ImGui resources diff --git a/REUSE.toml b/REUSE.toml index d8f31c2f2..ad2bc3678 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -30,6 +30,7 @@ path = [ "src/images/dump_icon.png", "src/images/exit_icon.png", "src/images/file_icon.png", + "src/images/trophy_icon.png", "src/images/flag_china.png", "src/images/flag_eu.png", "src/images/flag_jp.png", @@ -41,14 +42,17 @@ path = [ "src/images/grid_icon.png", "src/images/keyboard_icon.png", "src/images/iconsize_icon.png", + "src/images/KBM.png", "src/images/ko-fi.png", "src/images/list_icon.png", "src/images/list_mode_icon.png", "src/images/pause_icon.png", "src/images/play_icon.png", "src/images/ps4_controller.png", - "src/images/refresh_icon.png", + "src/images/restart_game_icon.png", + "src/images/refreshlist_icon.png", "src/images/settings_icon.png", + "src/images/fullscreen_icon.png", "src/images/stop_icon.png", "src/images/utils_icon.png", "src/images/shadPS4.icns", @@ -110,4 +114,9 @@ SPDX-License-Identifier = "CC0-1.0" [[annotations]] path = "cmake/CMakeRC.cmake" SPDX-FileCopyrightText = "Copyright (c) 2017 vector-of-bool " -SPDX-License-Identifier = "MIT" \ No newline at end of file +SPDX-License-Identifier = "MIT" + +[[annotations]] +path = "src/video_core/host_shaders/fsr/*" +SPDX-FileCopyrightText = "Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved." +SPDX-License-Identifier = "MIT" diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml index c8c9d5c23..99f9e070d 100644 --- a/dist/net.shadps4.shadPS4.metainfo.xml +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -37,6 +37,9 @@ Game + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.7.0 + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.6.0 diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index bb434677d..3b29a838e 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -26,22 +26,19 @@ if (NOT TARGET fmt::fmt) add_subdirectory(fmt) endif() -if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC) - # If it is clang and MSVC we will add a static lib - # CryptoPP - add_subdirectory(cryptoppwin) - target_include_directories(cryptoppwin INTERFACE cryptoppwin/include) -else() - # CryptoPP - if (NOT TARGET cryptopp::cryptopp) - set(CRYPTOPP_INSTALL OFF) - set(CRYPTOPP_BUILD_TESTING OFF) - set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp) - add_subdirectory(cryptopp-cmake) - file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h") - # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file - set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp") - endif() +# CryptoPP +if (NOT TARGET cryptopp::cryptopp) + set(CRYPTOPP_INSTALL OFF) + set(CRYPTOPP_BUILD_TESTING OFF) + set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp) + # cryptopp instruction set checks do not account for added compile options, + # so disable extensions in the library config to match our chosen target CPU. + set(CRYPTOPP_DISABLE_AESNI ON) + set(CRYPTOPP_DISABLE_AVX2 ON) + add_subdirectory(cryptopp-cmake) + file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h") + # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file + set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp") endif() if (NOT TARGET FFmpeg::ffmpeg) diff --git a/externals/cryptopp b/externals/cryptopp index 60f81a77e..effed0d0b 160000 --- a/externals/cryptopp +++ b/externals/cryptopp @@ -1 +1 @@ -Subproject commit 60f81a77e0c9a0e7ffc1ca1bc438ddfa2e43b78e +Subproject commit effed0d0b865afc23ed67e0916f83734e4b9b3b7 diff --git a/externals/cryptoppwin b/externals/cryptoppwin deleted file mode 160000 index bc3441dd2..000000000 --- a/externals/cryptoppwin +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bc3441dd2d6a9728e747dc0180bc8b9065a2923c diff --git a/src/common/config.cpp b/src/common/config.cpp index 0b720c5b4..09236f30c 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -32,6 +32,7 @@ std::filesystem::path find_fs_path_or(const basic_value& v, const K& ky, namespace Config { static bool isNeo = false; +static bool isDevKit = false; static bool playBGM = false; static bool isTrophyPopupDisabled = false; static int BGMvolume = 50; @@ -53,6 +54,7 @@ static bool isShaderDebug = false; static bool isShowSplash = false; static bool isAutoUpdate = false; static bool isAlwaysShowChangelog = false; +static std::string isSideTrophy = "right"; static bool isNullGpu = false; static bool shouldCopyGPUBuffers = false; static bool shouldDumpShaders = false; @@ -69,6 +71,7 @@ static bool isFpsColor = true; static bool isSeparateLogFilesEnabled = false; static s16 cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) +static double trophyNotificationDuration = 6.0; static bool useUnifiedInputConfig = true; static bool overrideControllerColor = false; static int controllerCustomColorRGB[3] = {0, 0, 255}; @@ -79,7 +82,8 @@ static std::string trophyKey; // Gui static bool load_game_size = true; -std::vector settings_install_dirs = {}; +static std::vector settings_install_dirs = {}; +std::vector install_dirs_enabled = {}; std::filesystem::path settings_addon_install_dir = {}; std::filesystem::path save_data_path = {}; u32 main_window_geometry_x = 400; @@ -103,6 +107,7 @@ static bool showBackgroundImage = true; static bool isFullscreen = false; static std::string fullscreenMode = "Windowed"; static bool isHDRAllowed = false; +static bool showLabelsUnderIcons = true; // Language u32 m_language = 1; // english @@ -164,10 +169,22 @@ bool isNeoModeConsole() { return isNeo; } +bool isDevKitConsole() { + return isDevKit; +} + bool getIsFullscreen() { return isFullscreen; } +bool getShowLabelsUnderIcons() { + return showLabelsUnderIcons; +} + +bool setShowLabelsUnderIcons() { + return false; +} + std::string getFullscreenMode() { return fullscreenMode; } @@ -196,6 +213,10 @@ int getCursorHideTimeout() { return cursorHideTimeout; } +double getTrophyNotificationDuration() { + return trophyNotificationDuration; +} + u32 getScreenWidth() { return screenWidth; } @@ -264,6 +285,10 @@ bool alwaysShowChangelog() { return isAlwaysShowChangelog; } +std::string sideTrophy() { + return isSideTrophy; +} + bool nullGpu() { return isNullGpu; } @@ -372,6 +397,10 @@ void setAlwaysShowChangelog(bool enable) { isAlwaysShowChangelog = enable; } +void setSideTrophy(std::string side) { + isSideTrophy = side; +} + void setNullGpu(bool enable) { isNullGpu = enable; } @@ -407,6 +436,9 @@ void setVblankDiv(u32 value) { void setIsFullscreen(bool enable) { isFullscreen = enable; } +static void setShowLabelsUnderIcons(bool enable) { + showLabelsUnderIcons = enable; +} void setFullscreenMode(std::string mode) { fullscreenMode = mode; @@ -435,6 +467,9 @@ void setCursorState(s16 newCursorState) { void setCursorHideTimeout(int newcursorHideTimeout) { cursorHideTimeout = newcursorHideTimeout; } +void setTrophyNotificationDuration(double newTrophyNotificationDuration) { + trophyNotificationDuration = newTrophyNotificationDuration; +} void setLanguage(u32 language) { m_language = language; @@ -502,22 +537,34 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_h = h; } -bool addGameInstallDir(const std::filesystem::path& dir) { - if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) == - settings_install_dirs.end()) { - settings_install_dirs.push_back(dir); - return true; +bool addGameInstallDir(const std::filesystem::path& dir, bool enabled) { + for (const auto& install_dir : settings_install_dirs) { + if (install_dir.path == dir) { + return false; + } } - return false; + settings_install_dirs.push_back({dir, enabled}); + return true; } void removeGameInstallDir(const std::filesystem::path& dir) { - auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir); + auto iterator = + std::find_if(settings_install_dirs.begin(), settings_install_dirs.end(), + [&dir](const GameInstallDir& install_dir) { return install_dir.path == dir; }); if (iterator != settings_install_dirs.end()) { settings_install_dirs.erase(iterator); } } +void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled) { + auto iterator = + std::find_if(settings_install_dirs.begin(), settings_install_dirs.end(), + [&dir](const GameInstallDir& install_dir) { return install_dir.path == dir; }); + if (iterator != settings_install_dirs.end()) { + iterator->enabled = enabled; + } +} + void setAddonInstallDir(const std::filesystem::path& dir) { settings_addon_install_dir = dir; } @@ -573,8 +620,15 @@ void setEmulatorLanguage(std::string language) { emulator_language = language; } -void setGameInstallDirs(const std::vector& settings_install_dirs_config) { - settings_install_dirs = settings_install_dirs_config; +void setGameInstallDirs(const std::vector& dirs_config) { + settings_install_dirs.clear(); + for (const auto& dir : dirs_config) { + settings_install_dirs.push_back({dir, true}); + } +} + +void setAllGameInstallDirs(const std::vector& dirs_config) { + settings_install_dirs = dirs_config; } void setSaveDataPath(const std::filesystem::path& path) { @@ -597,8 +651,22 @@ u32 getMainWindowGeometryH() { return main_window_geometry_h; } -const std::vector& getGameInstallDirs() { - return settings_install_dirs; +const std::vector getGameInstallDirs() { + std::vector enabled_dirs; + for (const auto& dir : settings_install_dirs) { + if (dir.enabled) { + enabled_dirs.push_back(dir.path); + } + } + return enabled_dirs; +} + +const std::vector getGameInstallDirsEnabled() { + std::vector enabled_dirs; + for (const auto& dir : settings_install_dirs) { + enabled_dirs.push_back(dir.enabled); + } + return enabled_dirs; } std::filesystem::path getAddonInstallDir() { @@ -704,8 +772,11 @@ void load(const std::filesystem::path& path) { const toml::value& general = data.at("General"); isNeo = toml::find_or(general, "isPS4Pro", false); + isDevKit = toml::find_or(general, "isDevKit", false); playBGM = toml::find_or(general, "playBGM", false); isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false); + trophyNotificationDuration = + toml::find_or(general, "trophyNotificationDuration", 5.0); BGMvolume = toml::find_or(general, "BGMvolume", 50); enableDiscordRPC = toml::find_or(general, "enableDiscordRPC", true); logFilter = toml::find_or(general, "logFilter", ""); @@ -719,6 +790,7 @@ void load(const std::filesystem::path& path) { isShowSplash = toml::find_or(general, "showSplash", true); isAutoUpdate = toml::find_or(general, "autoUpdate", false); isAlwaysShowChangelog = toml::find_or(general, "alwaysShowChangelog", false); + isSideTrophy = toml::find_or(general, "sideTrophy", "right"); separateupdatefolder = toml::find_or(general, "separateUpdateEnabled", false); compatibilityData = toml::find_or(general, "compatibilityEnabled", false); checkCompatibilityOnStartup = @@ -789,8 +861,22 @@ void load(const std::filesystem::path& path) { const auto install_dir_array = toml::find_or>(gui, "installDirs", {}); - for (const auto& dir : install_dir_array) { - addGameInstallDir(std::filesystem::path{dir}); + + try { + install_dirs_enabled = toml::find>(gui, "installDirsEnabled"); + } catch (...) { + // If it does not exist, assume that all are enabled. + install_dirs_enabled.resize(install_dir_array.size(), true); + } + + if (install_dirs_enabled.size() < install_dir_array.size()) { + install_dirs_enabled.resize(install_dir_array.size(), true); + } + + settings_install_dirs.clear(); + for (size_t i = 0; i < install_dir_array.size(); i++) { + settings_install_dirs.push_back( + {std::filesystem::path{install_dir_array[i]}, install_dirs_enabled[i]}); } save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {}); @@ -833,6 +919,37 @@ void load(const std::filesystem::path& path) { } } +void sortTomlSections(toml::ordered_value& data) { + toml::ordered_value ordered_data; + std::vector section_order = {"General", "Input", "GPU", "Vulkan", + "Debug", "Keys", "GUI", "Settings"}; + + for (const auto& section : section_order) { + if (data.contains(section)) { + std::vector keys; + for (const auto& item : data.at(section).as_table()) { + keys.push_back(item.first); + } + + std::sort(keys.begin(), keys.end(), [](const std::string& a, const std::string& b) { + return std::lexicographical_compare( + a.begin(), a.end(), b.begin(), b.end(), [](char a_char, char b_char) { + return std::tolower(a_char) < std::tolower(b_char); + }); + }); + + toml::ordered_value ordered_section; + for (const auto& key : keys) { + ordered_section[key] = data.at(section).at(key); + } + + ordered_data[section] = ordered_section; + } + } + + data = ordered_data; +} + void save(const std::filesystem::path& path) { toml::ordered_value data; @@ -856,7 +973,9 @@ void save(const std::filesystem::path& path) { } data["General"]["isPS4Pro"] = isNeo; + data["General"]["isDevKit"] = isDevKit; data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; + data["General"]["trophyNotificationDuration"] = trophyNotificationDuration; data["General"]["playBGM"] = playBGM; data["General"]["BGMvolume"] = BGMvolume; data["General"]["enableDiscordRPC"] = enableDiscordRPC; @@ -868,6 +987,7 @@ void save(const std::filesystem::path& path) { data["General"]["showSplash"] = isShowSplash; data["General"]["autoUpdate"] = isAutoUpdate; data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog; + data["General"]["sideTrophy"] = isSideTrophy; data["General"]["separateUpdateEnabled"] = separateupdatefolder; data["General"]["compatibilityEnabled"] = compatibilityData; data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; @@ -900,14 +1020,37 @@ void save(const std::filesystem::path& path) { data["Debug"]["CollectShader"] = isShaderDebug; data["Debug"]["isSeparateLogFilesEnabled"] = isSeparateLogFilesEnabled; data["Debug"]["FPSColor"] = isFpsColor; - data["Keys"]["TrophyKey"] = trophyKey; std::vector install_dirs; - for (const auto& dirString : settings_install_dirs) { - install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data}); + std::vector install_dirs_enabled; + + // temporary structure for ordering + struct DirEntry { + std::string path_str; + bool enabled; + }; + + std::vector sorted_dirs; + for (const auto& dirInfo : settings_install_dirs) { + sorted_dirs.push_back( + {std::string{fmt::UTF(dirInfo.path.u8string()).data}, dirInfo.enabled}); } + + // Sort directories alphabetically + std::sort(sorted_dirs.begin(), sorted_dirs.end(), [](const DirEntry& a, const DirEntry& b) { + return std::lexicographical_compare( + a.path_str.begin(), a.path_str.end(), b.path_str.begin(), b.path_str.end(), + [](char a_char, char b_char) { return std::tolower(a_char) < std::tolower(b_char); }); + }); + + for (const auto& entry : sorted_dirs) { + install_dirs.push_back(entry.path_str); + install_dirs_enabled.push_back(entry.enabled); + } + data["GUI"]["installDirs"] = install_dirs; + data["GUI"]["installDirsEnabled"] = install_dirs_enabled; data["GUI"]["saveDataPath"] = std::string{fmt::UTF(save_data_path.u8string()).data}; data["GUI"]["loadGameSizeEnabled"] = load_game_size; @@ -918,9 +1061,13 @@ void save(const std::filesystem::path& path) { data["GUI"]["showBackgroundImage"] = showBackgroundImage; data["Settings"]["consoleLanguage"] = m_language; + // Sorting of TOML sections + sortTomlSections(data); + std::ofstream file(path, std::ios::binary); file << data; file.close(); + saveMainWindow(path); } @@ -962,6 +1109,9 @@ void saveMainWindow(const std::filesystem::path& path) { data["GUI"]["elfDirs"] = m_elf_viewer; data["GUI"]["recentFiles"] = m_recent_files; + // Sorting of TOML sections + sortTomlSections(data); + std::ofstream file(path, std::ios::binary); file << data; file.close(); @@ -970,6 +1120,7 @@ void saveMainWindow(const std::filesystem::path& path) { void setDefaultValues() { isHDRAllowed = false; isNeo = false; + isDevKit = false; isFullscreen = false; isTrophyPopupDisabled = false; playBGM = false; @@ -988,6 +1139,7 @@ void setDefaultValues() { chooseHomeTab = "General"; cursorState = HideCursorState::Idle; cursorHideTimeout = 5; + trophyNotificationDuration = 6.0; backButtonBehavior = "left"; useSpecialPad = false; specialPadClass = 1; @@ -996,6 +1148,7 @@ void setDefaultValues() { isShowSplash = false; isAutoUpdate = false; isAlwaysShowChangelog = false; + isSideTrophy = "right"; isNullGpu = false; shouldDumpShaders = false; vblankDivider = 1; diff --git a/src/common/config.h b/src/common/config.h index abf8da8aa..3a0bf252c 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -9,6 +9,11 @@ namespace Config { +struct GameInstallDir { + std::filesystem::path path; + bool enabled; +}; + enum HideCursorState : s16 { Never, Idle, Always }; void load(const std::filesystem::path& path); @@ -21,8 +26,11 @@ bool GetLoadGameSizeEnabled(); std::filesystem::path GetSaveDataPath(); void setLoadGameSizeEnabled(bool enable); bool getIsFullscreen(); +bool getShowLabelsUnderIcons(); +bool setShowLabelsUnderIcons(); std::string getFullscreenMode(); bool isNeoModeConsole(); +bool isDevKitConsole(); bool getPlayBGM(); int getBGMvolume(); bool getisTrophyPopupDisabled(); @@ -41,6 +49,7 @@ std::string getChooseHomeTab(); s16 getCursorState(); int getCursorHideTimeout(); +double getTrophyNotificationDuration(); std::string getBackButtonBehavior(); bool getUseSpecialPad(); int getSpecialPadClass(); @@ -62,6 +71,7 @@ bool collectShadersForDebug(); bool showSplash(); bool autoUpdate(); bool alwaysShowChangelog(); +std::string sideTrophy(); bool nullGpu(); bool copyGPUCmdBuffers(); bool dumpShaders(); @@ -75,6 +85,7 @@ void setCollectShaderForDebug(bool enable); void setShowSplash(bool enable); void setAutoUpdate(bool enable); void setAlwaysShowChangelog(bool enable); +void setSideTrophy(std::string side); void setNullGpu(bool enable); void setAllowHDR(bool enable); void setCopyGPUCmdBuffers(bool enable); @@ -95,7 +106,8 @@ void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); void setChooseHomeTab(const std::string& type); void setSeparateUpdateEnabled(bool use); -void setGameInstallDirs(const std::vector& settings_install_dirs_config); +void setGameInstallDirs(const std::vector& dirs_config); +void setAllGameInstallDirs(const std::vector& dirs_config); void setSaveDataPath(const std::filesystem::path& path); void setCompatibilityEnabled(bool use); void setCheckCompatibilityOnStartup(bool use); @@ -104,6 +116,7 @@ void setShowBackgroundImage(bool show); void setCursorState(s16 cursorState); void setCursorHideTimeout(int newcursorHideTimeout); +void setTrophyNotificationDuration(double newTrophyNotificationDuration); void setBackButtonBehavior(const std::string& type); void setUseSpecialPad(bool use); void setSpecialPadClass(int type); @@ -129,8 +142,9 @@ void setVkGuestMarkersEnabled(bool enable); // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); -bool addGameInstallDir(const std::filesystem::path& dir); +bool addGameInstallDir(const std::filesystem::path& dir, bool enabled = true); void removeGameInstallDir(const std::filesystem::path& dir); +void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled); void setAddonInstallDir(const std::filesystem::path& dir); void setMainWindowTheme(u32 theme); void setIconSize(u32 size); @@ -149,7 +163,8 @@ u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); u32 getMainWindowGeometryW(); u32 getMainWindowGeometryH(); -const std::vector& getGameInstallDirs(); +const std::vector getGameInstallDirs(); +const std::vector getGameInstallDirsEnabled(); std::filesystem::path getAddonInstallDir(); u32 getMainWindowTheme(); u32 getIconSize(); diff --git a/src/common/elf_info.h b/src/common/elf_info.h index d885709cd..062cee012 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -69,6 +70,8 @@ class ElfInfo { u32 raw_firmware_ver = 0; PSFAttributes psf_attributes{}; + std::filesystem::path splash_path{}; + public: static constexpr u32 FW_15 = 0x1500000; static constexpr u32 FW_16 = 0x1600000; @@ -116,6 +119,10 @@ public: ASSERT(initialized); return psf_attributes; } + + [[nodiscard]] const std::filesystem::path& GetSplashPath() const { + return splash_path; + } }; } // namespace Common diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index 067010a26..3efadc6ea 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -125,12 +125,15 @@ namespace { [[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) { switch (origin) { case SeekOrigin::SetOrigin: - default: return SEEK_SET; case SeekOrigin::CurrentPosition: return SEEK_CUR; case SeekOrigin::End: return SEEK_END; + default: + LOG_ERROR(Common_Filesystem, "Unsupported origin {}, defaulting to SEEK_SET", + static_cast(origin)); + return SEEK_SET; } } @@ -377,20 +380,6 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const { return false; } - if (False(file_access_mode & (FileAccessMode::Write | FileAccessMode::Append))) { - u64 size = GetSize(); - if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; - } else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; - } else if (origin == SeekOrigin::End && offset > 0) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; - } - } - errno = 0; const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0; diff --git a/src/common/io_file.h b/src/common/io_file.h index 45787a092..fb20a2bc5 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -61,6 +61,8 @@ enum class SeekOrigin : u32 { SetOrigin, // Seeks from the start of the file. CurrentPosition, // Seeks from the current file pointer position. End, // Seeks from the end of the file. + SeekHole, // Seeks from the start of the next hole in the file. + SeekData, // Seeks from the start of the next non-hole region in the file. }; class IOFile final { diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index a4312fada..702d0fabc 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/logging/log.h" #include "common/path_util.h" @@ -16,6 +17,8 @@ #ifdef _WIN32 // This is the maximum number of UTF-16 code units permissible in Windows file paths #define MAX_PATH 260 +#include +#include #else // This is the maximum number of UTF-8 code units permissible in all other OSes' file paths #define MAX_PATH 1024 @@ -105,6 +108,10 @@ static auto UserPaths = [] { } else { user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4"; } +#elif _WIN32 + TCHAR appdata[MAX_PATH] = {0}; + SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, appdata); + user_dir = std::filesystem::path(appdata) / "shadPS4"; #endif } @@ -128,6 +135,22 @@ static auto UserPaths = [] { create_path(PathType::CheatsDir, user_dir / CHEATS_DIR); create_path(PathType::PatchesDir, user_dir / PATCHES_DIR); create_path(PathType::MetaDataDir, user_dir / METADATA_DIR); + create_path(PathType::CustomTrophy, user_dir / CUSTOM_TROPHY); + + std::ofstream notice_file(user_dir / CUSTOM_TROPHY / "Notice.txt"); + if (notice_file.is_open()) { + notice_file + << "++++++++++++++++++++++++++++++++\n+ Custom Trophy Images / Sound " + "+\n++++++++++++++++++++++++++++++++\n\nYou can add custom images to the " + "trophies.\n*We recommend a square resolution image, for example 200x200, 500x500, " + "the same size as the height and width.\nIn this folder ('user\\custom_trophy'), " + "add the files with the following " + "names:\n\nbronze.png\nsilver.png\ngold.png\nplatinum.png\n\nYou can add a custom " + "sound for trophy notifications.\n*By default, no audio is played unless it is in " + "this folder and you are using the QT version.\nIn this folder " + "('user\\custom_trophy'), add the files with the following names:\n\ntrophy.mp3"; + notice_file.close(); + } return paths; }(); @@ -222,4 +245,4 @@ std::filesystem::path PathFromQString(const QString& path) { } #endif -} // namespace Common::FS \ No newline at end of file +} // namespace Common::FS diff --git a/src/common/path_util.h b/src/common/path_util.h index 7190378d6..2fd9b1588 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -27,6 +27,7 @@ enum class PathType { CheatsDir, // Where cheats are stored. PatchesDir, // Where patches are stored. MetaDataDir, // Where game metadata (e.g. trophies and menu backgrounds) is stored. + CustomTrophy, // Where custom files for trophies are stored. }; constexpr auto PORTABLE_DIR = "user"; @@ -44,6 +45,7 @@ constexpr auto CAPTURES_DIR = "captures"; constexpr auto CHEATS_DIR = "cheats"; constexpr auto PATCHES_DIR = "patches"; constexpr auto METADATA_DIR = "game_data"; +constexpr auto CUSTOM_TROPHY = "custom_trophy"; // Filenames constexpr auto LOG_FILE = "shad_log.txt"; diff --git a/src/common/string_literal.h b/src/common/string_literal.h new file mode 100644 index 000000000..9f64f62bd --- /dev/null +++ b/src/common/string_literal.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +template +struct StringLiteral { + static constexpr size_t len = N; + + constexpr StringLiteral(const C (&str)[N]) { + std::copy_n(str, N, value); + } + + C value[N]{}; +}; diff --git a/src/common/version.h b/src/common/version.h index e7f6cc817..652f36e6d 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -8,7 +8,7 @@ namespace Common { -constexpr char VERSION[] = "0.6.1 WIP"; +constexpr char VERSION[] = "0.7.1 WIP"; constexpr bool isRelease = false; } // namespace Common diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index 21acf1a7b..a9f6c67a8 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -180,28 +180,44 @@ static void RestoreStack(Xbyak::CodeGenerator& c) { c.mov(rsp, qword[reinterpret_cast(stack_pointer_slot * sizeof(void*))]); } +/// Validates that the dst register is supported given the SaveStack/RestoreStack implementation. +static void ValidateDst(const Xbyak::Reg& dst) { + // No restrictions. +} + #else -// These utilities are not implemented as we can't save anything to thread local storage without -// temporary registers. void InitializeThreadPatchStack() { // No-op } +// NOTE: Since stack pointer here is subtracted through safe zone and not saved anywhere, +// it must not be modified during the instruction. Otherwise, we will not be able to find +// and load registers back from where they were saved. Thus, a limitation is placed on +// instructions, that they must not use the stack pointer register as a destination. + /// Saves the stack pointer to thread local storage and loads the patch stack. static void SaveStack(Xbyak::CodeGenerator& c) { - UNIMPLEMENTED(); + c.lea(rsp, ptr[rsp - 128]); // red zone } /// Restores the stack pointer from thread local storage. static void RestoreStack(Xbyak::CodeGenerator& c) { - UNIMPLEMENTED(); + c.lea(rsp, ptr[rsp + 128]); // red zone +} + +/// Validates that the dst register is supported given the SaveStack/RestoreStack implementation. +static void ValidateDst(const Xbyak::Reg& dst) { + // Stack pointer is not preserved, so it can't be used as a dst. + ASSERT_MSG(dst.getIdx() != rsp.getIdx(), "Stack pointer not supported as destination."); } #endif /// Switches to the patch stack, saves registers, and restores the original stack. static void SaveRegisters(Xbyak::CodeGenerator& c, const std::initializer_list regs) { + // Uses a more robust solution for saving registers on MacOS to avoid potential stack corruption + // if games decide to not follow the ABI and use the red zone. SaveStack(c); for (const auto& reg : regs) { c.push(reg.cvt64()); @@ -257,12 +273,11 @@ static void RestoreContext(Xbyak::CodeGenerator& c, const Xbyak::Operand& dst, RestoreStack(c); } -#ifdef __APPLE__ - static void GenerateANDN(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src1 = ZydisToXbyakRegisterOperand(operands[1]); const auto src2 = ZydisToXbyakOperand(operands[2]); + ValidateDst(dst); // Check if src2 is a memory operand or a register different to dst. // In those cases, we don't need to use a temporary register and are free to modify dst. @@ -301,6 +316,7 @@ static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenera const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src = ZydisToXbyakOperand(operands[1]); const auto start_len = ZydisToXbyakRegisterOperand(operands[2]); + ValidateDst(dst); const Xbyak::Reg32e shift(Xbyak::Operand::RCX, static_cast(start_len.getBit())); const auto scratch1 = @@ -338,6 +354,7 @@ static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenera static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src = ZydisToXbyakOperand(operands[1]); + ValidateDst(dst); const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit()); @@ -367,6 +384,7 @@ static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerat static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src = ZydisToXbyakOperand(operands[1]); + ValidateDst(dst); const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit()); @@ -395,9 +413,37 @@ static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGener RestoreRegisters(c, {scratch}); } +static void GenerateTZCNT(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { + const auto dst = ZydisToXbyakRegisterOperand(operands[0]); + const auto src = ZydisToXbyakOperand(operands[1]); + ValidateDst(dst); + + Xbyak::Label src_zero, end; + + c.cmp(*src, 0); + c.je(src_zero); + + // If src is not zero, functions like a BSF, but also clears the CF + c.bsf(dst, *src); + c.clc(); + c.jmp(end); + + c.L(src_zero); + c.mov(dst, operands[0].size); + // Since dst is not zero, also set ZF to zero. Testing dst with itself when we know + // it isn't zero is a good way to do this. + // Use cvt32 to avoid REX/Operand size prefixes. + c.test(dst.cvt32(), dst.cvt32()); + // When source is zero, TZCNT also sets CF. + c.stc(); + + c.L(end); +} + static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto src = ZydisToXbyakOperand(operands[1]); + ValidateDst(dst); const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit()); @@ -426,6 +472,8 @@ static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerat RestoreRegisters(c, {scratch}); } +#ifdef __APPLE__ + static __attribute__((sysv_abi)) void PerformVCVTPH2PS(float* out, const half_float::half* in, const u32 count) { for (u32 i = 0; i < count; i++) { @@ -616,6 +664,11 @@ static bool FilterNoSSE4a(const ZydisDecodedOperand*) { return !cpu.has(Cpu::tSSE4a); } +static bool FilterNoBMI1(const ZydisDecodedOperand*) { + Cpu cpu; + return !cpu.has(Cpu::tBMI1); +} + static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; @@ -897,14 +950,16 @@ static const std::unordered_map Patches = { {ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}}, {ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}}, + // BMI1 + {ZYDIS_MNEMONIC_ANDN, {FilterNoBMI1, GenerateANDN, true}}, + {ZYDIS_MNEMONIC_BEXTR, {FilterNoBMI1, GenerateBEXTR, true}}, + {ZYDIS_MNEMONIC_BLSI, {FilterNoBMI1, GenerateBLSI, true}}, + {ZYDIS_MNEMONIC_BLSMSK, {FilterNoBMI1, GenerateBLSMSK, true}}, + {ZYDIS_MNEMONIC_BLSR, {FilterNoBMI1, GenerateBLSR, true}}, + {ZYDIS_MNEMONIC_TZCNT, {FilterNoBMI1, GenerateTZCNT, true}}, + #ifdef __APPLE__ // Patches for instruction sets not supported by Rosetta 2. - // BMI1 - {ZYDIS_MNEMONIC_ANDN, {FilterRosetta2Only, GenerateANDN, true}}, - {ZYDIS_MNEMONIC_BEXTR, {FilterRosetta2Only, GenerateBEXTR, true}}, - {ZYDIS_MNEMONIC_BLSI, {FilterRosetta2Only, GenerateBLSI, true}}, - {ZYDIS_MNEMONIC_BLSMSK, {FilterRosetta2Only, GenerateBLSMSK, true}}, - {ZYDIS_MNEMONIC_BLSR, {FilterRosetta2Only, GenerateBLSR, true}}, // F16C {ZYDIS_MNEMONIC_VCVTPH2PS, {FilterRosetta2Only, GenerateVCVTPH2PS, true}}, {ZYDIS_MNEMONIC_VCVTPS2PH, {FilterRosetta2Only, GenerateVCVTPS2PH, true}}, diff --git a/src/core/debug_state.h b/src/core/debug_state.h index 217efd1a9..b1b8c00d6 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -158,6 +158,10 @@ public: float Framerate = 1.0f / 60.0f; float FrameDeltaTime; + std::pair game_resolution{}; + std::pair output_resolution{}; + bool is_using_fsr{}; + void ShowDebugMessage(std::string message) { if (message.empty()) { return; diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 603d76df5..94b39e801 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "SDL3/SDL_log.h" #include "layer.h" #include @@ -21,8 +22,8 @@ extern std::unique_ptr presenter; using namespace ImGui; -using namespace Core::Devtools; -using L = Core::Devtools::Layer; +using namespace ::Core::Devtools; +using L = ::Core::Devtools::Layer; static bool show_simple_fps = false; static bool visibility_toggled = false; @@ -81,8 +82,24 @@ void L::DrawMenuBar() { ImGui::EndMenu(); } if (BeginMenu("Display")) { + auto& pp_settings = presenter->GetPPSettingsRef(); if (BeginMenu("Brightness")) { - SliderFloat("Gamma", &presenter->GetGammaRef(), 0.1f, 2.0f); + SliderFloat("Gamma", &pp_settings.gamma, 0.1f, 2.0f); + ImGui::EndMenu(); + } + if (BeginMenu("FSR")) { + auto& fsr = presenter->GetFsrSettingsRef(); + Checkbox("FSR Enabled", &fsr.enable); + BeginDisabled(!fsr.enable); + { + Checkbox("RCAS", &fsr.use_rcas); + BeginDisabled(!fsr.use_rcas); + { + SliderFloat("RCAS Attenuation", &fsr.rcas_attenuation, 0.0, 3.0); + } + EndDisabled(); + } + EndDisabled(); ImGui::EndMenu(); } ImGui::EndMenu(); @@ -101,22 +118,6 @@ void L::DrawMenuBar() { EndMainMenuBar(); } - - if (IsKeyPressed(ImGuiKey_F9, false)) { - if (io.KeyCtrl && io.KeyAlt) { - if (!DebugState.ShouldPauseInSubmit()) { - DebugState.RequestFrameDump(dump_frame_count); - } - } - if (!io.KeyCtrl && !io.KeyAlt) { - if (isSystemPaused) { - DebugState.ResumeGuestThreads(); - } else { - DebugState.PauseGuestThreads(); - } - } - } - if (open_popup_options) { OpenPopup("GPU Tools Options"); just_opened_options = true; @@ -365,6 +366,32 @@ void L::Draw() { visibility_toggled = true; } + if (IsKeyPressed(ImGuiKey_F9, false)) { + if (io.KeyCtrl && io.KeyAlt) { + if (!DebugState.ShouldPauseInSubmit()) { + DebugState.RequestFrameDump(dump_frame_count); + } + } else { + if (DebugState.IsGuestThreadsPaused()) { + DebugState.ResumeGuestThreads(); + SDL_Log("Game resumed from Keyboard"); + show_pause_status = false; + } else { + DebugState.PauseGuestThreads(); + SDL_Log("Game paused from Keyboard"); + show_pause_status = true; + } + visibility_toggled = true; + } + } + + if (show_pause_status) { + ImVec2 pos = ImVec2(10, 10); + ImU32 color = IM_COL32(255, 255, 255, 255); + + ImGui::GetForegroundDrawList()->AddText(pos, color, "Game Paused Press F9 to Resume"); + } + if (show_simple_fps) { if (Begin("Video Info", nullptr, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | diff --git a/src/core/devtools/layer.h b/src/core/devtools/layer.h index 5bb53fbdb..9e949c8e9 100644 --- a/src/core/devtools/layer.h +++ b/src/core/devtools/layer.h @@ -19,6 +19,7 @@ public: static void SetupSettings(); void Draw() override; + bool show_pause_status = false; }; } // namespace Core::Devtools diff --git a/src/core/devtools/widget/frame_graph.cpp b/src/core/devtools/widget/frame_graph.cpp index d93de571a..8f3e133f5 100644 --- a/src/core/devtools/widget/frame_graph.cpp +++ b/src/core/devtools/widget/frame_graph.cpp @@ -74,7 +74,7 @@ void FrameGraph::Draw() { if (!is_open) { return; } - SetNextWindowSize({340.0, 185.0f}, ImGuiCond_FirstUseEver); + SetNextWindowSize({308.0, 270.0f}, ImGuiCond_FirstUseEver); if (Begin("Video debug info", &is_open)) { const auto& ctx = *GImGui; const auto& io = ctx.IO; @@ -88,13 +88,20 @@ void FrameGraph::Draw() { frameRate = 1000.0f / deltaTime; } + SeparatorText("Frame graph"); + DrawFrameGraph(); + + SeparatorText("Renderer info"); + Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate); Text("Presenter time: %.3f ms (%.1f FPS)", io.DeltaTime * 1000.0f, 1.0f / io.DeltaTime); Text("Flip frame: %d Gnm submit frame: %d", DebugState.flip_frame_count.load(), DebugState.gnm_frame_count.load()); - - SeparatorText("Frame graph"); - DrawFrameGraph(); + Text("Game Res: %dx%d", DebugState.game_resolution.first, + DebugState.game_resolution.second); + Text("Output Res: %dx%d", DebugState.output_resolution.first, + DebugState.output_resolution.second); + Text("FSR: %s", DebugState.is_using_fsr ? "on" : "off"); } End(); } diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp index a6b5eb9a8..ecc5f10a4 100644 --- a/src/core/file_format/pkg.cpp +++ b/src/core/file_format/pkg.cpp @@ -350,7 +350,7 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: auto title_id = GetTitleID(); if (parent_path.filename() != title_id && - !fmt::UTF(extract_path.u8string()).data.ends_with("-UPDATE")) { + !fmt::UTF(extract_path.u8string()).data.ends_with("-patch")) { extractPaths[ndinode_counter] = parent_path / title_id; } else { // DLCs path has different structure diff --git a/src/core/file_format/splash.cpp b/src/core/file_format/splash.cpp deleted file mode 100644 index 4eb701cf7..000000000 --- a/src/core/file_format/splash.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "common/assert.h" -#include "common/io_file.h" -#include "common/stb.h" -#include "splash.h" - -bool Splash::Open(const std::filesystem::path& filepath) { - ASSERT_MSG(filepath.extension().string() == ".png", "Unexpected file format passed"); - - Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); - if (!file.IsOpen()) { - return false; - } - - std::vector png_file{}; - const auto png_size = file.GetSize(); - png_file.resize(png_size); - file.Seek(0); - file.Read(png_file); - - auto* img_mem = stbi_load_from_memory(png_file.data(), png_file.size(), - reinterpret_cast(&img_info.width), - reinterpret_cast(&img_info.height), - reinterpret_cast(&img_info.num_channels), 4); - if (!img_mem) { - return false; - } - - const auto img_size = img_info.GetSizeBytes(); - img_data.resize(img_size); - std::memcpy(img_data.data(), img_mem, img_size); - stbi_image_free(img_mem); - return true; -} diff --git a/src/core/file_format/splash.h b/src/core/file_format/splash.h deleted file mode 100644 index 7c563f317..000000000 --- a/src/core/file_format/splash.h +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include "common/types.h" - -class Splash { -public: - struct ImageInfo { - u32 width; - u32 height; - u32 num_channels; - - u32 GetSizeBytes() const { - return width * height * 4; // we always forcing rgba8 for simplicity - } - }; - - Splash() = default; - ~Splash() = default; - - bool Open(const std::filesystem::path& filepath); - [[nodiscard]] bool IsLoaded() const { - return img_data.size(); - } - - const auto& GetImageData() const { - return img_data; - } - - ImageInfo GetImageInfo() const { - return img_info; - } - -private: - ImageInfo img_info{}; - std::vector img_data{}; -}; diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index ec940503f..4dad44874 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -70,6 +70,10 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea std::filesystem::path host_path = mount->host_path / rel_path; std::filesystem::path patch_path = mount->host_path; patch_path += "-UPDATE"; + if (!std::filesystem::exists(patch_path)) { + patch_path = mount->host_path; + patch_path += "-patch"; + } patch_path /= rel_path; if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && diff --git a/src/core/libraries/disc_map/disc_map.cpp b/src/core/libraries/disc_map/disc_map.cpp index bb566a149..e8b40e624 100644 --- a/src/core/libraries/disc_map/disc_map.cpp +++ b/src/core/libraries/disc_map/disc_map.cpp @@ -9,29 +9,29 @@ namespace Libraries::DiscMap { -int PS4_SYSV_ABI sceDiscMapGetPackageSize() { - LOG_WARNING(Lib_DiscMap, "(DUMMY) called"); +int PS4_SYSV_ABI sceDiscMapGetPackageSize(s64 fflags, int* ret1, int* ret2) { return ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO; } -int PS4_SYSV_ABI sceDiscMapIsRequestOnHDD() { - LOG_WARNING(Lib_DiscMap, "(DUMMY) called"); +int PS4_SYSV_ABI sceDiscMapIsRequestOnHDD(char* path, s64 offset, s64 nbytes, int* ret) { return ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO; } -int PS4_SYSV_ABI Func_7C980FFB0AA27E7A() { - LOG_ERROR(Lib_DiscMap, "(STUBBED) called"); +int PS4_SYSV_ABI Func_7C980FFB0AA27E7A(char* path, s64 offset, s64 nbytes, int* flags, int* ret1, + int* ret2) { + *flags = 0; + *ret1 = 0; + *ret2 = 0; return ORBIS_OK; } -int PS4_SYSV_ABI Func_8A828CAEE7EDD5E9() { - LOG_ERROR(Lib_DiscMap, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI Func_8A828CAEE7EDD5E9(char* path, s64 offset, s64 nbytes, int* flags, int* ret1, + int* ret2) { + return ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO; } int PS4_SYSV_ABI Func_E7EBCE96E92F91F8() { - LOG_ERROR(Lib_DiscMap, "(STUBBED) called"); - return ORBIS_OK; + return ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO; } void RegisterlibSceDiscMap(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/disc_map/disc_map.h b/src/core/libraries/disc_map/disc_map.h index 08abee632..dc8b875ac 100644 --- a/src/core/libraries/disc_map/disc_map.h +++ b/src/core/libraries/disc_map/disc_map.h @@ -10,10 +10,12 @@ class SymbolsResolver; } namespace Libraries::DiscMap { -int PS4_SYSV_ABI sceDiscMapGetPackageSize(); -int PS4_SYSV_ABI sceDiscMapIsRequestOnHDD(); -int PS4_SYSV_ABI Func_7C980FFB0AA27E7A(); -int PS4_SYSV_ABI Func_8A828CAEE7EDD5E9(); +int PS4_SYSV_ABI sceDiscMapGetPackageSize(s64 fflags, int* ret1, int* ret2); +int PS4_SYSV_ABI sceDiscMapIsRequestOnHDD(char* path, s64 offset, s64 nbytes, int* ret); +int PS4_SYSV_ABI Func_7C980FFB0AA27E7A(char* path, s64 offset, s64 nbytes, int* flags, int* ret1, + int* ret2); +int PS4_SYSV_ABI Func_8A828CAEE7EDD5E9(char* path, s64 offset, s64 nbytes, int* flags, int* ret1, + int* ret2); int PS4_SYSV_ABI Func_E7EBCE96E92F91F8(); void RegisterlibSceDiscMap(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index e2e865def..e8560b2b8 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -1087,7 +1087,8 @@ s32 PS4_SYSV_ABI sceGnmInsertWaitFlipDone(u32* cmdbuf, u32 size, s32 vo_handle, } uintptr_t label_addr{}; - VideoOut::sceVideoOutGetBufferLabelAddress(vo_handle, &label_addr); + ASSERT_MSG(VideoOut::sceVideoOutGetBufferLabelAddress(vo_handle, &label_addr) == 16, + "sceVideoOutGetBufferLabelAddress call failed"); auto* wait_reg_mem = reinterpret_cast(cmdbuf); wait_reg_mem->header = PM4Type3Header{PM4ItOpcode::WaitRegMem, 5}; @@ -2041,7 +2042,8 @@ static inline s32 PatchFlipRequest(u32* cmdbuf, u32 size, u32 vo_handle, u32 buf } uintptr_t label_addr{}; - VideoOut::sceVideoOutGetBufferLabelAddress(vo_handle, &label_addr); + ASSERT_MSG(VideoOut::sceVideoOutGetBufferLabelAddress(vo_handle, &label_addr) == 16, + "sceVideoOutGetBufferLabelAddress call failed"); // Write event to lock the VO surface auto* write_lock = reinterpret_cast(cmdbuf); diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp index dfd659db8..1c61bc276 100644 --- a/src/core/libraries/ime/ime.cpp +++ b/src/core/libraries/ime/ime.cpp @@ -49,9 +49,9 @@ public: // Are we supposed to call the event handler on init with // ADD_OSK? - if (!ime_mode && False(m_param.key.option & OrbisImeKeyboardOption::AddOsk)) { + /* if (!ime_mode && False(m_param.key.option & OrbisImeKeyboardOption::AddOsk)) { Execute(nullptr, &openEvent, true); - } + }*/ if (ime_mode) { g_ime_state = ImeState(&m_param.ime); @@ -274,6 +274,13 @@ s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* par if (!param) { return ORBIS_IME_ERROR_INVALID_ADDRESS; } + if (!param->arg) { + return ORBIS_IME_ERROR_INVALID_ARG; + } + if (!param->handler) { + return ORBIS_IME_ERROR_INVALID_HANDLER; + } + if (g_keyboard_handler) { return ORBIS_IME_ERROR_BUSY; } diff --git a/src/core/libraries/ime/ime.h b/src/core/libraries/ime/ime.h index 448ee6896..fcf381048 100644 --- a/src/core/libraries/ime/ime.h +++ b/src/core/libraries/ime/ime.h @@ -20,7 +20,7 @@ enum class OrbisImeKeyboardOption : u32 { Repeat = 1, RepeatEachKey = 2, AddOsk = 4, - EffectiveWithTime = 8, + EffectiveWithIme = 8, DisableResume = 16, DisableCapslockWithoutShift = 32, }; diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 0150c11f5..3321559ed 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -18,6 +18,7 @@ #include "core/file_sys/fs.h" #include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/libs.h" #include "core/memory.h" #include "kernel.h" @@ -57,7 +58,7 @@ static std::map available_device = { namespace Libraries::Kernel { -int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { +s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode); auto* h = Common::Singleton::Instance(); auto* mnt = Common::Singleton::Instance(); @@ -99,7 +100,8 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { file->m_host_name = mnt->GetHostPath(file->m_guest_name); if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist h->DeleteHandle(handle); - return ORBIS_KERNEL_ERROR_ENOTDIR; + *__Error() = POSIX_ENOENT; + return -1; } else { if (create) { return handle; // dir already exists @@ -116,61 +118,87 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { } else { file->m_guest_name = path; file->m_host_name = mnt->GetHostPath(file->m_guest_name); + bool exists = std::filesystem::exists(file->m_host_name); int e = 0; - if (read) { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); - } else if (write && (create || truncate)) { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); - } else if (write && create && append) { // CUSA04729 (appends app0/shaderlist.txt) - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); - } else if (rdwr) { - if (create) { // Create an empty file first. - Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write); + + if (create) { + if (excl && exists) { + // Error if file exists + h->DeleteHandle(handle); + *__Error() = POSIX_EEXIST; + return -1; } - // RW, then scekernelWrite is called and savedata is written just fine now. - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); - } else if (write) { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); - } else { - UNREACHABLE(); - } - if (e != 0) { + // Create file if it doesn't exist + Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write); + } else if (!exists) { + // File to open doesn't exist, return ENOENT h->DeleteHandle(handle); - return ErrnoToSceKernelError(e); + *__Error() = POSIX_ENOENT; + return -1; + } + + if (read) { + // Read only + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); + } else if (write) { + // Write only + if (append) { + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); + } else { + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); + } + } else if (rdwr) { + // Read and write + if (append) { + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); + } else { + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); + } + } else { + // Invalid flags + *__Error() = POSIX_EINVAL; + return -1; + } + + if (truncate && e == 0) { + // If the file was opened successfully and truncate was enabled, reduce size to 0 + file->f.SetSize(0); + } + + if (e != 0) { + // Open failed in platform-specific code, errno needs to be converted. + h->DeleteHandle(handle); + SetPosixErrno(e); + return -1; } } file->is_opened = true; return handle; } -int PS4_SYSV_ABI posix_open(const char* path, int flags, /* SceKernelMode*/ u16 mode) { - LOG_INFO(Kernel_Fs, "posix open redirect to sceKernelOpen"); - int result = sceKernelOpen(path, flags, mode); - // Posix calls different only for their return values +s32 PS4_SYSV_ABI posix_open(const char* filename, s32 flags, u16 mode) { + return open(filename, flags, mode); +} + +s32 PS4_SYSV_ABI sceKernelOpen(const char* path, s32 flags, /* SceKernelMode*/ u16 mode) { + s32 result = open(path, flags, mode); if (result < 0) { - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI open(const char* filename, const char* mode) { - LOG_INFO(Kernel_Fs, "open redirect to sceKernelOpen"); - int result = sceKernelOpen(filename, ORBIS_KERNEL_O_RDWR, 0); - if (result < 0) { - return -1; - } - return result; -} - -int PS4_SYSV_ABI sceKernelClose(int d) { - if (d < 3) { // d probably hold an error code - return ORBIS_KERNEL_ERROR_EPERM; +s32 PS4_SYSV_ABI close(s32 fd) { + if (fd < 3) { + // This is technically possible, but it's usually caused by some stubbed function instead. + LOG_WARNING(Kernel_Fs, "called on an std handle, fd = {}", fd); } auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } if (file->type == Core::FileSys::FileType::Regular) { file->f.Close(); @@ -178,63 +206,54 @@ int PS4_SYSV_ABI sceKernelClose(int d) { file->is_opened = false; LOG_INFO(Kernel_Fs, "Closing {}", file->m_guest_name); // FIXME: Lock file mutex before deleting it? - h->DeleteHandle(d); + h->DeleteHandle(fd); return ORBIS_OK; } -int PS4_SYSV_ABI posix_close(int d) { - int result = sceKernelClose(d); +s32 PS4_SYSV_ABI posix_close(s32 fd) { + return close(fd); +} + +s32 PS4_SYSV_ABI sceKernelClose(s32 fd) { + s32 result = close(fd); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_close: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) { +s64 PS4_SYSV_ABI write(s32 fd, const void* buf, size_t nbytes) { auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->write(buf, nbytes); + s64 result = file->device->write(buf, nbytes); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } return file->f.WriteRaw(buf, nbytes); } -int PS4_SYSV_ABI sceKernelUnlink(const char* path) { - if (path == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; +s64 PS4_SYSV_ABI posix_write(s32 fd, const void* buf, size_t nbytes) { + return write(fd, buf, nbytes); +} + +s64 PS4_SYSV_ABI sceKernelWrite(s32 fd, const void* buf, size_t nbytes) { + s64 result = write(fd, buf, nbytes); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } - - auto* h = Common::Singleton::Instance(); - auto* mnt = Common::Singleton::Instance(); - - bool ro = false; - const auto host_path = mnt->GetHostPath(path, &ro); - if (host_path.empty()) { - return ORBIS_KERNEL_ERROR_EACCES; - } - - if (ro) { - return ORBIS_KERNEL_ERROR_EROFS; - } - - if (std::filesystem::is_directory(host_path)) { - return ORBIS_KERNEL_ERROR_EPERM; - } - - auto* file = h->GetFile(host_path); - if (file != nullptr) { - file->f.Unlink(); - } - - LOG_INFO(Kernel_Fs, "Unlinked {}", path); - return ORBIS_OK; + return result; } size_t ReadFile(Common::FS::IOFile& file, void* buf, size_t nbytes) { @@ -246,58 +265,97 @@ size_t ReadFile(Common::FS::IOFile& file, void* buf, size_t nbytes) { return file.ReadRaw(buf, nbytes); } -size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) { +size_t PS4_SYSV_ABI readv(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - int r = file->device->readv(iov, iovcnt); - if (r < 0) { - ErrSceToPosix(r); + size_t result = file->device->readv(iov, iovcnt); + if (result < 0) { + ErrSceToPosix(result); return -1; } - return r; + return result; } size_t total_read = 0; - for (int i = 0; i < iovcnt; i++) { + for (s32 i = 0; i < iovcnt; i++) { total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len); } return total_read; } -size_t PS4_SYSV_ABI _writev(int fd, const SceKernelIovec* iov, int iovcn) { +size_t PS4_SYSV_ABI posix_readv(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { + return readv(fd, iov, iovcnt); +} + +size_t PS4_SYSV_ABI sceKernelReadv(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { + size_t result = readv(fd, iov, iovcnt); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +size_t PS4_SYSV_ABI writev(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->writev(iov, iovcn); + size_t result = file->device->writev(iov, iovcnt); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } size_t total_written = 0; - for (int i = 0; i < iovcn; i++) { + for (s32 i = 0; i < iovcnt; i++) { total_written += file->f.WriteRaw(iov[i].iov_base, iov[i].iov_len); } return total_written; } -s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { +size_t PS4_SYSV_ABI posix_writev(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { + return writev(fd, iov, iovcnt); +} + +size_t PS4_SYSV_ABI sceKernelWritev(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { + size_t result = writev(fd, iov, iovcnt); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s64 PS4_SYSV_ABI posix_lseek(s32 fd, s64 offset, s32 whence) { auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->lseek(offset, whence); + s64 result = file->device->lseek(offset, whence); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } Common::FS::SeekOrigin origin{}; @@ -307,53 +365,82 @@ s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { origin = Common::FS::SeekOrigin::CurrentPosition; } else if (whence == 2) { origin = Common::FS::SeekOrigin::End; + } else if (whence == 3) { + origin = Common::FS::SeekOrigin::SeekHole; + } else if (whence == 4) { + origin = Common::FS::SeekOrigin::SeekData; + } else { + // whence parameter is invalid + *__Error() = POSIX_EINVAL; + return -1; } if (!file->f.Seek(offset, origin)) { - LOG_CRITICAL(Kernel_Fs, "sceKernelLseek: failed to seek"); - return ORBIS_KERNEL_ERROR_EINVAL; + if (errno != 0) { + // Seek failed in platform-specific code, errno needs to be converted. + SetPosixErrno(errno); + return -1; + } + // Shouldn't be possible, but just in case. + return -1; } - return file->f.Tell(); -} -s64 PS4_SYSV_ABI posix_lseek(int d, s64 offset, int whence) { - s64 result = sceKernelLseek(d, offset, whence); + s64 result = file->f.Tell(); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_lseek: error = {}", result); - ErrSceToPosix(result); + // Tell failed in platform-specific code, errno needs to be converted. + SetPosixErrno(errno); return -1; } return result; } -s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) { +s64 PS4_SYSV_ABI sceKernelLseek(s32 fd, s64 offset, s32 whence) { + s64 result = posix_lseek(fd, offset, whence); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s64 PS4_SYSV_ABI read(s32 fd, void* buf, size_t nbytes) { auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->read(buf, nbytes); + s64 result = file->device->read(buf, nbytes); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } return ReadFile(file->f, buf, nbytes); } -int PS4_SYSV_ABI posix_read(int d, void* buf, size_t nbytes) { - int result = sceKernelRead(d, buf, nbytes); +s64 PS4_SYSV_ABI posix_read(s32 fd, void* buf, size_t nbytes) { + return read(fd, buf, nbytes); +} + +s64 PS4_SYSV_ABI sceKernelRead(s32 fd, void* buf, size_t nbytes) { + s64 result = read(fd, buf, nbytes); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_read: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { +s32 PS4_SYSV_ABI posix_mkdir(const char* path, u16 mode) { LOG_INFO(Kernel_Fs, "path = {} mode = {}", path, mode); if (path == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_ENOTDIR; + return -1; } auto* mnt = Common::Singleton::Instance(); @@ -361,88 +448,79 @@ int PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { const auto dir_name = mnt->GetHostPath(path, &ro); if (std::filesystem::exists(dir_name)) { - return ORBIS_KERNEL_ERROR_EEXIST; + *__Error() = POSIX_EEXIST; + return -1; } if (ro) { - return ORBIS_KERNEL_ERROR_EROFS; + *__Error() = POSIX_EROFS; + return -1; } // CUSA02456: path = /aotl after sceSaveDataMount(mode = 1) std::error_code ec; if (dir_name.empty() || !std::filesystem::create_directory(dir_name, ec)) { - return ORBIS_KERNEL_ERROR_EIO; + *__Error() = POSIX_EIO; + return -1; } if (!std::filesystem::exists(dir_name)) { - return ORBIS_KERNEL_ERROR_ENOENT; + *__Error() = POSIX_ENOENT; + return -1; } return ORBIS_OK; } -int PS4_SYSV_ABI posix_mkdir(const char* path, u16 mode) { - int result = sceKernelMkdir(path, mode); +s32 PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { + s32 result = posix_mkdir(path, mode); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_mkdir: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI sceKernelRmdir(const char* path) { +s32 PS4_SYSV_ABI posix_rmdir(const char* path) { auto* mnt = Common::Singleton::Instance(); bool ro = false; const std::filesystem::path dir_name = mnt->GetHostPath(path, &ro); - if (dir_name.empty()) { - LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, permission denied", - fmt::UTF(dir_name.u8string())); - return ORBIS_KERNEL_ERROR_EACCES; + if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) { + *__Error() = POSIX_ENOTDIR; + return -1; } if (ro) { - LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, directory is read only", - fmt::UTF(dir_name.u8string())); - return ORBIS_KERNEL_ERROR_EROFS; - } - - if (!std::filesystem::is_directory(dir_name)) { - LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, path is not a directory", - fmt::UTF(dir_name.u8string())); - return ORBIS_KERNEL_ERROR_ENOTDIR; + *__Error() = POSIX_EROFS; + return -1; } if (!std::filesystem::exists(dir_name)) { - LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, no such file or directory", - fmt::UTF(dir_name.u8string())); - return ORBIS_KERNEL_ERROR_ENOENT; + *__Error() = POSIX_ENOENT; + return -1; } std::error_code ec; - int result = std::filesystem::remove_all(dir_name, ec); + s32 result = std::filesystem::remove_all(dir_name, ec); - if (!ec) { - LOG_INFO(Kernel_Fs, "Removed directory: {}", fmt::UTF(dir_name.u8string())); - return ORBIS_OK; + if (ec) { + *__Error() = POSIX_EIO; + return -1; } - LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, error_code={}", - fmt::UTF(dir_name.u8string()), ec.message()); - return ErrnoToSceKernelError(ec.value()); + return ORBIS_OK; } -int PS4_SYSV_ABI posix_rmdir(const char* path) { - int result = sceKernelRmdir(path); +s32 PS4_SYSV_ABI sceKernelRmdir(const char* path) { + s32 result = posix_rmdir(path); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_rmdir: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { +s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { LOG_INFO(Kernel_Fs, "(PARTIAL) path = {}", path); auto* mnt = Common::Singleton::Instance(); bool ro = false; @@ -451,7 +529,8 @@ int PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { const bool is_dir = std::filesystem::is_directory(path_name); const bool is_file = std::filesystem::is_regular_file(path_name); if (!is_dir && !is_file) { - return ORBIS_KERNEL_ERROR_ENOENT; + *__Error() = POSIX_ENOENT; + return -1; } if (std::filesystem::is_directory(path_name)) { sb->st_mode = 0000777u | 0040000u; @@ -461,7 +540,7 @@ int PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { // TODO incomplete } else { sb->st_mode = 0000777u | 0100000u; - sb->st_size = static_cast(std::filesystem::file_size(path_name)); + sb->st_size = static_cast(std::filesystem::file_size(path_name)); sb->st_blksize = 512; sb->st_blocks = (sb->st_size + 511) / 512; // TODO incomplete @@ -473,17 +552,16 @@ int PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { return ORBIS_OK; } -int PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { - int result = sceKernelStat(path, sb); +s32 PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { + s32 result = posix_stat(path, sb); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_stat: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { +s32 PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { auto* mnt = Common::Singleton::Instance(); std::string_view guest_path{path}; for (const auto& prefix : available_device | std::views::keys) { @@ -498,23 +576,165 @@ int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { return ORBIS_OK; } -s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 offset) { - if (d < 3) { - return ORBIS_KERNEL_ERROR_EPERM; +s32 PS4_SYSV_ABI fstat(s32 fd, OrbisKernelStat* sb) { + LOG_INFO(Kernel_Fs, "(PARTIAL) fd = {}", fd); + if (sb == nullptr) { + *__Error() = POSIX_EFAULT; + return -1; } + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(fd); + if (file == nullptr) { + *__Error() = POSIX_EBADF; + return -1; + } + std::memset(sb, 0, sizeof(OrbisKernelStat)); + + switch (file->type) { + case Core::FileSys::FileType::Device: { + s32 result = file->device->fstat(sb); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; + } + case Core::FileSys::FileType::Regular: { + sb->st_mode = 0000777u | 0100000u; + sb->st_size = file->f.GetSize(); + sb->st_blksize = 512; + sb->st_blocks = (sb->st_size + 511) / 512; + // TODO incomplete + break; + } + case Core::FileSys::FileType::Directory: { + sb->st_mode = 0000777u | 0040000u; + sb->st_size = 0; + sb->st_blksize = 512; + sb->st_blocks = 0; + // TODO incomplete + break; + } + default: + UNREACHABLE(); + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI posix_fstat(s32 fd, OrbisKernelStat* sb) { + return fstat(fd, sb); +} + +s32 PS4_SYSV_ABI sceKernelFstat(s32 fd, OrbisKernelStat* sb) { + s32 result = fstat(fd, sb); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s32 PS4_SYSV_ABI posix_ftruncate(s32 fd, s64 length) { + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(fd); + + if (file == nullptr) { + *__Error() = POSIX_EBADF; + return -1; + } + + if (file->type == Core::FileSys::FileType::Device) { + s32 result = file->device->ftruncate(length); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; + } + + if (file->m_host_name.empty()) { + *__Error() = POSIX_EACCES; + return -1; + } + + file->f.SetSize(length); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceKernelFtruncate(s32 fd, s64 length) { + s32 result = posix_ftruncate(fd, length); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s32 PS4_SYSV_ABI posix_rename(const char* from, const char* to) { + auto* mnt = Common::Singleton::Instance(); + bool ro = false; + const auto src_path = mnt->GetHostPath(from, &ro); + if (!std::filesystem::exists(src_path)) { + *__Error() = POSIX_ENOENT; + return -1; + } + if (ro) { + *__Error() = POSIX_EROFS; + return -1; + } + const auto dst_path = mnt->GetHostPath(to, &ro); + if (ro) { + *__Error() = POSIX_EROFS; + return -1; + } + const bool src_is_dir = std::filesystem::is_directory(src_path); + const bool dst_is_dir = std::filesystem::is_directory(dst_path); + if (src_is_dir && !dst_is_dir) { + *__Error() = POSIX_ENOTDIR; + return -1; + } + if (!src_is_dir && dst_is_dir) { + *__Error() = POSIX_EISDIR; + return -1; + } + if (dst_is_dir && !std::filesystem::is_empty(dst_path)) { + *__Error() = POSIX_ENOTEMPTY; + return -1; + } + std::filesystem::copy(src_path, dst_path, std::filesystem::copy_options::overwrite_existing); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) { + s32 result = posix_rename(from, to); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s64 PS4_SYSV_ABI posix_preadv(s32 fd, SceKernelIovec* iov, s32 iovcnt, s64 offset) { if (offset < 0) { - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_EINVAL; + return -1; } auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->preadv(iov, iovcnt, offset); + s64 result = file->device->preadv(iov, iovcnt, offset); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } const s64 pos = file->f.Tell(); @@ -522,8 +742,8 @@ s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 off file->f.Seek(pos); }; if (!file->f.Seek(offset)) { - LOG_CRITICAL(Kernel_Fs, "failed to seek"); - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_EIO; + return -1; } size_t total_read = 0; for (int i = 0; i < iovcnt; i++) { @@ -532,118 +752,72 @@ s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 off return total_read; } -s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset) { - SceKernelIovec iovec{buf, nbytes}; - return sceKernelPreadv(d, &iovec, 1, offset); -} - -int PS4_SYSV_ABI sceKernelFStat(int fd, OrbisKernelStat* sb) { - LOG_INFO(Kernel_Fs, "(PARTIAL) fd = {}", fd); - if (fd < 3) { - return ORBIS_KERNEL_ERROR_EPERM; - } - if (sb == nullptr) { - return ORBIS_KERNEL_ERROR_EFAULT; - } - auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(fd); - if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; - } - std::memset(sb, 0, sizeof(OrbisKernelStat)); - - switch (file->type) { - case Core::FileSys::FileType::Device: - return file->device->fstat(sb); - case Core::FileSys::FileType::Regular: - sb->st_mode = 0000777u | 0100000u; - sb->st_size = file->f.GetSize(); - sb->st_blksize = 512; - sb->st_blocks = (sb->st_size + 511) / 512; - // TODO incomplete - break; - case Core::FileSys::FileType::Directory: - sb->st_mode = 0000777u | 0040000u; - sb->st_size = 0; - sb->st_blksize = 512; - sb->st_blocks = 0; - // TODO incomplete - break; - default: - UNREACHABLE(); - } - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_fstat(int fd, OrbisKernelStat* sb) { - int result = sceKernelFStat(fd, sb); +s64 PS4_SYSV_ABI sceKernelPreadv(s32 fd, SceKernelIovec* iov, s32 iovcnt, s64 offset) { + s64 result = posix_preadv(fd, iov, iovcnt, offset); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_fstat: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -s32 PS4_SYSV_ABI sceKernelFsync(int fd) { +s64 PS4_SYSV_ABI posix_pread(s32 fd, void* buf, size_t nbytes, s64 offset) { + SceKernelIovec iovec{buf, nbytes}; + return posix_preadv(fd, &iovec, 1, offset); +} + +s64 PS4_SYSV_ABI sceKernelPread(s32 fd, void* buf, size_t nbytes, s64 offset) { + SceKernelIovec iovec{buf, nbytes}; + return sceKernelPreadv(fd, &iovec, 1, offset); +} + +s32 PS4_SYSV_ABI posix_fsync(s32 fd) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } if (file->type == Core::FileSys::FileType::Device) { - return file->device->fsync(); + s32 result = file->device->fsync(); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } file->f.Flush(); return ORBIS_OK; } -s32 PS4_SYSV_ABI posix_fsync(int fd) { - s32 result = sceKernelFsync(fd); +s32 PS4_SYSV_ABI sceKernelFsync(s32 fd) { + s32 result = posix_fsync(fd); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_fsync: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI sceKernelFtruncate(int fd, s64 length) { - auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(fd); - - if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; - } - - if (file->type == Core::FileSys::FileType::Device) { - return file->device->ftruncate(length); - } - - if (file->m_host_name.empty()) { - return ORBIS_KERNEL_ERROR_EACCES; - } - - file->f.SetSize(length); - return ORBIS_OK; -} - -static int GetDents(int fd, char* buf, int nbytes, s64* basep) { - if (fd < 3) { - return ORBIS_KERNEL_ERROR_EBADF; - } - +static s32 GetDents(s32 fd, char* buf, s32 nbytes, s64* basep) { if (buf == nullptr) { - return ORBIS_KERNEL_ERROR_EFAULT; + *__Error() = POSIX_EFAULT; + return -1; } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } if (file->type == Core::FileSys::FileType::Device) { - return file->device->getdents(buf, nbytes, basep); + s32 result = file->device->getdents(buf, nbytes, basep); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } if (file->dirents_index == file->dirents.size()) { @@ -651,7 +825,8 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { } if (file->type != Core::FileSys::FileType::Directory || nbytes < 512 || file->dirents_index > file->dirents.size()) { - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_EINVAL; + return -1; } const auto& entry = file->dirents.at(file->dirents_index++); auto str = entry.name; @@ -672,118 +847,178 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { return sizeof(OrbisKernelDirent); } -int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) { +s32 PS4_SYSV_ABI posix_getdents(s32 fd, char* buf, s32 nbytes) { return GetDents(fd, buf, nbytes, nullptr); } -int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) { +s32 PS4_SYSV_ABI sceKernelGetdents(s32 fd, char* buf, s32 nbytes) { + s32 result = GetDents(fd, buf, nbytes, nullptr); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s32 PS4_SYSV_ABI getdirentries(s32 fd, char* buf, s32 nbytes, s64* basep) { return GetDents(fd, buf, nbytes, basep); } -s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) { - if (d < 3) { - return ORBIS_KERNEL_ERROR_EPERM; +s32 PS4_SYSV_ABI posix_getdirentries(s32 fd, char* buf, s32 nbytes, s64* basep) { + return GetDents(fd, buf, nbytes, basep); +} + +s32 PS4_SYSV_ABI sceKernelGetdirentries(s32 fd, char* buf, s32 nbytes, s64* basep) { + s32 result = GetDents(fd, buf, nbytes, basep); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } + return result; +} + +s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { if (offset < 0) { - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_EINVAL; + return -1; } auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->pwrite(buf, nbytes, offset); + s64 result = file->device->pwrite(buf, nbytes, offset); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } const s64 pos = file->f.Tell(); SCOPE_EXIT { file->f.Seek(pos); }; if (!file->f.Seek(offset)) { - LOG_CRITICAL(Kernel_Fs, "sceKernelPwrite: failed to seek"); - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_EIO; + return -1; } return file->f.WriteRaw(buf, nbytes); } -s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) { +s64 PS4_SYSV_ABI sceKernelPwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { + s64 result = posix_pwrite(fd, buf, nbytes, offset); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s32 PS4_SYSV_ABI posix_unlink(const char* path) { + if (path == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + + auto* h = Common::Singleton::Instance(); auto* mnt = Common::Singleton::Instance(); + bool ro = false; - const auto src_path = mnt->GetHostPath(from, &ro); - if (!std::filesystem::exists(src_path)) { - return ORBIS_KERNEL_ERROR_ENOENT; + const auto host_path = mnt->GetHostPath(path, &ro); + if (host_path.empty()) { + *__Error() = POSIX_ENOENT; + return -1; } + if (ro) { - return ORBIS_KERNEL_ERROR_EROFS; + *__Error() = POSIX_EROFS; + return -1; } - const auto dst_path = mnt->GetHostPath(to, &ro); - if (ro) { - return ORBIS_KERNEL_ERROR_EROFS; + + if (std::filesystem::is_directory(host_path)) { + *__Error() = POSIX_EPERM; + return -1; } - const bool src_is_dir = std::filesystem::is_directory(src_path); - const bool dst_is_dir = std::filesystem::is_directory(dst_path); - if (src_is_dir && !dst_is_dir) { - return ORBIS_KERNEL_ERROR_ENOTDIR; + + auto* file = h->GetFile(host_path); + if (file == nullptr) { + // File to unlink hasn't been opened, manually open and unlink it. + Common::FS::IOFile file(host_path, Common::FS::FileAccessMode::ReadWrite); + file.Unlink(); + } else { + file->f.Unlink(); } - if (!src_is_dir && dst_is_dir) { - return ORBIS_KERNEL_ERROR_EISDIR; - } - if (dst_is_dir && !std::filesystem::is_empty(dst_path)) { - return ORBIS_KERNEL_ERROR_ENOTEMPTY; - } - std::filesystem::copy(src_path, dst_path, std::filesystem::copy_options::overwrite_existing); + + LOG_INFO(Kernel_Fs, "Unlinked {}", path); return ORBIS_OK; } -void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen); - LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open); - LIB_FUNCTION("wuCroIGjt2g", "libkernel", 1, "libkernel", 1, 1, open); - LIB_FUNCTION("UK2Tl2DWUns", "libkernel", 1, "libkernel", 1, 1, sceKernelClose); - LIB_FUNCTION("bY-PO6JhzhQ", "libkernel", 1, "libkernel", 1, 1, posix_close); - LIB_FUNCTION("bY-PO6JhzhQ", "libScePosix", 1, "libkernel", 1, 1, posix_close); - LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite); +s32 PS4_SYSV_ABI sceKernelUnlink(const char* path) { + s32 result = posix_unlink(path); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} - LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, _readv); - LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev); - LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek); +void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("6c3rCVE-fTU", "libkernel", 1, "libkernel", 1, 1, open); + LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open); + LIB_FUNCTION("wuCroIGjt2g", "libkernel", 1, "libkernel", 1, 1, posix_open); + LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen); + LIB_FUNCTION("NNtFaKJbPt0", "libkernel", 1, "libkernel", 1, 1, close); + LIB_FUNCTION("bY-PO6JhzhQ", "libScePosix", 1, "libkernel", 1, 1, posix_close); + LIB_FUNCTION("bY-PO6JhzhQ", "libkernel", 1, "libkernel", 1, 1, posix_close); + LIB_FUNCTION("UK2Tl2DWUns", "libkernel", 1, "libkernel", 1, 1, sceKernelClose); + LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, write); + LIB_FUNCTION("FN4gaPmuFV8", "libScePosix", 1, "libkernel", 1, 1, posix_write); + LIB_FUNCTION("FN4gaPmuFV8", "libkernel", 1, "libkernel", 1, 1, posix_write); + LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite); + LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, readv); + LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, writev); LIB_FUNCTION("Oy6IpwgtYOk", "libScePosix", 1, "libkernel", 1, 1, posix_lseek); + LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek); LIB_FUNCTION("oib76F-12fk", "libkernel", 1, "libkernel", 1, 1, sceKernelLseek); - LIB_FUNCTION("Cg4srZ6TKbU", "libkernel", 1, "libkernel", 1, 1, sceKernelRead); + LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, read); LIB_FUNCTION("AqBioC2vF3I", "libScePosix", 1, "libkernel", 1, 1, posix_read); - LIB_FUNCTION("1-LFLmRFxxM", "libkernel", 1, "libkernel", 1, 1, sceKernelMkdir); + LIB_FUNCTION("AqBioC2vF3I", "libkernel", 1, "libkernel", 1, 1, posix_read); + LIB_FUNCTION("Cg4srZ6TKbU", "libkernel", 1, "libkernel", 1, 1, sceKernelRead); LIB_FUNCTION("JGMio+21L4c", "libScePosix", 1, "libkernel", 1, 1, posix_mkdir); LIB_FUNCTION("JGMio+21L4c", "libkernel", 1, "libkernel", 1, 1, posix_mkdir); - LIB_FUNCTION("naInUjYt3so", "libkernel", 1, "libkernel", 1, 1, sceKernelRmdir); + LIB_FUNCTION("1-LFLmRFxxM", "libkernel", 1, "libkernel", 1, 1, sceKernelMkdir); LIB_FUNCTION("c7ZnT7V1B98", "libScePosix", 1, "libkernel", 1, 1, posix_rmdir); LIB_FUNCTION("c7ZnT7V1B98", "libkernel", 1, "libkernel", 1, 1, posix_rmdir); - LIB_FUNCTION("eV9wAD2riIA", "libkernel", 1, "libkernel", 1, 1, sceKernelStat); - LIB_FUNCTION("kBwCPsYX-m4", "libkernel", 1, "libkernel", 1, 1, sceKernelFStat); - LIB_FUNCTION("mqQMh1zPPT8", "libScePosix", 1, "libkernel", 1, 1, posix_fstat); - LIB_FUNCTION("mqQMh1zPPT8", "libkernel", 1, "libkernel", 1, 1, posix_fstat); - LIB_FUNCTION("VW3TVZiM4-E", "libkernel", 1, "libkernel", 1, 1, sceKernelFtruncate); - LIB_FUNCTION("52NcYU9+lEo", "libkernel", 1, "libkernel", 1, 1, sceKernelRename); - + LIB_FUNCTION("naInUjYt3so", "libkernel", 1, "libkernel", 1, 1, sceKernelRmdir); LIB_FUNCTION("E6ao34wPw+U", "libScePosix", 1, "libkernel", 1, 1, posix_stat); LIB_FUNCTION("E6ao34wPw+U", "libkernel", 1, "libkernel", 1, 1, posix_stat); - LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread); - LIB_FUNCTION("yTj62I7kw4s", "libkernel", 1, "libkernel", 1, 1, sceKernelPreadv); + LIB_FUNCTION("eV9wAD2riIA", "libkernel", 1, "libkernel", 1, 1, sceKernelStat); LIB_FUNCTION("uWyW3v98sU4", "libkernel", 1, "libkernel", 1, 1, sceKernelCheckReachability); - LIB_FUNCTION("fTx66l5iWIA", "libkernel", 1, "libkernel", 1, 1, sceKernelFsync); - LIB_FUNCTION("juWbTNM+8hw", "libkernel", 1, "libkernel", 1, 1, posix_fsync); + LIB_FUNCTION("mqQMh1zPPT8", "libScePosix", 1, "libkernel", 1, 1, posix_fstat); + LIB_FUNCTION("mqQMh1zPPT8", "libkernel", 1, "libkernel", 1, 1, posix_fstat); + LIB_FUNCTION("kBwCPsYX-m4", "libkernel", 1, "libkernel", 1, 1, sceKernelFstat); + LIB_FUNCTION("ih4CD9-gghM", "libkernel", 1, "libkernel", 1, 1, posix_ftruncate); + LIB_FUNCTION("VW3TVZiM4-E", "libkernel", 1, "libkernel", 1, 1, sceKernelFtruncate); + LIB_FUNCTION("52NcYU9+lEo", "libkernel", 1, "libkernel", 1, 1, sceKernelRename); + LIB_FUNCTION("yTj62I7kw4s", "libkernel", 1, "libkernel", 1, 1, sceKernelPreadv); + LIB_FUNCTION("ezv-RSBNKqI", "libScePosix", 1, "libkernel", 1, 1, posix_pread); + LIB_FUNCTION("ezv-RSBNKqI", "libkernel", 1, "libkernel", 1, 1, posix_pread); + LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread); LIB_FUNCTION("juWbTNM+8hw", "libScePosix", 1, "libkernel", 1, 1, posix_fsync); + LIB_FUNCTION("juWbTNM+8hw", "libkernel", 1, "libkernel", 1, 1, posix_fsync); + LIB_FUNCTION("fTx66l5iWIA", "libkernel", 1, "libkernel", 1, 1, sceKernelFsync); LIB_FUNCTION("j2AIqSqJP0w", "libkernel", 1, "libkernel", 1, 1, sceKernelGetdents); + LIB_FUNCTION("sfKygSjIbI8", "libkernel", 1, "libkernel", 1, 1, getdirentries); LIB_FUNCTION("taRWhTJFTgE", "libkernel", 1, "libkernel", 1, 1, sceKernelGetdirentries); + LIB_FUNCTION("C2kJ-byS5rM", "libkernel", 1, "libkernel", 1, 1, posix_pwrite); LIB_FUNCTION("nKWi-N2HBV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPwrite); LIB_FUNCTION("AUXVxWeJU-A", "libkernel", 1, "libkernel", 1, 1, sceKernelUnlink); - - // openOrbis (to check if it is valid out of OpenOrbis - LIB_FUNCTION("6c3rCVE-fTU", "libkernel", 1, "libkernel", 1, 1, - posix_open); // _open should be equal to open function } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/file_system.h b/src/core/libraries/kernel/file_system.h index 1838df2fe..77ce3ec3d 100644 --- a/src/core/libraries/kernel/file_system.h +++ b/src/core/libraries/kernel/file_system.h @@ -65,10 +65,10 @@ constexpr int ORBIS_KERNEL_O_DSYNC = 0x1000; constexpr int ORBIS_KERNEL_O_DIRECT = 0x00010000; constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000; -s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes); -s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes); -s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset); -s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset); +s64 PS4_SYSV_ABI sceKernelWrite(s32 fd, const void* buf, size_t nbytes); +s64 PS4_SYSV_ABI sceKernelRead(s32 fd, void* buf, size_t nbytes); +s64 PS4_SYSV_ABI sceKernelPread(s32 fd, void* buf, size_t nbytes, s64 offset); +s64 PS4_SYSV_ABI sceKernelPwrite(s32 fd, void* buf, size_t nbytes, s64 offset); void RegisterFileSystem(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 2b7735219..9227cf45a 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -85,17 +85,23 @@ int ErrnoToSceKernelError(int error) { } void SetPosixErrno(int e) { - // Some error numbers are different between supported OSes or the PS4 + // Some error numbers are different between supported OSes switch (e) { case EPERM: g_posix_errno = POSIX_EPERM; break; - case EAGAIN: - g_posix_errno = POSIX_EAGAIN; + case ENOENT: + g_posix_errno = POSIX_ENOENT; + break; + case EDEADLK: + g_posix_errno = POSIX_EDEADLK; break; case ENOMEM: g_posix_errno = POSIX_ENOMEM; break; + case EACCES: + g_posix_errno = POSIX_EACCES; + break; case EINVAL: g_posix_errno = POSIX_EINVAL; break; @@ -105,13 +111,14 @@ void SetPosixErrno(int e) { case ERANGE: g_posix_errno = POSIX_ERANGE; break; - case EDEADLK: - g_posix_errno = POSIX_EDEADLK; + case EAGAIN: + g_posix_errno = POSIX_EAGAIN; break; case ETIMEDOUT: g_posix_errno = POSIX_ETIMEDOUT; break; default: + LOG_WARNING(Kernel, "Unhandled errno {}", e); g_posix_errno = e; } } @@ -133,14 +140,6 @@ void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) { info->getSegmentInfo = 0; } -s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) { - return sceKernelWrite(d, buf, nbytes); -} - -s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) { - return sceKernelRead(d, buf, nbytes); -} - struct OrbisKernelUuid { u32 timeLow; u16 timeMid; @@ -229,13 +228,10 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate); LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail); LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error); - LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read); LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize); LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1, sceLibcHeapGetTraceInfo); - LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write); - LIB_FUNCTION("FN4gaPmuFV8", "libScePosix", 1, "libkernel", 1, 1, ps4__write); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h index 8e7f475ad..58911727d 100644 --- a/src/core/libraries/kernel/kernel.h +++ b/src/core/libraries/kernel/kernel.h @@ -5,6 +5,7 @@ #include #include +#include "common/string_literal.h" #include "common/types.h" #include "core/libraries/kernel/orbis_error.h" @@ -18,15 +19,6 @@ void ErrSceToPosix(int result); int ErrnoToSceKernelError(int e); void SetPosixErrno(int e); -template -struct StringLiteral { - constexpr StringLiteral(const char (&str)[N]) { - std::copy_n(str, N, value); - } - - char value[N]; -}; - template struct WrapperImpl; diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index 82c5115f1..7b3ac5646 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -79,6 +79,9 @@ s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, } s32 PS4_SYSV_ABI sceKernelCheckedReleaseDirectMemory(u64 start, size_t len) { + if (len == 0) { + return ORBIS_OK; + } LOG_INFO(Kernel_Vmm, "called start = {:#x}, len = {:#x}", start, len); auto* memory = Core::Memory::Instance(); memory->Free(start, len); @@ -86,6 +89,9 @@ s32 PS4_SYSV_ABI sceKernelCheckedReleaseDirectMemory(u64 start, size_t len) { } s32 PS4_SYSV_ABI sceKernelReleaseDirectMemory(u64 start, size_t len) { + if (len == 0) { + return ORBIS_OK; + } auto* memory = Core::Memory::Instance(); memory->Free(start, len); return ORBIS_OK; @@ -507,7 +513,7 @@ s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) { int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) { LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len); if (len == 0) { - return ORBIS_OK; + return ORBIS_KERNEL_ERROR_EINVAL; } auto* memory = Core::Memory::Instance(); return memory->UnmapMemory(std::bit_cast(addr), len); diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 58628867a..d61ee37ac 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -32,44 +32,44 @@ void* PS4_SYSV_ABI sceKernelGetProcParam() { return linker->GetProcParam(); } -s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t args, const void* argp, +s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, u64 args, const void* argp, u32 flags, const void* pOpt, int* pRes) { LOG_INFO(Lib_Kernel, "called filename = {}, args = {}", moduleFileName, args); - - if (flags != 0) { - return ORBIS_KERNEL_ERROR_EINVAL; - } + ASSERT(flags == 0); auto* mnt = Common::Singleton::Instance(); - const auto path = mnt->GetHostPath(moduleFileName); - - // Load PRX module and relocate any modules that import it. auto* linker = Common::Singleton::Instance(); - u32 handle = linker->FindByName(path); - if (handle != -1) { - return handle; - } - handle = linker->LoadModule(path, true); - if (handle == -1) { - return ORBIS_KERNEL_ERROR_ESRCH; - } - auto* module = linker->GetModule(handle); - linker->RelocateAnyImports(module); - // If the new module has a TLS image, trigger its load when TlsGetAddr is called. - if (module->tls.image_size != 0) { - linker->AdvanceGenerationCounter(); + std::filesystem::path path; + std::string guest_path(moduleFileName); + + s32 handle = -1; + + if (guest_path[0] == '/') { + // try load /system/common/lib/ +path + // try load /system/priv/lib/ +path + path = mnt->GetHostPath(guest_path); + handle = linker->LoadAndStartModule(path, args, argp, pRes); + if (handle != -1) + return handle; + } else { + if (!guest_path.contains('/')) { + path = mnt->GetHostPath("/app0/" + guest_path); + handle = linker->LoadAndStartModule(path, args, argp, pRes); + if (handle != -1) + return handle; + // if ((flags & 0x10000) != 0) + // try load /system/priv/lib/ +basename + // try load /system/common/lib/ +basename + } else { + path = mnt->GetHostPath(guest_path); + handle = linker->LoadAndStartModule(path, args, argp, pRes); + if (handle != -1) + return handle; + } } - // Retrieve and verify proc param according to libkernel. - u64* param = module->GetProcParam(); - ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]); - s32 ret = module->Start(args, argp, param); - if (pRes) { - *pRes = ret; - } - - return handle; + return ORBIS_KERNEL_ERROR_ENOENT; } s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) { @@ -85,19 +85,7 @@ s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) { return ORBIS_OK; } -static constexpr size_t ORBIS_DBG_MAX_NAME_LENGTH = 256; - -struct OrbisModuleInfoForUnwind { - u64 st_size; - std::array name; - VAddr eh_frame_hdr_addr; - VAddr eh_frame_addr; - u64 eh_frame_size; - VAddr seg0_addr; - u64 seg0_size; -}; - -s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags, +s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, s32 flags, OrbisModuleInfoForUnwind* info) { if (flags >= 3) { std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind)); diff --git a/src/core/libraries/kernel/process.h b/src/core/libraries/kernel/process.h index 0340a9793..09e4276fb 100644 --- a/src/core/libraries/kernel/process.h +++ b/src/core/libraries/kernel/process.h @@ -11,10 +11,25 @@ class SymbolsResolver; namespace Libraries::Kernel { +static constexpr size_t ORBIS_DBG_MAX_NAME_LENGTH = 256; + +struct OrbisModuleInfoForUnwind { + u64 st_size; + std::array name; + VAddr eh_frame_hdr_addr; + VAddr eh_frame_addr; + u64 eh_frame_size; + VAddr seg0_addr; + u64 seg0_size; +}; + int PS4_SYSV_ABI sceKernelIsNeoMode(); int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver); +s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, s32 flags, + OrbisModuleInfoForUnwind* info); + void RegisterProcess(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp index 2565b8078..b7e4c1756 100644 --- a/src/core/libraries/kernel/time.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -5,6 +5,7 @@ #include "common/assert.h" #include "common/native_clock.h" +#include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/time.h" #include "core/libraries/libs.h" @@ -19,6 +20,7 @@ #if __APPLE__ #include #endif +#include #include #include #include @@ -93,44 +95,189 @@ u32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) { return 0; } -int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) { - if (tp == nullptr) { +#ifdef _WIN64 +#ifndef CLOCK_REALTIME +#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(&ft); +} + +static s32 clock_gettime(u32 clock_id, struct timespec* ts) { + switch (clock_id) { + case CLOCK_REALTIME: + case CLOCK_REALTIME_COARSE: { + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + const u64 ns = FileTimeTo100Ns(ft) - DELTA_EPOCH_IN_100NS; + ts->tv_sec = ns / 10'000'000; + ts->tv_nsec = (ns % 10'000'000) * 100; + return 0; + } + case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_COARSE: { + static LARGE_INTEGER pf = [] { + LARGE_INTEGER res{}; + QueryPerformanceFrequency(&pf); + return res; + }(); + + LARGE_INTEGER pc{}; + QueryPerformanceCounter(&pc); + ts->tv_sec = pc.QuadPart / pf.QuadPart; + ts->tv_nsec = ((pc.QuadPart % pf.QuadPart) * 1000'000'000) / pf.QuadPart; + return 0; + } + case CLOCK_PROCESS_CPUTIME_ID: { + FILETIME ct, et, kt, ut; + if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) { + return EFAULT; + } + const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt); + ts->tv_sec = ns / 10'000'000; + ts->tv_nsec = (ns % 10'000'000) * 100; + return 0; + } + case CLOCK_THREAD_CPUTIME_ID: { + FILETIME ct, et, kt, ut; + if (!GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut)) { + return EFAULT; + } + const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt); + ts->tv_sec = ns / 10'000'000; + ts->tv_nsec = (ns % 10'000'000) * 100; + return 0; + } + default: + return EINVAL; + } +} +#endif + +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_REALTIME; + + clockid_t pclock_id = CLOCK_MONOTONIC; 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_REALTIME_FAST: +#ifndef __APPLE__ + 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: - case ORBIS_CLOCK_MONOTONIC_FAST: pclock_id = CLOCK_MONOTONIC; break; - default: - LOG_ERROR(Lib_Kernel, "unsupported = {} using CLOCK_REALTIME", clock_id); + case ORBIS_CLOCK_UPTIME_FAST: + case ORBIS_CLOCK_MONOTONIC_FAST: +#ifndef __APPLE__ + pclock_id = CLOCK_MONOTONIC_COARSE; +#else + pclock_id = CLOCK_MONOTONIC; +#endif break; + case ORBIS_CLOCK_THREAD_CPUTIME_ID: + pclock_id = CLOCK_THREAD_CPUTIME_ID; + 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: { +#ifdef _WIN64 + 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); + if (res < 0) { + return res; + } + ts->tv_sec = ru.ru_utime.tv_sec; + ts->tv_nsec = ru.ru_utime.tv_usec * 1000; +#endif + return 0; + } + case ORBIS_CLOCK_PROF: { +#ifdef _WIN64 + 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); + if (res < 0) { + return res; + } + ts->tv_sec = ru.ru_stime.tv_sec; + ts->tv_nsec = ru.ru_stime.tv_usec * 1000; +#endif + 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: + return EINVAL; } timespec t{}; int result = clock_gettime(pclock_id, &t); - tp->tv_sec = t.tv_sec; - tp->tv_nsec = t.tv_nsec; - if (result == 0) { - return ORBIS_OK; - } - return ORBIS_KERNEL_ERROR_EINVAL; + ts->tv_sec = t.tv_sec; + ts->tv_nsec = t.tv_nsec; + return result; } -int PS4_SYSV_ABI posix_clock_gettime(s32 clock_id, OrbisKernelTimespec* time) { - int result = sceKernelClockGettime(clock_id, time); - if (result < 0) { - UNREACHABLE(); // TODO return posix error code +int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) { + const auto res = orbis_clock_gettime(clock_id, tp); + if (res < 0) { + return ErrnoToSceKernelError(res); } - return result; + return ORBIS_OK; } int PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) { @@ -316,11 +463,11 @@ void RegisterTime(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep); LIB_FUNCTION("QBi7HCK03hw", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGettime); LIB_FUNCTION("kOcnerypnQA", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimezone); - LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, posix_clock_gettime); - LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime); + 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("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime); } -} // namespace Libraries::Kernel +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 074cf524e..cd0fe650b 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -115,6 +115,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::NpParty::RegisterlibSceNpParty(sym); Libraries::Zlib::RegisterlibSceZlib(sym); Libraries::Hmd::RegisterlibSceHmd(sym); + Libraries::DiscMap::RegisterlibSceDiscMap(sym); } } // namespace Libraries diff --git a/src/core/libraries/network/http.cpp b/src/core/libraries/network/http.cpp index 8e06c76fa..dbb5b096a 100644 --- a/src/core/libraries/network/http.cpp +++ b/src/core/libraries/network/http.cpp @@ -5,6 +5,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/network/http.h" +#include "http_error.h" namespace Libraries::Http { @@ -566,17 +567,277 @@ int PS4_SYSV_ABI sceHttpUriMerge() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriParse() { +int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool, + size_t* require, size_t prepare) { + LOG_INFO(Lib_Http, "srcUri = {}", std::string(srcUri)); + if (!srcUri) { + LOG_ERROR(Lib_Http, "invalid url"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + if (!out && !pool && !require) { + LOG_ERROR(Lib_Http, "invalid values"); + return ORBIS_HTTP_ERROR_INVALID_VALUE; + } + + if (out && pool) { + memset(out, 0, sizeof(OrbisHttpUriElement)); + out->scheme = (char*)pool; + } + + // Track the total required buffer size + size_t requiredSize = 0; + + // Parse the scheme (e.g., "http:", "https:", "file:") + size_t schemeLength = 0; + while (srcUri[schemeLength] && srcUri[schemeLength] != ':') { + if (!isalnum(srcUri[schemeLength])) { + LOG_ERROR(Lib_Http, "invalid url"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + schemeLength++; + } + + if (pool && prepare < schemeLength + 1) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy(out->scheme, srcUri, schemeLength); + out->scheme[schemeLength] = '\0'; + } + + requiredSize += schemeLength + 1; + + // Move past the scheme and ':' character + size_t offset = schemeLength + 1; + + // Check if "//" appears after the scheme + if (strncmp(srcUri + offset, "//", 2) == 0) { + // "//" is present + if (out) { + out->opaque = false; + } + offset += 2; // Move past "//" + } else { + // "//" is not present + if (out) { + out->opaque = true; + } + } + + // Handle "file" scheme + if (strncmp(srcUri, "file", 4) == 0) { + // File URIs typically start with "file://" + if (out && !out->opaque) { + // Skip additional slashes (e.g., "////") + while (srcUri[offset] == '/') { + offset++; + } + + // Parse the path (everything after the slashes) + char* pathStart = (char*)srcUri + offset; + size_t pathLength = 0; + while (pathStart[pathLength] && pathStart[pathLength] != '?' && + pathStart[pathLength] != '#') { + pathLength++; + } + + // Ensure the path starts with '/' + if (pathLength > 0 && pathStart[0] != '/') { + // Prepend '/' to the path + requiredSize += pathLength + 2; // Include '/' and null terminator + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + out->path = (char*)pool + (requiredSize - pathLength - 2); + out->path[0] = '/'; // Add leading '/' + memcpy(out->path + 1, pathStart, pathLength); + out->path[pathLength + 1] = '\0'; + } + } else { + // Path already starts with '/' + requiredSize += pathLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy((char*)pool + (requiredSize - pathLength - 1), pathStart, pathLength); + out->path = (char*)pool + (requiredSize - pathLength - 1); + out->path[pathLength] = '\0'; + } + } + + // Move past the path + offset += pathLength; + } + } + + // Handle non-file schemes (e.g., "http", "https") + else { + // Parse the host and port + char* hostStart = (char*)srcUri + offset; + while (*hostStart == '/') { + hostStart++; + } + + size_t hostLength = 0; + while (hostStart[hostLength] && hostStart[hostLength] != '/' && + hostStart[hostLength] != '?' && hostStart[hostLength] != ':') { + hostLength++; + } + + requiredSize += hostLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy((char*)pool + (requiredSize - hostLength - 1), hostStart, hostLength); + out->hostname = (char*)pool + (requiredSize - hostLength - 1); + out->hostname[hostLength] = '\0'; + } + + // Move past the host + offset += hostLength; + + // Parse the port (if present) + if (hostStart[hostLength] == ':') { + char* portStart = hostStart + hostLength + 1; + size_t portLength = 0; + while (portStart[portLength] && isdigit(portStart[portLength])) { + portLength++; + } + + requiredSize += portLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + // Convert the port string to a uint16_t + char portStr[6]; // Max length for a port number (65535) + if (portLength > 5) { + LOG_ERROR(Lib_Http, "invalid url"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + memcpy(portStr, portStart, portLength); + portStr[portLength] = '\0'; + + uint16_t port = (uint16_t)atoi(portStr); + if (port == 0 && portStr[0] != '0') { + LOG_ERROR(Lib_Http, "invalid url"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + + // Set the port in the output structure + if (out) { + out->port = port; + } + + // Move past the port + offset += portLength + 1; + } + } + + // Parse the path (if present) + if (srcUri[offset] == '/') { + char* pathStart = (char*)srcUri + offset; + size_t pathLength = 0; + while (pathStart[pathLength] && pathStart[pathLength] != '?' && + pathStart[pathLength] != '#') { + pathLength++; + } + + requiredSize += pathLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy((char*)pool + (requiredSize - pathLength - 1), pathStart, pathLength); + out->path = (char*)pool + (requiredSize - pathLength - 1); + out->path[pathLength] = '\0'; + } + + // Move past the path + offset += pathLength; + } + + // Parse the query (if present) + if (srcUri[offset] == '?') { + char* queryStart = (char*)srcUri + offset + 1; + size_t queryLength = 0; + while (queryStart[queryLength] && queryStart[queryLength] != '#') { + queryLength++; + } + + requiredSize += queryLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy((char*)pool + (requiredSize - queryLength - 1), queryStart, queryLength); + out->query = (char*)pool + (requiredSize - queryLength - 1); + out->query[queryLength] = '\0'; + } + + // Move past the query + offset += queryLength + 1; + } + + // Parse the fragment (if present) + if (srcUri[offset] == '#') { + char* fragmentStart = (char*)srcUri + offset + 1; + size_t fragmentLength = 0; + while (fragmentStart[fragmentLength]) { + fragmentLength++; + } + + requiredSize += fragmentLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy((char*)pool + (requiredSize - fragmentLength - 1), fragmentStart, + fragmentLength); + out->fragment = (char*)pool + (requiredSize - fragmentLength - 1); + out->fragment[fragmentLength] = '\0'; + } + } + + // Calculate the total required buffer size + if (require) { + *require = requiredSize; // Update with actual required size + } + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriSweepPath() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttpUriUnescape() { +int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/network/http.h b/src/core/libraries/network/http.h index 24bc83020..c687c60c4 100644 --- a/src/core/libraries/network/http.h +++ b/src/core/libraries/network/http.h @@ -11,6 +11,19 @@ class SymbolsResolver; namespace Libraries::Http { +struct OrbisHttpUriElement { + bool opaque; + char* scheme; + char* username; + char* password; + char* hostname; + char* path; + char* query; + char* fragment; + u16 port; + u8 reserved[10]; +}; + int PS4_SYSV_ABI sceHttpAbortRequest(); int PS4_SYSV_ABI sceHttpAbortRequestForce(); int PS4_SYSV_ABI sceHttpAbortWaitRequest(); @@ -122,9 +135,10 @@ int PS4_SYSV_ABI sceHttpUriBuild(); int PS4_SYSV_ABI sceHttpUriCopy(); int PS4_SYSV_ABI sceHttpUriEscape(); int PS4_SYSV_ABI sceHttpUriMerge(); -int PS4_SYSV_ABI sceHttpUriParse(); -int PS4_SYSV_ABI sceHttpUriSweepPath(); -int PS4_SYSV_ABI sceHttpUriUnescape(); +int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool, + size_t* require, size_t prepare); +int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize); +int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in); int PS4_SYSV_ABI sceHttpWaitRequest(); void RegisterlibSceHttp(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/network/http_error.h b/src/core/libraries/network/http_error.h new file mode 100644 index 000000000..49cc89766 --- /dev/null +++ b/src/core/libraries/network/http_error.h @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +constexpr int ORBIS_HTTP_ERROR_BEFORE_INIT = 0x80431001; +constexpr int ORBIS_HTTP_ERROR_ALREADY_INITED = 0x80431020; +constexpr int ORBIS_HTTP_ERROR_BUSY = 0x80431021; +constexpr int ORBIS_HTTP_ERROR_OUT_OF_MEMORY = 0x80431022; +constexpr int ORBIS_HTTP_ERROR_NOT_FOUND = 0x80431025; +constexpr int ORBIS_HTTP_ERROR_INVALID_VERSION = 0x8043106a; +constexpr int ORBIS_HTTP_ERROR_INVALID_ID = 0x80431100; +constexpr int ORBIS_HTTP_ERROR_OUT_OF_SIZE = 0x80431104; +constexpr int ORBIS_HTTP_ERROR_INVALID_VALUE = 0x804311fe; + +constexpr int ORBIS_HTTP_ERROR_INVALID_URL = 0x80433060; +constexpr int ORBIS_HTTP_ERROR_UNKNOWN_SCHEME = 0x80431061; +constexpr int ORBIS_HTTP_ERROR_NETWORK = 0x80431063; +constexpr int ORBIS_HTTP_ERROR_BAD_RESPONSE = 0x80431064; +constexpr int ORBIS_HTTP_ERROR_BEFORE_SEND = 0x80431065; +constexpr int ORBIS_HTTP_ERROR_AFTER_SEND = 0x80431066; +constexpr int ORBIS_HTTP_ERROR_TIMEOUT = 0x80431068; +constexpr int ORBIS_HTTP_ERROR_UNKNOWN_AUTH_TYPE = 0x80431069; +constexpr int ORBIS_HTTP_ERROR_UNKNOWN_METHOD = 0x8043106b; +constexpr int ORBIS_HTTP_ERROR_READ_BY_HEAD_METHOD = 0x8043106f; +constexpr int ORBIS_HTTP_ERROR_NOT_IN_COM = 0x80431070; +constexpr int ORBIS_HTTP_ERROR_NO_CONTENT_LENGTH = 0x80431071; +constexpr int ORBIS_HTTP_ERROR_CHUNK_ENC = 0x80431072; +constexpr int ORBIS_HTTP_ERROR_TOO_LARGE_RESPONSE_HEADER = 0x80431073; +constexpr int ORBIS_HTTP_ERROR_SSL = 0x80431075; +constexpr int ORBIS_HTTP_ERROR_INSUFFICIENT_STACKSIZE = 0x80431076; +constexpr int ORBIS_HTTP_ERROR_ABORTED = 0x80431080; +constexpr int ORBIS_HTTP_ERROR_UNKNOWN = 0x80431081; +constexpr int ORBIS_HTTP_ERROR_EAGAIN = 0x80431082; +constexpr int ORBIS_HTTP_ERROR_PROXY = 0x80431084; +constexpr int ORBIS_HTTP_ERROR_BROKEN = 0x80431085; + +constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_NOT_FOUND = 0x80432025; +constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE = 0x80432060; +constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_VALUE = 0x804321fe; + +constexpr int ORBIS_HTTP_ERROR_RESOLVER_EPACKET = 0x80436001; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENODNS = 0x80436002; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ETIMEDOUT = 0x80436003; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOSUPPORT = 0x80436004; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_EFORMAT = 0x80436005; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ESERVERFAILURE = 0x80436006; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOHOST = 0x80436007; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOTIMPLEMENTED = 0x80436008; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ESERVERREFUSED = 0x80436009; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENORECORD = 0x8043600a; + +constexpr int ORBIS_HTTPS_ERROR_CERT = 0x80435060; +constexpr int ORBIS_HTTPS_ERROR_HANDSHAKE = 0x80435061; +constexpr int ORBIS_HTTPS_ERROR_IO = 0x80435062; +constexpr int ORBIS_HTTPS_ERROR_INTERNAL = 0x80435063; +constexpr int ORBIS_HTTPS_ERROR_PROXY = 0x80435064; + +constexpr int ORBIS_HTTPS_ERROR_SSL_INTERNAL = 0x01; +constexpr int ORBIS_HTTPS_ERROR_SSL_INVALID_CERT = 0x02; +constexpr int ORBIS_HTTPS_ERROR_SSL_CN_CHECK = 0x04; +constexpr int ORBIS_HTTPS_ERROR_SSL_NOT_AFTER_CHECK = 0x08; +constexpr int ORBIS_HTTPS_ERROR_SSL_NOT_BEFORE_CHECK = 0x10; +constexpr int ORBIS_HTTPS_ERROR_SSL_UNKNOWN_CA = 0x20; diff --git a/src/core/libraries/ngs2/ngs2.cpp b/src/core/libraries/ngs2/ngs2.cpp index 7eb663413..0b42e2471 100644 --- a/src/core/libraries/ngs2/ngs2.cpp +++ b/src/core/libraries/ngs2/ngs2.cpp @@ -5,21 +5,480 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" +#include "core/libraries/ngs2/ngs2_custom.h" #include "core/libraries/ngs2/ngs2_error.h" +#include "core/libraries/ngs2/ngs2_geom.h" #include "core/libraries/ngs2/ngs2_impl.h" +#include "core/libraries/ngs2/ngs2_pan.h" +#include "core/libraries/ngs2/ngs2_report.h" namespace Libraries::Ngs2 { -int PS4_SYSV_ABI sceNgs2CalcWaveformBlock() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); +// Ngs2 + +s32 PS4_SYSV_ABI sceNgs2CalcWaveformBlock(const OrbisNgs2WaveformFormat* format, u32 samplePos, + u32 numSamples, OrbisNgs2WaveformBlock* outBlock) { + LOG_INFO(Lib_Ngs2, "samplePos = {}, numSamples = {}", samplePos, numSamples); return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2CustomRackGetModuleInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNgs2GetWaveformFrameInfo(const OrbisNgs2WaveformFormat* format, + u32* outFrameSize, u32* outNumFrameSamples, + u32* outUnitsPerFrame, u32* outNumDelaySamples) { + LOG_INFO(Lib_Ngs2, "called"); return ORBIS_OK; } +s32 PS4_SYSV_ABI sceNgs2ParseWaveformData(const void* data, size_t dataSize, + OrbisNgs2WaveformInfo* outInfo) { + LOG_INFO(Lib_Ngs2, "dataSize = {}", dataSize); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2ParseWaveformFile(const char* path, u64 offset, + OrbisNgs2WaveformInfo* outInfo) { + LOG_INFO(Lib_Ngs2, "path = {}, offset = {}", path, offset); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2ParseWaveformUser(OrbisNgs2ParseReadHandler handler, uintptr_t userData, + OrbisNgs2WaveformInfo* outInfo) { + LOG_INFO(Lib_Ngs2, "userData = {}", userData); + if (!handler) { + LOG_ERROR(Lib_Ngs2, "handler is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackCreate(OrbisNgs2Handle systemHandle, u32 rackId, + const OrbisNgs2RackOption* option, + const OrbisNgs2ContextBufferInfo* bufferInfo, + OrbisNgs2Handle* outHandle) { + LOG_INFO(Lib_Ngs2, "rackId = {}", rackId); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackCreateWithAllocator(OrbisNgs2Handle systemHandle, u32 rackId, + const OrbisNgs2RackOption* option, + const OrbisNgs2BufferAllocator* allocator, + OrbisNgs2Handle* outHandle) { + LOG_INFO(Lib_Ngs2, "rackId = {}", rackId); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackDestroy(OrbisNgs2Handle rackHandle, + OrbisNgs2ContextBufferInfo* outBufferInfo) { + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackGetInfo(OrbisNgs2Handle rackHandle, OrbisNgs2RackInfo* outInfo, + size_t infoSize) { + LOG_INFO(Lib_Ngs2, "infoSize = {}", infoSize); + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackGetUserData(OrbisNgs2Handle rackHandle, uintptr_t* outUserData) { + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackGetVoiceHandle(OrbisNgs2Handle rackHandle, u32 voiceIndex, + OrbisNgs2Handle* outHandle) { + LOG_INFO(Lib_Ngs2, "voiceIndex = {}", voiceIndex); + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackLock(OrbisNgs2Handle rackHandle) { + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackQueryBufferSize(u32 rackId, const OrbisNgs2RackOption* option, + OrbisNgs2ContextBufferInfo* outBufferInfo) { + LOG_INFO(Lib_Ngs2, "rackId = {}", rackId); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackSetUserData(OrbisNgs2Handle rackHandle, uintptr_t userData) { + LOG_INFO(Lib_Ngs2, "userData = {}", userData); + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackUnlock(OrbisNgs2Handle rackHandle) { + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemCreate(const OrbisNgs2SystemOption* option, + const OrbisNgs2ContextBufferInfo* bufferInfo, + OrbisNgs2Handle* outHandle) { + s32 result; + OrbisNgs2ContextBufferInfo localInfo; + if (!bufferInfo || !outHandle) { + if (!bufferInfo) { + result = ORBIS_NGS2_ERROR_INVALID_BUFFER_INFO; + LOG_ERROR(Lib_Ngs2, "Invalid system buffer info {}", (void*)bufferInfo); + } else { + result = ORBIS_NGS2_ERROR_INVALID_OUT_ADDRESS; + LOG_ERROR(Lib_Ngs2, "Invalid system handle address {}", (void*)outHandle); + } + + // TODO: Report errors? + } else { + // Make bufferInfo copy + localInfo.hostBuffer = bufferInfo->hostBuffer; + localInfo.hostBufferSize = bufferInfo->hostBufferSize; + for (int i = 0; i < 5; i++) { + localInfo.reserved[i] = bufferInfo->reserved[i]; + } + localInfo.userData = bufferInfo->userData; + + result = SystemSetup(option, &localInfo, 0, outHandle); + } + + // TODO: API reporting? + + LOG_INFO(Lib_Ngs2, "called"); + return result; +} + +s32 PS4_SYSV_ABI sceNgs2SystemCreateWithAllocator(const OrbisNgs2SystemOption* option, + const OrbisNgs2BufferAllocator* allocator, + OrbisNgs2Handle* outHandle) { + s32 result; + if (allocator && allocator->allocHandler != 0) { + OrbisNgs2BufferAllocHandler hostAlloc = allocator->allocHandler; + if (outHandle) { + OrbisNgs2BufferFreeHandler hostFree = allocator->freeHandler; + OrbisNgs2ContextBufferInfo* bufferInfo = 0; + result = SystemSetup(option, bufferInfo, 0, 0); + if (result >= 0) { + uintptr_t sysUserData = allocator->userData; + result = hostAlloc(bufferInfo); + if (result >= 0) { + OrbisNgs2Handle* handleCopy = outHandle; + result = SystemSetup(option, bufferInfo, hostFree, handleCopy); + if (result < 0) { + if (hostFree) { + hostFree(bufferInfo); + } + } + } + } + } else { + result = ORBIS_NGS2_ERROR_INVALID_OUT_ADDRESS; + LOG_ERROR(Lib_Ngs2, "Invalid system handle address {}", (void*)outHandle); + } + } else { + result = ORBIS_NGS2_ERROR_INVALID_BUFFER_ALLOCATOR; + LOG_ERROR(Lib_Ngs2, "Invalid system buffer allocator {}", (void*)allocator); + } + LOG_INFO(Lib_Ngs2, "called"); + return result; +} + +s32 PS4_SYSV_ABI sceNgs2SystemDestroy(OrbisNgs2Handle systemHandle, + OrbisNgs2ContextBufferInfo* outBufferInfo) { + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemEnumHandles(OrbisNgs2Handle* aOutHandle, u32 maxHandles) { + LOG_INFO(Lib_Ngs2, "maxHandles = {}", maxHandles); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemEnumRackHandles(OrbisNgs2Handle systemHandle, + OrbisNgs2Handle* aOutHandle, u32 maxHandles) { + LOG_INFO(Lib_Ngs2, "maxHandles = {}", maxHandles); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemGetInfo(OrbisNgs2Handle rackHandle, OrbisNgs2SystemInfo* outInfo, + size_t infoSize) { + LOG_INFO(Lib_Ngs2, "infoSize = {}", infoSize); + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemGetUserData(OrbisNgs2Handle systemHandle, uintptr_t* outUserData) { + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemLock(OrbisNgs2Handle systemHandle) { + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemQueryBufferSize(const OrbisNgs2SystemOption* option, + OrbisNgs2ContextBufferInfo* outBufferInfo) { + s32 result; + if (outBufferInfo) { + result = SystemSetup(option, outBufferInfo, 0, 0); + LOG_INFO(Lib_Ngs2, "called"); + } else { + result = ORBIS_NGS2_ERROR_INVALID_OUT_ADDRESS; + LOG_ERROR(Lib_Ngs2, "Invalid system buffer info {}", (void*)outBufferInfo); + } + + return result; +} + +s32 PS4_SYSV_ABI sceNgs2SystemRender(OrbisNgs2Handle systemHandle, + const OrbisNgs2RenderBufferInfo* aBufferInfo, + u32 numBufferInfo) { + LOG_INFO(Lib_Ngs2, "numBufferInfo = {}", numBufferInfo); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +static s32 PS4_SYSV_ABI sceNgs2SystemResetOption(OrbisNgs2SystemOption* outOption) { + static const OrbisNgs2SystemOption option = { + sizeof(OrbisNgs2SystemOption), "", 0, 512, 256, 48000, {0}}; + + if (!outOption) { + LOG_ERROR(Lib_Ngs2, "Invalid system option address {}", (void*)outOption); + return ORBIS_NGS2_ERROR_INVALID_OPTION_ADDRESS; + } + *outOption = option; + + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemSetGrainSamples(OrbisNgs2Handle systemHandle, u32 numSamples) { + LOG_INFO(Lib_Ngs2, "numSamples = {}", numSamples); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemSetSampleRate(OrbisNgs2Handle systemHandle, u32 sampleRate) { + LOG_INFO(Lib_Ngs2, "sampleRate = {}", sampleRate); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemSetUserData(OrbisNgs2Handle systemHandle, uintptr_t userData) { + LOG_INFO(Lib_Ngs2, "userData = {}", userData); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemUnlock(OrbisNgs2Handle systemHandle) { + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceControl(OrbisNgs2Handle voiceHandle, + const OrbisNgs2VoiceParamHeader* paramList) { + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceGetMatrixInfo(OrbisNgs2Handle voiceHandle, u32 matrixId, + OrbisNgs2VoiceMatrixInfo* outInfo, size_t outInfoSize) { + LOG_INFO(Lib_Ngs2, "matrixId = {}, outInfoSize = {}", matrixId, outInfoSize); + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceGetOwner(OrbisNgs2Handle voiceHandle, OrbisNgs2Handle* outRackHandle, + u32* outVoiceId) { + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceGetPortInfo(OrbisNgs2Handle voiceHandle, u32 port, + OrbisNgs2VoicePortInfo* outInfo, size_t outInfoSize) { + LOG_INFO(Lib_Ngs2, "port = {}, outInfoSize = {}", port, outInfoSize); + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceGetState(OrbisNgs2Handle voiceHandle, OrbisNgs2VoiceState* outState, + size_t stateSize) { + LOG_INFO(Lib_Ngs2, "stateSize = {}", stateSize); + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceGetStateFlags(OrbisNgs2Handle voiceHandle, u32* outStateFlags) { + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +// Ngs2Custom + +s32 PS4_SYSV_ABI sceNgs2CustomRackGetModuleInfo(OrbisNgs2Handle rackHandle, u32 moduleIndex, + OrbisNgs2CustomModuleInfo* outInfo, + size_t infoSize) { + LOG_INFO(Lib_Ngs2, "moduleIndex = {}, infoSize = {}", moduleIndex, infoSize); + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + return ORBIS_OK; +} + +// Ngs2Geom + +s32 PS4_SYSV_ABI sceNgs2GeomResetListenerParam(OrbisNgs2GeomListenerParam* outListenerParam) { + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2GeomResetSourceParam(OrbisNgs2GeomSourceParam* outSourceParam) { + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2GeomCalcListener(const OrbisNgs2GeomListenerParam* param, + OrbisNgs2GeomListenerWork* outWork, u32 flags) { + LOG_INFO(Lib_Ngs2, "flags = {}", flags); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2GeomApply(const OrbisNgs2GeomListenerWork* listener, + const OrbisNgs2GeomSourceParam* source, + OrbisNgs2GeomAttribute* outAttrib, u32 flags) { + LOG_INFO(Lib_Ngs2, "flags = {}", flags); + return ORBIS_OK; +} + +// Ngs2Pan + +s32 PS4_SYSV_ABI sceNgs2PanInit(OrbisNgs2PanWork* work, const float* aSpeakerAngle, float unitAngle, + u32 numSpeakers) { + LOG_INFO(Lib_Ngs2, "aSpeakerAngle = {}, unitAngle = {}, numSpeakers = {}", *aSpeakerAngle, + unitAngle, numSpeakers); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2PanGetVolumeMatrix(OrbisNgs2PanWork* work, const OrbisNgs2PanParam* aParam, + u32 numParams, u32 matrixFormat, + float* outVolumeMatrix) { + LOG_INFO(Lib_Ngs2, "numParams = {}, matrixFormat = {}", numParams, matrixFormat); + return ORBIS_OK; +} + +// Ngs2Report + +s32 PS4_SYSV_ABI sceNgs2ReportRegisterHandler(u32 reportType, OrbisNgs2ReportHandler handler, + uintptr_t userData, OrbisNgs2Handle* outHandle) { + LOG_INFO(Lib_Ngs2, "reportType = {}, userData = {}", reportType, userData); + if (!handler) { + LOG_ERROR(Lib_Ngs2, "handler is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2ReportUnregisterHandler(OrbisNgs2Handle reportHandle) { + if (!reportHandle) { + LOG_ERROR(Lib_Ngs2, "reportHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +// Unknown + int PS4_SYSV_ABI sceNgs2FftInit() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; @@ -35,31 +494,6 @@ int PS4_SYSV_ABI sceNgs2FftQuerySize() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2GeomApply() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2GeomCalcListener() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2GeomResetListenerParam() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2GeomResetSourceParam() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2GetWaveformFrameInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2JobSchedulerResetOption() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; @@ -80,71 +514,6 @@ int PS4_SYSV_ABI sceNgs2ModuleQueueEnumItems() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2PanGetVolumeMatrix() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2PanInit() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2ParseWaveformData() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2ParseWaveformFile() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2ParseWaveformUser() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackCreate() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackCreateWithAllocator() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackDestroy() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackGetInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackGetUserData() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackGetVoiceHandle() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackLock() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackQueryBufferSize() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2RackQueryInfo() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; @@ -155,116 +524,21 @@ int PS4_SYSV_ABI sceNgs2RackRunCommands() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2RackSetUserData() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackUnlock() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2ReportRegisterHandler() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2ReportUnregisterHandler() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemCreate() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemCreateWithAllocator() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemDestroy() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemEnumHandles() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemEnumRackHandles() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemGetInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemGetUserData() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemLock() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemQueryBufferSize() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2SystemQueryInfo() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2SystemRender() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemResetOption() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2SystemRunCommands() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2SystemSetGrainSamples() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2SystemSetLoudThreshold() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2SystemSetSampleRate() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemSetUserData() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemUnlock() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2StreamCreate() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; @@ -300,36 +574,6 @@ int PS4_SYSV_ABI sceNgs2StreamRunCommands() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2VoiceControl() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2VoiceGetMatrixInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2VoiceGetOwner() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2VoiceGetPortInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2VoiceGetState() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2VoiceGetStateFlags() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2VoiceQueryInfo() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; diff --git a/src/core/libraries/ngs2/ngs2.h b/src/core/libraries/ngs2/ngs2.h index a5f1f52a6..a34bf21d4 100644 --- a/src/core/libraries/ngs2/ngs2.h +++ b/src/core/libraries/ngs2/ngs2.h @@ -3,7 +3,11 @@ #pragma once +#include "core/libraries/ngs2/ngs2_impl.h" + #include +#include +#include #include "common/types.h" namespace Core::Loader { @@ -12,60 +16,253 @@ class SymbolsResolver; namespace Libraries::Ngs2 { -class Ngs2; +typedef s32 (*OrbisNgs2ParseReadHandler)(uintptr_t userData, u32 offset, void* data, size_t size); -using SceNgs2Handle = Ngs2*; - -enum class SceNgs2HandleType : u32 { - System = 0, +enum class OrbisNgs2HandleType : u32 { + Invalid = 0, + System = 1, + Rack = 2, + Voice = 3, + VoiceControl = 6 }; -struct Ngs2Handle { - void* selfPointer; - void* dataPointer; - std::atomic* atomicPtr; - u32 handleType; - u32 flags_unk; +static const int ORBIS_NGS2_MAX_VOICE_CHANNELS = 8; +static const int ORBIS_NGS2_WAVEFORM_INFO_MAX_BLOCKS = 4; +static const int ORBIS_NGS2_MAX_MATRIX_LEVELS = + (ORBIS_NGS2_MAX_VOICE_CHANNELS * ORBIS_NGS2_MAX_VOICE_CHANNELS); - u32 uid; - u16 maxGrainSamples; - u16 minGrainSamples; - u16 currentGrainSamples; - u16 numGrainSamples; - u16 unknown2; +struct OrbisNgs2WaveformFormat { + u32 waveformType; + u32 numChannels; u32 sampleRate; - u32 unknown3; - - void* flushMutex; - u32 flushMutexInitialized; - void* processMutex; - u32 processMutexInitialized; - - // Linked list pointers for system list - Ngs2Handle* prev; - Ngs2Handle* next; + u32 configData; + u32 frameOffset; + u32 frameMargin; }; -struct SystemOptions { - char padding[6]; - s32 maxGrainSamples; - s32 numGrainSamples; - s32 sampleRate; +struct OrbisNgs2WaveformBlock { + u32 dataOffset; + u32 dataSize; + u32 numRepeats; + u32 numSkipSamples; + u32 numSamples; + u32 reserved; + uintptr_t userData; }; -struct SystemState { - // TODO +struct OrbisNgs2WaveformInfo { + OrbisNgs2WaveformFormat format; + + u32 dataOffset; + u32 dataSize; + + u32 loopBeginPosition; + u32 loopEndPosition; + u32 numSamples; + + u32 audioUnitSize; + u32 numAudioUnitSamples; + u32 numAudioUnitPerFrame; + + u32 audioFrameSize; + u32 numAudioFrameSamples; + + u32 numDelaySamples; + + u32 numBlocks; + OrbisNgs2WaveformBlock aBlock[ORBIS_NGS2_WAVEFORM_INFO_MAX_BLOCKS]; }; -struct StackBuffer { - void** top; - void* base; - void* curr; - size_t usedSize; - size_t totalSize; - size_t alignment; - char isVerifyEnabled; - char padding[7]; +struct OrbisNgs2EnvelopePoint { + u32 curve; + u32 duration; + float height; +}; + +struct OrbisNgs2UserFxProcessContext { + float** aChannelData; + uintptr_t userData0; + uintptr_t userData1; + uintptr_t userData2; + u32 flags; + u32 numChannels; + u32 numGrainSamples; + u32 sampleRate; +}; + +typedef s32 (*OrbisNgs2UserFxProcessHandler)(OrbisNgs2UserFxProcessContext* context); + +struct OrbisNgs2UserFx2SetupContext { + void* common; + void* param; + void* work; + uintptr_t userData; + u32 maxVoices; + u32 voiceIndex; + u64 reserved[4]; +}; + +typedef s32 (*OrbisNgs2UserFx2SetupHandler)(OrbisNgs2UserFx2SetupContext* context); + +struct OrbisNgs2UserFx2CleanupContext { + void* common; + void* param; + void* work; + uintptr_t userData; + u32 maxVoices; + u32 voiceIndex; + u64 reserved[4]; +}; + +typedef s32 (*OrbisNgs2UserFx2CleanupHandler)(OrbisNgs2UserFx2CleanupContext* context); + +struct OrbisNgs2UserFx2ControlContext { + const void* data; + size_t dataSize; + void* common; + void* param; + uintptr_t userData; + u64 reserved[4]; +}; + +typedef s32 (*OrbisNgs2UserFx2ControlHandler)(OrbisNgs2UserFx2ControlContext* context); + +struct OrbisNgs2UserFx2ProcessContext { + float** aChannelData; + void* common; + const void* param; + void* work; + void* state; + uintptr_t userData; + u32 flags; + u32 numInputChannels; + u32 numOutputChannels; + u32 numGrainSamples; + u32 sampleRate; + u32 reserved; + u64 reserved2[4]; +}; + +typedef s32 (*OrbisNgs2UserFx2ProcessHandler)(OrbisNgs2UserFx2ProcessContext* context); + +struct OrbisNgs2BufferAllocator { + OrbisNgs2BufferAllocHandler allocHandler; + OrbisNgs2BufferFreeHandler freeHandler; + uintptr_t userData; +}; + +struct OrbisNgs2RenderBufferInfo { + void* buffer; + size_t bufferSize; + u32 waveformType; + u32 numChannels; +}; + +struct OrbisNgs2RackOption { + size_t size; + char name[ORBIS_NGS2_RACK_NAME_LENGTH]; + + u32 flags; + u32 maxGrainSamples; + u32 maxVoices; + u32 maxInputDelayBlocks; + u32 maxMatrices; + u32 maxPorts; + u32 aReserved[20]; +}; + +struct OrbisNgs2VoiceParamHeader { + u16 size; + s16 next; + u32 id; +}; + +struct OrbisNgs2VoiceMatrixLevelsParam { + OrbisNgs2VoiceParamHeader header; + + u32 matrixId; + u32 numLevels; + const float* aLevel; +}; + +struct OrbisNgs2VoicePortMatrixParam { + OrbisNgs2VoiceParamHeader header; + + u32 port; + s32 matrixId; +}; + +struct OrbisNgs2VoicePortVolumeParam { + OrbisNgs2VoiceParamHeader header; + + u32 port; + float level; +}; + +struct OrbisNgs2VoicePortDelayParam { + OrbisNgs2VoiceParamHeader header; + + u32 port; + u32 numSamples; +}; + +struct OrbisNgs2VoicePatchParam { + OrbisNgs2VoiceParamHeader header; + + u32 port; + u32 destInputId; + OrbisNgs2Handle destHandle; +}; + +struct OrbisNgs2VoiceEventParam { + OrbisNgs2VoiceParamHeader header; + + u32 eventId; +}; + +struct OrbisNgs2VoiceCallbackInfo { + uintptr_t callbackData; + OrbisNgs2Handle voiceHandle; + u32 flag; + u32 reserved; + union { + struct { + uintptr_t userData; + const void* data; + u32 dataSize; + u32 repeatedCount; + u32 attributeFlags; + u32 reserved2; + } waveformBlock; + } param; +}; + +typedef void (*OrbisNgs2VoiceCallbackHandler)(const OrbisNgs2VoiceCallbackInfo* info); + +struct OrbisNgs2VoiceCallbackParam { + OrbisNgs2VoiceParamHeader header; + OrbisNgs2VoiceCallbackHandler callbackHandler; + + uintptr_t callbackData; + u32 flags; + u32 reserved; +}; + +struct OrbisNgs2VoicePortInfo { + s32 matrixId; + float volume; + u32 numDelaySamples; + u32 destInputId; + OrbisNgs2Handle destHandle; +}; + +struct OrbisNgs2VoiceMatrixInfo { + u32 numLevels; + float aLevel[ORBIS_NGS2_MAX_MATRIX_LEVELS]; +}; + +struct OrbisNgs2VoiceState { + u32 stateFlags; }; void RegisterlibSceNgs2(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/ngs2/ngs2_custom.cpp b/src/core/libraries/ngs2/ngs2_custom.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_custom.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_custom.h b/src/core/libraries/ngs2/ngs2_custom.h new file mode 100644 index 000000000..0c45a5d81 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_custom.h @@ -0,0 +1,444 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" +#include "ngs2_reverb.h" + +namespace Libraries::Ngs2 { + +class Ngs2Custom; + +static const int ORBIS_NGS2_CUSTOM_MAX_MODULES = 24; +static const int ORBIS_NGS2_CUSTOM_MAX_PORTS = 16; +static const int ORBIS_NGS2_CUSTOM_DELAY_MAX_TAPS = 8; + +struct OrbisNgs2CustomModuleOption { + u32 size; +}; + +struct OrbisNgs2CustomEnvelopeModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + u32 maxPoints; + u32 reserved; +}; + +struct OrbisNgs2CustomReverbModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + u32 reverbSize; + u32 reserved; +}; + +struct OrbisNgs2CustomChorusModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + u32 maxPhases; + u32 reserved; +} OrbisNgs2CustomChorusModuleOption; + +struct OrbisNgs2CustomPeakMeterModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + u32 numBlocks; + u32 reserved; +}; + +struct OrbisNgs2CustomDelayModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + u32 type; + u32 maxTaps; + float maxLength; + u32 reserved; +}; + +struct OrbisNgs2CustomPitchShiftModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + u32 quality; +}; + +struct OrbisNgs2CustomUserFx2ModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + OrbisNgs2UserFx2SetupHandler setupHandler; + OrbisNgs2UserFx2CleanupHandler cleanupHandler; + OrbisNgs2UserFx2ControlHandler controlHandler; + OrbisNgs2UserFx2ProcessHandler processHandler; + + size_t commonSize; + size_t paramSize; + size_t workSize; + uintptr_t userData; +}; + +struct OrbisNgs2CustomRackModuleInfo { + const OrbisNgs2CustomModuleOption* option; + + u32 moduleId; + u32 sourceBufferId; + u32 extraBufferId; + u32 destBufferId; + u32 stateOffset; + u32 stateSize; + u32 reserved; + u32 reserved2; +}; + +struct OrbisNgs2CustomRackPortInfo { + u32 sourceBufferId; + u32 reserved; +}; + +struct OrbisNgs2CustomRackOption { + OrbisNgs2RackOption rackOption; + u32 stateSize; + u32 numBuffers; + u32 numModules; + u32 reserved; + OrbisNgs2CustomRackModuleInfo aModule[ORBIS_NGS2_CUSTOM_MAX_MODULES]; + OrbisNgs2CustomRackPortInfo aPort[ORBIS_NGS2_CUSTOM_MAX_PORTS]; +}; + +struct OrbisNgs2CustomSamplerRackOption { + OrbisNgs2CustomRackOption customRackOption; + + u32 maxChannelWorks; + u32 maxWaveformBlocks; + u32 maxAtrac9Decoders; + u32 maxAtrac9ChannelWorks; + u32 maxAjmAtrac9Decoders; + u32 maxCodecCaches; +}; + +struct OrbisNgs2CustomSubmixerRackOption { + OrbisNgs2CustomRackOption customRackOption; + + u32 maxChannels; + u32 maxInputs; +}; + +struct OrbisNgs2CustomMasteringRackOption { + OrbisNgs2CustomRackOption customRackOption; + + u32 maxChannels; + u32 maxInputs; +}; + +struct OrbisNgs2CustomSamplerVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + OrbisNgs2WaveformFormat format; + u32 flags; + u32 reserved; +}; + +struct OrbisNgs2CustomSamplerVoiceWaveformBlocksParam { + OrbisNgs2VoiceParamHeader header; + const void* data; + u32 flags; + u32 numBlocks; + const OrbisNgs2WaveformBlock* aBlock; +}; + +struct OrbisNgs2CustomSamplerVoiceWaveformAddressParam { + OrbisNgs2VoiceParamHeader header; + const void* from; + const void* to; +}; + +struct OrbisNgs2CustomSamplerVoiceWaveformFrameOffsetParam { + OrbisNgs2VoiceParamHeader header; + u32 frameOffset; + u32 reserved; +}; + +struct OrbisNgs2CustomSamplerVoiceExitLoopParam { + OrbisNgs2VoiceParamHeader header; +}; + +struct OrbisNgs2CustomSamplerVoicePitchParam { + OrbisNgs2VoiceParamHeader header; + float ratio; + u32 reserved; +}; + +struct OrbisNgs2CustomSamplerVoiceState { + OrbisNgs2VoiceState voiceState; + char padding[32]; + const void* waveformData; + u64 numDecodedSamples; + u64 decodedDataSize; + u64 userData; + u32 reserved; + u32 reserved2; +}; + +struct OrbisNgs2CustomSubmixerVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + u32 numInputChannels; + u32 numOutputChannels; + u32 flags; + u32 reserved; +}; + +struct OrbisNgs2CustomSubmixerVoiceState { + OrbisNgs2VoiceState voiceState; // Voice state + u32 reserved; + u32 reserved2; +}; + +struct OrbisNgs2CustomMasteringVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + u32 numInputChannels; + u32 flags; +}; + +struct OrbisNgs2CustomMasteringVoiceOutputParam { + OrbisNgs2VoiceParamHeader header; + u32 outputId; + u32 reserved; +}; + +struct OrbisNgs2CustomMasteringVoiceState { + OrbisNgs2VoiceState voiceState; + u32 reserved; + u32 reserved2; +}; + +struct OrbisNgs2CustomVoiceEnvelopeParam { + OrbisNgs2VoiceParamHeader header; + u32 numForwardPoints; + u32 numReleasePoints; + const OrbisNgs2EnvelopePoint* aPoint; +}; + +struct OrbisNgs2CustomVoiceDistortionParam { + OrbisNgs2VoiceParamHeader header; + u32 flags; + float a; + float b; + float clip; + float gate; + float wetLevel; + float dryLevel; + u32 reserved; +}; + +struct OrbisNgs2CustomVoiceCompressorParam { + OrbisNgs2VoiceParamHeader header; + u32 flags; + float threshold; + float ratio; + float knee; + float attackTime; + float releaseTime; + float level; + u32 reserved; +}; + +struct OrbisNgs2CustomVoiceFilterParam { + OrbisNgs2VoiceParamHeader header; + u32 type; + u32 channelMask; + union { + struct { + float i0; + float i1; + float i2; + float o1; + float o2; + } direct; + struct { + float fc; + float q; + float level; + u32 reserved; + u32 reserved2; + } fcq; + } param; + u32 reserved3; +}; + +struct OrbisNgs2CustomVoiceLfeFilterParam { + OrbisNgs2VoiceParamHeader header; + u32 enableFlag; + u32 fc; +}; + +struct OrbisNgs2CustomVoiceGainParam { + OrbisNgs2VoiceParamHeader header; + float aLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS]; +}; + +struct OrbisNgs2CustomVoiceMixerParam { + OrbisNgs2VoiceParamHeader header; + float aSourceLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS]; + float aDestLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS]; +}; + +struct OrbisNgs2CustomVoiceChannelMixerParam { + OrbisNgs2VoiceParamHeader header; + float aLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS][ORBIS_NGS2_MAX_VOICE_CHANNELS]; +}; + +struct OrbisNgs2CustomVoiceUserFxParam { + OrbisNgs2VoiceParamHeader header; + OrbisNgs2UserFxProcessHandler handler; + + uintptr_t userData0; + uintptr_t userData1; + uintptr_t userData2; +}; + +struct OrbisNgs2CustomVoiceUserFx2Param { + OrbisNgs2VoiceParamHeader header; + const void* data; + size_t dataSize; +}; + +struct OrbisNgs2CustomVoiceOutputParam { + OrbisNgs2VoiceParamHeader header; + u32 outputId; + u32 reserved; +}; + +struct OrbisNgs2CustomVoicePeakMeterParam { + OrbisNgs2VoiceParamHeader header; + u32 enableFlag; + u32 reserved; +} OrbisNgs2CustomVoicePeakMeterParam; + +struct OrbisNgs2CustomVoiceReverbParam { + OrbisNgs2VoiceParamHeader header; + OrbisNgs2ReverbI3DL2Param i3dl2; +}; + +struct OrbisNgs2CustomVoiceChorusParam { + OrbisNgs2VoiceParamHeader header; + u32 flags; + u32 numPhases; + u32 channelMask; + float inputLevel; + float delayTime; + float modulationRatio; + float modulationDepth; + float feedbackLevel; + float wetLevel; + float dryLevel; +}; + +struct OrbisNgs2DelayTapInfo { + float tapLevel; + float delayTime; +}; + +struct OrbisNgs2CustomVoiceDelayParam { + OrbisNgs2VoiceParamHeader header; + float dryLevel; + float wetLevel; + float inputLevel; + float feedbackLevel; + float lowpassFc; + u32 numTaps; + OrbisNgs2DelayTapInfo aTap[ORBIS_NGS2_CUSTOM_DELAY_MAX_TAPS]; + float aInputMixLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS]; + u32 channelMask; + u32 flags; +}; + +struct OrbisNgs2CustomVoiceNoiseGateParam { + OrbisNgs2VoiceParamHeader header; + u32 flags; + float threshold; + float attackTime; + float releaseTime; +}; + +struct OrbisNgs2CustomVoicePitchShiftParam { + OrbisNgs2VoiceParamHeader header; + s32 cent; +}; + +struct OrbisNgs2CustomEnvelopeModuleState { + float height; + u32 reserved; +}; + +struct OrbisNgs2CustomCompressorModuleState { + float peakHeight; + float compressorHeight; +}; + +struct OrbisNgs2CustomPeakMeterModuleState { + float peak; + float aChannelPeak[ORBIS_NGS2_MAX_VOICE_CHANNELS]; + u32 reserved; +}; + +struct OrbisNgs2CustomNoiseGateModuleState { + float gateHeight; +}; + +struct OrbisNgs2CustomRackInfo { + OrbisNgs2RackInfo rackInfo; + u32 stateSize; + u32 numBuffers; + u32 numModules; + u32 reserved; + OrbisNgs2CustomRackModuleInfo aModule[ORBIS_NGS2_CUSTOM_MAX_MODULES]; + OrbisNgs2CustomRackPortInfo aPort[ORBIS_NGS2_CUSTOM_MAX_PORTS]; +}; + +struct OrbisNgs2CustomSamplerRackInfo { + OrbisNgs2CustomRackInfo customRackInfo; + + u32 maxChannelWorks; + u32 maxWaveformBlocks; + u32 maxAtrac9Decoders; + u32 maxAtrac9ChannelWorks; + u32 maxAjmAtrac9Decoders; + u32 maxCodecCaches; +}; + +struct OrbisNgs2CustomSubmixerRackInfo { + OrbisNgs2CustomRackInfo customRackInfo; + + u32 maxChannels; + u32 maxInputs; +}; + +struct OrbisNgs2CustomMasteringRackInfo { + OrbisNgs2CustomRackInfo customRackInfo; + + u32 maxChannels; + u32 maxInputs; +}; + +struct OrbisNgs2CustomModuleInfo { + u32 moduleId; + u32 sourceBufferId; + u32 extraBufferId; + u32 destBufferId; + u32 stateOffset; + u32 stateSize; + u32 reserved; + u32 reserved2; +}; + +struct OrbisNgs2CustomEnvelopeModuleInfo { + OrbisNgs2CustomModuleInfo moduleInfo; + + u32 maxPoints; + u32 reserved; +}; + +struct OrbisNgs2CustomReverbModuleInfo { + OrbisNgs2CustomModuleInfo moduleInfo; + + u32 reverbSize; + u32 reserved; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_eq.cpp b/src/core/libraries/ngs2/ngs2_eq.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_eq.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_eq.h b/src/core/libraries/ngs2/ngs2_eq.h new file mode 100644 index 000000000..99688f24e --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_eq.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Eq; + +struct OrbisNgs2EqVoiceSetupParam { + u32 numChannels; +}; + +struct OrbisNgs2EqVoiceFilterParam { + u32 type; + u32 channelMask; + union { + struct { + float i0; + float i1; + float i2; + float o1; + float o2; + } direct; + struct { + float fc; + float q; + float level; + u32 reserved; + u32 reserved2; + } fcq; + } param; +}; + +struct OrbisNgs2EqVoiceState { + u32 stateFlags; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_geom.cpp b/src/core/libraries/ngs2/ngs2_geom.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_geom.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_geom.h b/src/core/libraries/ngs2/ngs2_geom.h new file mode 100644 index 000000000..93af99d8d --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_geom.h @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Geom; + +struct OrbisNgs2GeomVector { + float x; + float y; + float z; +}; + +struct OrbisNgs2GeomCone { + float innerLevel; + float innerAngle; + float outerLevel; + float outerAngle; +}; + +struct OrbisNgs2GeomRolloff { + u32 model; + float maxDistance; + float rolloffFactor; + float referenceDistance; +}; + +struct OrbisNgs2GeomListenerParam { + OrbisNgs2GeomVector position; + OrbisNgs2GeomVector orientFront; + OrbisNgs2GeomVector orientUp; + OrbisNgs2GeomVector velocity; + float soundSpeed; + u32 reserved[2]; +}; + +struct OrbisNgs2GeomListenerWork { + float matrix[4][4]; + OrbisNgs2GeomVector velocity; + float soundSpeed; + u32 coordinate; + u32 reserved[3]; +}; + +struct OrbisNgs2GeomSourceParam { + OrbisNgs2GeomVector position; + OrbisNgs2GeomVector velocity; + OrbisNgs2GeomVector direction; + OrbisNgs2GeomCone cone; + OrbisNgs2GeomRolloff rolloff; + float dopplerFactor; + float fbwLevel; + float lfeLevel; + float maxLevel; + float minLevel; + float radius; + u32 numSpeakers; + u32 matrixFormat; + u32 reserved[2]; +}; + +struct OrbisNgs2GeomA3dAttribute { + OrbisNgs2GeomVector position; + float volume; + u32 reserved[4]; +}; + +struct OrbisNgs2GeomAttribute { + float pitchRatio; + float aLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS * ORBIS_NGS2_MAX_VOICE_CHANNELS]; + + OrbisNgs2GeomA3dAttribute a3dAttrib; + u32 reserved[4]; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_impl.cpp b/src/core/libraries/ngs2/ngs2_impl.cpp index b358a05f7..1248f76d7 100644 --- a/src/core/libraries/ngs2/ngs2_impl.cpp +++ b/src/core/libraries/ngs2/ngs2_impl.cpp @@ -12,153 +12,171 @@ using namespace Libraries::Kernel; namespace Libraries::Ngs2 { -s32 Ngs2::ReportInvalid(Ngs2Handle* handle, u32 handle_type) const { - uintptr_t hAddress = reinterpret_cast(handle); - switch (handle_type) { +s32 HandleReportInvalid(OrbisNgs2Handle handle, u32 handleType) { + switch (handleType) { case 1: - LOG_ERROR(Lib_Ngs2, "Invalid system handle {}", hAddress); + LOG_ERROR(Lib_Ngs2, "Invalid system handle {}", handle); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; case 2: - LOG_ERROR(Lib_Ngs2, "Invalid rack handle {}", hAddress); + LOG_ERROR(Lib_Ngs2, "Invalid rack handle {}", handle); return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; case 4: - LOG_ERROR(Lib_Ngs2, "Invalid voice handle {}", hAddress); + LOG_ERROR(Lib_Ngs2, "Invalid voice handle {}", handle); return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; case 8: - LOG_ERROR(Lib_Ngs2, "Invalid report handle {}", hAddress); + LOG_ERROR(Lib_Ngs2, "Invalid report handle {}", handle); return ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLE; default: - LOG_ERROR(Lib_Ngs2, "Invalid handle {}", hAddress); + LOG_ERROR(Lib_Ngs2, "Invalid handle {}", handle); return ORBIS_NGS2_ERROR_INVALID_HANDLE; } } -s32 Ngs2::HandleSetup(Ngs2Handle* handle, void* data, std::atomic* atomic, u32 type, - u32 flags) { - handle->dataPointer = data; - handle->atomicPtr = atomic; - handle->handleType = type; - handle->flags_unk = flags; - return ORBIS_OK; +void* MemoryClear(void* buffer, size_t size) { + return memset(buffer, 0, size); } -s32 Ngs2::HandleCleanup(Ngs2Handle* handle, u32 hType, void* dataOut) { - if (handle && handle->selfPointer == handle) { - std::atomic* tmp_atomic = handle->atomicPtr; - if (tmp_atomic && handle->handleType == hType) { - while (tmp_atomic->load() != 0) { - u32 expected = 1; - if (tmp_atomic->compare_exchange_strong(expected, 0)) { - if (dataOut) { - dataOut = handle->dataPointer; - } - // sceNgs2MemoryClear(handle, 32); - return ORBIS_OK; - } - tmp_atomic = handle->atomicPtr; - } - } - } - return this->ReportInvalid(handle, hType); -} - -s32 Ngs2::HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut) { - if (!handle) { - return this->ReportInvalid(handle, 0); - } - - if (handle->selfPointer != handle || !handle->atomicPtr || !handle->dataPointer || - (~hType & handle->handleType)) { - return this->ReportInvalid(handle, handle->handleType); - } - - std::atomic* atomic = handle->atomicPtr; - while (true) { - u32 i = atomic->load(); - if (i == 0) { - return this->ReportInvalid(handle, handle->handleType); - } - if (atomic->compare_exchange_strong(i, i + 1)) { - break; - } - } - - if (handleOut) { - handleOut = handle; +s32 StackBufferClose(StackBuffer* stackBuffer, size_t* outTotalSize) { + if (outTotalSize) { + *outTotalSize = stackBuffer->usedSize + stackBuffer->alignment; } return ORBIS_OK; } -s32 Ngs2::HandleLeave(Ngs2Handle* handle) { - std::atomic* tmp_atomic; - u32 i; - do { - tmp_atomic = handle->atomicPtr; - i = tmp_atomic->load(); - } while (!tmp_atomic->compare_exchange_strong(i, i - 1)); - return ORBIS_OK; -} +s32 StackBufferOpen(StackBuffer* stackBuffer, void* bufferStart, size_t bufferSize, + void** outBuffer, u8 flags) { + stackBuffer->top = outBuffer; + stackBuffer->base = bufferStart; + stackBuffer->size = (size_t)bufferStart; + stackBuffer->currentOffset = (size_t)bufferStart; + stackBuffer->usedSize = 0; + stackBuffer->totalSize = bufferSize; + stackBuffer->alignment = 8; // this is a fixed value + stackBuffer->flags = flags; -s32 Ngs2::StackBufferOpen(StackBuffer* buf, void* base_addr, size_t size, void** stackTop, - bool verify) { - buf->top = stackTop; - buf->base = base_addr; - buf->curr = base_addr; - buf->usedSize = 0; - buf->totalSize = size; - buf->alignment = 8; - buf->isVerifyEnabled = verify; - - if (stackTop) { - *stackTop = nullptr; + if (outBuffer != NULL) { + *outBuffer = NULL; } return ORBIS_OK; } -s32 Ngs2::StackBufferClose(StackBuffer* buf, size_t* usedSize) { - if (usedSize) { - *usedSize = buf->usedSize + buf->alignment; +s32 SystemCleanup(OrbisNgs2Handle systemHandle, OrbisNgs2ContextBufferInfo* outInfo) { + if (!systemHandle) { + return ORBIS_NGS2_ERROR_INVALID_HANDLE; } + // TODO + return ORBIS_OK; } -s32 Ngs2::SystemSetupCore(StackBuffer* buf, SystemOptions* options, Ngs2Handle** sysOut) { +s32 SystemSetupCore(StackBuffer* stackBuffer, const OrbisNgs2SystemOption* option, + SystemInternal* outSystem) { u32 maxGrainSamples = 512; u32 numGrainSamples = 256; u32 sampleRate = 48000; - if (options) { - maxGrainSamples = options->maxGrainSamples; - numGrainSamples = options->numGrainSamples; - sampleRate = options->sampleRate; + if (option) { + sampleRate = option->sampleRate; + maxGrainSamples = option->maxGrainSamples; + numGrainSamples = option->numGrainSamples; } - // Validate maxGrainSamples - if (maxGrainSamples < 64 || maxGrainSamples > 1024 || (maxGrainSamples & 0x3F) != 0) { + if (maxGrainSamples < 64 || maxGrainSamples > 1024 || (maxGrainSamples & 63) != 0) { LOG_ERROR(Lib_Ngs2, "Invalid system option (maxGrainSamples={},x64)", maxGrainSamples); return ORBIS_NGS2_ERROR_INVALID_MAX_GRAIN_SAMPLES; } - // Validate numGrainSamples - if (numGrainSamples < 64 || numGrainSamples > 1024 || (numGrainSamples & 0x3F) != 0) { + if (numGrainSamples < 64 || numGrainSamples > 1024 || (numGrainSamples & 63) != 0) { LOG_ERROR(Lib_Ngs2, "Invalid system option (numGrainSamples={},x64)", numGrainSamples); return ORBIS_NGS2_ERROR_INVALID_NUM_GRAIN_SAMPLES; } - // Validate sampleRate if (sampleRate != 11025 && sampleRate != 12000 && sampleRate != 22050 && sampleRate != 24000 && - sampleRate != 44100 && sampleRate != 48000 && sampleRate != 88200 && sampleRate != 96000) { + sampleRate != 44100 && sampleRate != 48000 && sampleRate != 88200 && sampleRate != 96000 && + sampleRate != 176400 && sampleRate != 192000) { LOG_ERROR(Lib_Ngs2, "Invalid system option(sampleRate={}:44.1/48kHz series)", sampleRate); return ORBIS_NGS2_ERROR_INVALID_SAMPLE_RATE; } - int result = ORBIS_OK; + return ORBIS_OK; +} +s32 SystemSetup(const OrbisNgs2SystemOption* option, OrbisNgs2ContextBufferInfo* hostBufferInfo, + OrbisNgs2BufferFreeHandler hostFree, OrbisNgs2Handle* outHandle) { + u8 optionFlags = 0; + StackBuffer stackBuffer; + SystemInternal setupResult; + void* systemList = NULL; + size_t requiredBufferSize = 0; + u32 result = ORBIS_NGS2_ERROR_INVALID_BUFFER_SIZE; + + if (option) { + if (option->size != 64) { + LOG_ERROR(Lib_Ngs2, "Invalid system option size ({})", option->size); + return ORBIS_NGS2_ERROR_INVALID_OPTION_SIZE; + } + optionFlags = option->flags >> 31; + } + + // Init + StackBufferOpen(&stackBuffer, NULL, 0, NULL, optionFlags); + result = SystemSetupCore(&stackBuffer, option, 0); + + if (result < 0) { + return result; + } + + StackBufferClose(&stackBuffer, &requiredBufferSize); + + // outHandle unprovided + if (!outHandle) { + hostBufferInfo->hostBuffer = NULL; + hostBufferInfo->hostBufferSize = requiredBufferSize; + MemoryClear(&hostBufferInfo->reserved, sizeof(hostBufferInfo->reserved)); + return ORBIS_OK; + } + + if (!hostBufferInfo->hostBuffer) { + LOG_ERROR(Lib_Ngs2, "Invalid system buffer address ({})", hostBufferInfo->hostBuffer); + return ORBIS_NGS2_ERROR_INVALID_BUFFER_ADDRESS; + } + + if (hostBufferInfo->hostBufferSize < requiredBufferSize) { + LOG_ERROR(Lib_Ngs2, "Invalid system buffer size ({}<{}[byte])", + hostBufferInfo->hostBufferSize, requiredBufferSize); + return ORBIS_NGS2_ERROR_INVALID_BUFFER_SIZE; + } + + // Setup + StackBufferOpen(&stackBuffer, hostBufferInfo->hostBuffer, hostBufferInfo->hostBufferSize, + &systemList, optionFlags); + result = SystemSetupCore(&stackBuffer, option, &setupResult); + + if (result < 0) { + return result; + } + + StackBufferClose(&stackBuffer, &requiredBufferSize); + + // Copy buffer results + setupResult.bufferInfo = *hostBufferInfo; + setupResult.hostFree = hostFree; // TODO + // setupResult.systemList = systemList; - return result; // Success + OrbisNgs2Handle systemHandle = setupResult.systemHandle; + if (hostBufferInfo->hostBufferSize >= requiredBufferSize) { + *outHandle = systemHandle; + return ORBIS_OK; + } + + SystemCleanup(systemHandle, 0); + + LOG_ERROR(Lib_Ngs2, "Invalid system buffer size ({}<{}[byte])", hostBufferInfo->hostBufferSize, + requiredBufferSize); + return ORBIS_NGS2_ERROR_INVALID_BUFFER_SIZE; } } // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_impl.h b/src/core/libraries/ngs2/ngs2_impl.h index fea87c51c..7be0f89cc 100644 --- a/src/core/libraries/ngs2/ngs2_impl.h +++ b/src/core/libraries/ngs2/ngs2_impl.h @@ -3,23 +3,176 @@ #pragma once -#include "ngs2.h" +#include "core/libraries/kernel/threads/pthread.h" namespace Libraries::Ngs2 { -class Ngs2 { -public: - s32 ReportInvalid(Ngs2Handle* handle, u32 handle_type) const; - s32 HandleSetup(Ngs2Handle* handle, void* data, std::atomic* atomic, u32 type, u32 flags); - s32 HandleCleanup(Ngs2Handle* handle, u32 hType, void* dataOut); - s32 HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut); - s32 HandleLeave(Ngs2Handle* handle); - s32 StackBufferOpen(StackBuffer* buf, void* base_addr, size_t size, void** stackTop, - bool verify); - s32 StackBufferClose(StackBuffer* buf, size_t* usedSize); - s32 SystemSetupCore(StackBuffer* buf, SystemOptions* options, Ngs2Handle** sysOut); +static const int ORBIS_NGS2_SYSTEM_NAME_LENGTH = 16; +static const int ORBIS_NGS2_RACK_NAME_LENGTH = 16; -private: +typedef uintptr_t OrbisNgs2Handle; + +struct OrbisNgs2ContextBufferInfo { + void* hostBuffer; + size_t hostBufferSize; + uintptr_t reserved[5]; + uintptr_t userData; }; +struct OrbisNgs2SystemOption { + size_t size; + char name[ORBIS_NGS2_SYSTEM_NAME_LENGTH]; + + u32 flags; + u32 maxGrainSamples; + u32 numGrainSamples; + u32 sampleRate; + u32 aReserved[6]; +}; + +typedef s32 (*OrbisNgs2BufferAllocHandler)(OrbisNgs2ContextBufferInfo* ioBufferInfo); +typedef s32 (*OrbisNgs2BufferFreeHandler)(OrbisNgs2ContextBufferInfo* ioBufferInfo); + +struct OrbisNgs2SystemInfo { + char name[ORBIS_NGS2_SYSTEM_NAME_LENGTH]; // 0 + + OrbisNgs2Handle systemHandle; // 16 + OrbisNgs2ContextBufferInfo bufferInfo; // 24 + + u32 uid; // 88 + u32 minGrainSamples; // 92 + u32 maxGrainSamples; // 96 + + u32 stateFlags; // 100 + u32 rackCount; // 104 + float lastRenderRatio; // 108 + s64 lastRenderTick; // 112 + s64 renderCount; // 120 + u32 sampleRate; // 128 + u32 numGrainSamples; // 132 +}; + +struct OrbisNgs2RackInfo { + char name[ORBIS_NGS2_RACK_NAME_LENGTH]; // 0 + + OrbisNgs2Handle rackHandle; // 16 + OrbisNgs2ContextBufferInfo bufferInfo; // 24 + + OrbisNgs2Handle ownerSystemHandle; // 88 + + u32 type; // 96 + u32 rackId; // 100 + u32 uid; // 104 + u32 minGrainSamples; // 108 + u32 maxGrainSamples; // 112 + u32 maxVoices; // 116 + u32 maxChannelWorks; // 120 + u32 maxInputs; // 124 + u32 maxMatrices; // 128 + u32 maxPorts; // 132 + + u32 stateFlags; // 136 + float lastProcessRatio; // 140 + u64 lastProcessTick; // 144 + u64 renderCount; // 152 + u32 activeVoiceCount; // 160 + u32 activeChannelWorkCount; // 164 +}; + +struct StackBuffer { + void** top; + void* base; + size_t size; + size_t currentOffset; + size_t usedSize; + size_t totalSize; + size_t alignment; + u8 flags; + char padding[7]; +}; + +struct SystemInternal { + // setup init + char name[ORBIS_NGS2_SYSTEM_NAME_LENGTH]; // 0 + OrbisNgs2ContextBufferInfo bufferInfo; // 16 + OrbisNgs2BufferFreeHandler hostFree; // 80 + OrbisNgs2Handle systemHandle; // 88 + void* unknown1; // 96 + void* unknown2; // 104 + OrbisNgs2Handle rackHandle; // 112 + uintptr_t* userData; // 120 + SystemInternal* systemList; // 128 + StackBuffer* stackBuffer; // 136 + OrbisNgs2SystemInfo ownerSystemInfo; // 144 + + struct rackList { + void* prev; + void* next; + void* unknown; + }; + + rackList rackListPreset; // 152 + rackList rackListNormal; // 176 + rackList rackListMaster; // 200 + + void* unknown3; // 208 + void* systemListPrev; // 216 + void* unknown4; // 224 + void* systemListNext; // 232 + void* rackFunction; // 240 + + Kernel::PthreadMutex processLock; // 248 + u32 hasProcessMutex; // 256 + u32 unknown5; // 260 + Kernel::PthreadMutex flushLock; // 264 + u32 hasFlushMutex; // 272 + u32 unknown6; // 276 + + // info + u64 lastRenderTick; // 280 + u64 renderCount; // 288 + u32 isActive; // 296 + std::atomic lockCount; // 300 + u32 uid; // 304 + u32 systemType; // 308 + + struct { + u8 isBufferValid : 1; + u8 isRendering : 1; + u8 isSorted : 1; + u8 isFlushReady : 1; + } flags; // 312 + + u16 currentMaxGrainSamples; // 316 + u16 minGrainSamples; // 318 + u16 maxGrainSamples; // 320 + u16 numGrainSamples; // 322 + u32 currentNumGrainSamples; // 324 + u32 sampleRate; // 328 + u32 currentSampleRate; // 332 + u32 rackCount; // 336 + float lastRenderRatio; // 340 + float cpuLoad; // 344 +}; + +struct HandleInternal { + HandleInternal* selfPtr; // 0 + SystemInternal* systemData; // 8 + std::atomic refCount; // 16 + u32 handleType; // 24 + u32 handleID; // 28 +}; + +s32 StackBufferClose(StackBuffer* stackBuffer, size_t* outTotalSize); +s32 StackBufferOpen(StackBuffer* stackBuffer, void* buffer, size_t bufferSize, void** outBuffer, + u8 flags); +s32 SystemSetupCore(StackBuffer* stackBuffer, const OrbisNgs2SystemOption* option, + SystemInternal* outSystem); + +s32 HandleReportInvalid(OrbisNgs2Handle handle, u32 handleType); +void* MemoryClear(void* buffer, size_t size); +s32 SystemCleanup(OrbisNgs2Handle systemHandle, OrbisNgs2ContextBufferInfo* outInfo); +s32 SystemSetup(const OrbisNgs2SystemOption* option, OrbisNgs2ContextBufferInfo* hostBufferInfo, + OrbisNgs2BufferFreeHandler hostFree, OrbisNgs2Handle* outHandle); + } // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_mastering.cpp b/src/core/libraries/ngs2/ngs2_mastering.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_mastering.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_mastering.h b/src/core/libraries/ngs2/ngs2_mastering.h new file mode 100644 index 000000000..e0ba478c3 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_mastering.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Mastering; + +struct OrbisNgs2MasteringRackOption { + OrbisNgs2RackOption rackOption; + u32 maxChannels; + u32 numPeakMeterBlocks; +}; + +struct OrbisNgs2MasteringVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + + u32 numInputChannels; + u32 flags; +}; + +struct OrbisNgs2MasteringVoiceMatrixParam { + OrbisNgs2VoiceParamHeader header; + + u32 type; + u32 numLevels; + const float* aLevel; +}; + +struct OrbisNgs2MasteringVoiceLfeParam { + OrbisNgs2VoiceParamHeader header; + + u32 enableFlag; + u32 fc; +}; + +struct OrbisNgs2MasteringVoiceLimiterParam { + OrbisNgs2VoiceParamHeader header; + + u32 enableFlag; + float threshold; +}; + +struct OrbisNgs2MasteringVoiceGainParam { + OrbisNgs2VoiceParamHeader header; + + float fbwLevel; + float lfeLevel; +}; + +struct OrbisNgs2MasteringVoiceOutputParam { + OrbisNgs2VoiceParamHeader header; + + u32 outputId; + u32 reserved; +}; + +struct OrbisNgs2MasteringVoicePeakMeterParam { + OrbisNgs2VoiceParamHeader header; + u32 enableFlag; + u32 reserved; +}; + +struct OrbisNgs2MasteringVoiceState { + OrbisNgs2VoiceState voiceState; + float limiterPeakLevel; + float limiterPressLevel; + float aInputPeakHeight[ORBIS_NGS2_MAX_VOICE_CHANNELS]; + float aOutputPeakHeight[ORBIS_NGS2_MAX_VOICE_CHANNELS]; +}; + +struct OrbisNgs2MasteringRackInfo { + OrbisNgs2RackInfo rackInfo; + u32 maxChannels; + u32 reserved; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_pan.cpp b/src/core/libraries/ngs2/ngs2_pan.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_pan.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_pan.h b/src/core/libraries/ngs2/ngs2_pan.h new file mode 100644 index 000000000..d39ec67cd --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_pan.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Pan; + +struct OrbisNgs2PanParam { + float angle; + float distance; + float fbwLevel; + float lfeLevel; +}; + +struct OrbisNgs2PanWork { + float aSpeakerAngle[ORBIS_NGS2_MAX_VOICE_CHANNELS]; + float unitAngle; + u32 numSpeakers; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_report.cpp b/src/core/libraries/ngs2/ngs2_report.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_report.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_report.h b/src/core/libraries/ngs2/ngs2_report.h new file mode 100644 index 000000000..88f6d1df0 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_report.h @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +#include // va_list + +namespace Libraries::Ngs2 { + +class Ngs2Report; + +struct OrbisNgs2ReportDataHeader { + size_t size; + OrbisNgs2Handle handle; + u32 type; + s32 result; +}; + +typedef void (*OrbisNgs2ReportHandler)(const OrbisNgs2ReportDataHeader* data, uintptr_t userData); + +struct OrbisNgs2ReportMessageData { + OrbisNgs2ReportDataHeader header; + const char* message; +}; + +struct OrbisNgs2ReportApiData { + OrbisNgs2ReportDataHeader header; + const char* functionName; + const char* format; + va_list argument; +}; + +struct OrbisNgs2ReportControlData { + OrbisNgs2ReportDataHeader header; + const OrbisNgs2VoiceParamHeader* param; +}; + +struct OrbisNgs2ReportOutputData { + OrbisNgs2ReportDataHeader header; + const OrbisNgs2RenderBufferInfo* bufferInfo; + + u32 bufferIndex; + u32 sampleRate; + u32 numGrainSamples; + u32 reserved; +}; + +struct OrbisNgs2ReportCpuLoadData { + OrbisNgs2ReportDataHeader header; + float totalRatio; + float flushRatio; + float processRatio; + float feedbackRatio; +}; + +struct OrbisNgs2ReportRenderStateData { + OrbisNgs2ReportDataHeader header; + u32 state; + u32 reserved; +}; + +struct OrbisNgs2ReportVoiceWaveformData { + OrbisNgs2ReportDataHeader header; + u32 location; + u32 waveformType; + u32 numChannels; + u32 sampleRate; + u32 numGrainSamples; + u32 reserved; + void* const* aData; +}; + +s32 PS4_SYSV_ABI sceNgs2ReportRegisterHandler(u32 reportType, OrbisNgs2ReportHandler handler, + uintptr_t userData, OrbisNgs2Handle* outHandle); + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_reverb.cpp b/src/core/libraries/ngs2/ngs2_reverb.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_reverb.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_reverb.h b/src/core/libraries/ngs2/ngs2_reverb.h new file mode 100644 index 000000000..715d7480a --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_reverb.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Reverb; + +struct OrbisNgs2ReverbRackOption { + OrbisNgs2RackOption rackOption; + u32 maxChannels; + u32 reverbSize; +}; + +struct OrbisNgs2ReverbI3DL2Param { + float wet; + float dry; + s32 room; + s32 roomHF; + u32 reflectionPattern; + float decayTime; + float decayHFRatio; + s32 reflections; + float reflectionsDelay; + s32 reverb; + float reverbDelay; + float diffusion; + float density; + float HFReference; + u32 reserve[8]; +}; + +struct OrbisNgs2ReverbVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + + u32 numInputChannels; + u32 numOutputChannels; + u32 flags; + u32 reserved; +}; + +struct OrbisNgs2ReverbVoiceI3DL2Param { + OrbisNgs2VoiceParamHeader header; + + OrbisNgs2ReverbI3DL2Param i3dl2; +}; + +struct OrbisNgs2ReverbVoiceState { + OrbisNgs2VoiceState voiceState; +}; + +struct OrbisNgs2ReverbRackInfo { + OrbisNgs2RackInfo rackInfo; + u32 maxChannels; + u32 reverbSize; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_sampler.cpp b/src/core/libraries/ngs2/ngs2_sampler.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_sampler.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_sampler.h b/src/core/libraries/ngs2/ngs2_sampler.h new file mode 100644 index 000000000..0842b9cb2 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_sampler.h @@ -0,0 +1,162 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Sampler; + +struct OrbisNgs2SamplerRackOption { + OrbisNgs2RackOption rackOption; + u32 maxChannelWorks; + u32 maxCodecCaches; + u32 maxWaveformBlocks; + u32 maxEnvelopePoints; + u32 maxFilters; + u32 maxAtrac9Decoders; + u32 maxAtrac9ChannelWorks; + u32 maxAjmAtrac9Decoders; + u32 numPeakMeterBlocks; +}; + +struct OrbisNgs2SamplerVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + + OrbisNgs2WaveformFormat format; + u32 flags; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceWaveformBlocksParam { + OrbisNgs2VoiceParamHeader header; + + const void* data; + u32 flags; + u32 numBlocks; + const OrbisNgs2WaveformBlock* aBlock; + // Blocks +}; + +struct OrbisNgs2SamplerVoiceWaveformAddressParam { + OrbisNgs2VoiceParamHeader header; + + const void* from; + const void* to; +}; + +struct OrbisNgs2SamplerVoiceWaveformFrameOffsetParam { + OrbisNgs2VoiceParamHeader header; + + u32 frameOffset; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceExitLoopParam { + OrbisNgs2VoiceParamHeader header; +}; + +struct OrbisNgs2SamplerVoicePitchParam { + OrbisNgs2VoiceParamHeader header; + + float ratio; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceEnvelopeParam { + OrbisNgs2VoiceParamHeader header; + + u32 numForwardPoints; + u32 numReleasePoints; + const OrbisNgs2EnvelopePoint* aPoint; +}; + +struct OrbisNgs2SamplerVoiceDistortionParam { + OrbisNgs2VoiceParamHeader header; + + u32 flags; + float a; + float b; + float clip; + float gate; + float wetLevel; + float dryLevel; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceUserFxParam { + OrbisNgs2VoiceParamHeader header; + + OrbisNgs2UserFxProcessHandler handler; + + uintptr_t userData0; + uintptr_t userData1; + uintptr_t userData2; +}; + +struct OrbisNgs2SamplerVoicePeakMeterParam { + OrbisNgs2VoiceParamHeader header; + + u32 enableFlag; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceFilterParam { + OrbisNgs2VoiceParamHeader header; + + u32 index; + u32 location; + u32 type; + u32 channelMask; + union { + struct { + float i0; + float i1; + float i2; + float o1; + float o2; + } direct; + struct { + float fc; + float q; + float level; + u32 reserved; + u32 reserved2; + } fcq; + } param; + u32 reserved3; +}; + +struct OrbisNgs2SamplerVoiceNumFilters { + OrbisNgs2VoiceParamHeader header; + + u32 numFilters; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceState { + OrbisNgs2VoiceState voiceState; + float envelopeHeight; + float peakHeight; + u32 reserved; + u64 numDecodedSamples; + u64 decodedDataSize; + u64 userData; + const void* waveformData; +}; + +struct OrbisNgs2SamplerRackInfo { + OrbisNgs2RackInfo rackInfo; + u32 maxChannelWorks; + u32 maxCodecCaches; + u32 maxWaveformBlocks; + u32 maxEnvelopePoints; + u32 maxFilters; + u32 maxAtrac9Decoders; + u32 maxAtrac9ChannelWorks; + u32 maxAjmAtrac9Decoders; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_submixer.cpp b/src/core/libraries/ngs2/ngs2_submixer.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_submixer.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_submixer.h b/src/core/libraries/ngs2/ngs2_submixer.h new file mode 100644 index 000000000..df2d8a835 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_submixer.h @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Submixer; + +struct OrbisNgs2SubmixerRackOption { + OrbisNgs2RackOption rackOption; + u32 maxChannels; + u32 maxEnvelopePoints; + u32 maxFilters; + u32 maxInputs; + u32 numPeakMeterBlocks; +}; + +struct OrbisNgs2SubmixerVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + u32 numIoChannels; + u32 flags; +}; + +struct OrbisNgs2SubmixerVoiceEnvelopeParam { + OrbisNgs2VoiceParamHeader header; + + u32 numForwardPoints; + u32 numReleasePoints; + const OrbisNgs2EnvelopePoint* aPoint; +}; + +struct OrbisNgs2SubmixerVoiceCompressorParam { + OrbisNgs2VoiceParamHeader header; + + u32 flags; + float threshold; + float ratio; + float knee; + float attackTime; + float releaseTime; + float level; + u32 reserved; +}; + +struct OrbisNgs2SubmixerVoiceDistortionParam { + OrbisNgs2VoiceParamHeader header; + + u32 flags; + float a; + float b; + float clip; + float gate; + float wetLevel; + float dryLevel; + u32 reserved; +}; + +struct OrbisNgs2SubmixerVoiceUserFxParam { + OrbisNgs2VoiceParamHeader header; + + OrbisNgs2UserFxProcessHandler handler; + + uintptr_t userData0; + uintptr_t userData1; + uintptr_t userData2; +}; + +struct OrbisNgs2SubmixerVoicePeakMeterParam { + OrbisNgs2VoiceParamHeader header; + + u32 enableFlag; + u32 reserved; +}; + +struct OrbisNgs2SubmixerVoiceFilterParam { + OrbisNgs2VoiceParamHeader header; + + u32 index; + u32 location; + u32 type; + u32 channelMask; + union { + struct { + float i0; + float i1; + float i2; + float o1; + float o2; + } direct; + struct { + float fc; + float q; + float level; + u32 reserved; + u32 reserved2; + } fcq; + } param; + u32 reserved3; +}; + +struct OrbisNgs2SubmixerVoiceNumFilters { + OrbisNgs2VoiceParamHeader header; + + u32 numFilters; + u32 reserved; +}; + +struct OrbisNgs2SubmixerVoiceState { + OrbisNgs2VoiceState voiceState; + float envelopeHeight; + float peakHeight; + float compressorHeight; +}; + +struct OrbisNgs2SubmixerRackInfo { + OrbisNgs2RackInfo rackInfo; + u32 maxChannels; + u32 maxEnvelopePoints; + u32 maxFilters; + u32 maxInputs; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index 91dd5b4b4..a951d5655 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -923,15 +923,16 @@ int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTr node.attribute("unlockstate").set_value("true"); } - Rtc::OrbisRtcTick trophyTimestamp; - Rtc::sceRtcGetCurrentTick(&trophyTimestamp); + auto trophyTimestamp = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); if (node.attribute("timestamp").empty()) { node.append_attribute("timestamp") = - std::to_string(trophyTimestamp.tick).c_str(); + std::to_string(trophyTimestamp).c_str(); } else { node.attribute("timestamp") - .set_value(std::to_string(trophyTimestamp.tick).c_str()); + .set_value(std::to_string(trophyTimestamp).c_str()); } std::string trophy_icon_file = "TROP"; @@ -955,15 +956,16 @@ int PS4_SYSV_ABI sceNpTrophyUnlockTrophy(OrbisNpTrophyContext context, OrbisNpTr platinum_node.attribute("unlockstate").set_value("true"); } - Rtc::OrbisRtcTick trophyTimestamp; - Rtc::sceRtcGetCurrentTick(&trophyTimestamp); + auto trophyTimestamp = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); if (platinum_node.attribute("timestamp").empty()) { platinum_node.append_attribute("timestamp") = - std::to_string(trophyTimestamp.tick).c_str(); + std::to_string(trophyTimestamp).c_str(); } else { platinum_node.attribute("timestamp") - .set_value(std::to_string(trophyTimestamp.tick).c_str()); + .set_value(std::to_string(trophyTimestamp).c_str()); } int platinum_trophy_id = diff --git a/src/core/libraries/np_trophy/trophy_ui.cpp b/src/core/libraries/np_trophy/trophy_ui.cpp index efa02e9c4..3c9f883b3 100644 --- a/src/core/libraries/np_trophy/trophy_ui.cpp +++ b/src/core/libraries/np_trophy/trophy_ui.cpp @@ -2,9 +2,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include +#include #include #include +#include #include + +#ifdef ENABLE_QT_GUI +#include +#endif + #include "common/assert.h" #include "common/config.h" #include "common/singleton.h" @@ -12,7 +20,7 @@ #include "trophy_ui.h" CMRC_DECLARE(res); - +namespace fs = std::filesystem; using namespace ImGui; namespace Libraries::NpTrophy { @@ -20,10 +28,18 @@ std::optional current_trophy_ui; std::queue trophy_queue; std::mutex queueMtx; +std::string side = "right"; + +double trophy_timer; + TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::string& trophyName, const std::string_view& rarity) : trophy_name(trophyName), trophy_type(rarity) { + side = Config::sideTrophy(); + + trophy_timer = Config::getTrophyNotificationDuration(); + if (std::filesystem::exists(trophyIconPath)) { trophy_icon = RefCountedTexture::DecodePngFile(trophyIconPath); } else { @@ -31,23 +47,61 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin fmt::UTF(trophyIconPath.u8string())); } - std::string pathString; + std::string pathString = "src/images/"; + if (trophy_type == "P") { - pathString = "src/images/platinum.png"; + pathString += "platinum.png"; } else if (trophy_type == "G") { - pathString = "src/images/gold.png"; + pathString += "gold.png"; } else if (trophy_type == "S") { - pathString = "src/images/silver.png"; + pathString += "silver.png"; } else if (trophy_type == "B") { - pathString = "src/images/bronze.png"; + pathString += "bronze.png"; + } + + const auto CustomTrophy_Dir = Common::FS::GetUserPath(Common::FS::PathType::CustomTrophy); + std::string customPath; + + if (trophy_type == "P" && fs::exists(CustomTrophy_Dir / "platinum.png")) { + customPath = (CustomTrophy_Dir / "platinum.png").string(); + } else if (trophy_type == "G" && fs::exists(CustomTrophy_Dir / "gold.png")) { + customPath = (CustomTrophy_Dir / "gold.png").string(); + } else if (trophy_type == "S" && fs::exists(CustomTrophy_Dir / "silver.png")) { + customPath = (CustomTrophy_Dir / "silver.png").string(); + } else if (trophy_type == "B" && fs::exists(CustomTrophy_Dir / "bronze.png")) { + customPath = (CustomTrophy_Dir / "bronze.png").string(); + } + + std::vector imgdata; + if (!customPath.empty()) { + std::ifstream file(customPath, std::ios::binary); + if (file) { + imgdata = std::vector(std::istreambuf_iterator(file), + std::istreambuf_iterator()); + } else { + LOG_ERROR(Lib_NpTrophy, "Could not open custom file for trophy in {}", customPath); + } + } else { + auto resource = cmrc::res::get_filesystem(); + auto file = resource.open(pathString); + imgdata = std::vector(file.begin(), file.end()); } - auto resource = cmrc::res::get_filesystem(); - auto file = resource.open(pathString); - std::vector imgdata(file.begin(), file.end()); trophy_type_icon = RefCountedTexture::DecodePngTexture(imgdata); AddLayer(this); + +#ifdef ENABLE_QT_GUI + QString musicPathWav = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.wav"); + QString musicPathMp3 = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.mp3"); + if (fs::exists(musicPathWav.toStdString())) { + BackgroundMusicPlayer::getInstance().setVolume(100); + BackgroundMusicPlayer::getInstance().playMusic(musicPathWav, false); + } else if (fs::exists(musicPathMp3.toStdString())) { + BackgroundMusicPlayer::getInstance().setVolume(100); + BackgroundMusicPlayer::getInstance().playMusic(musicPathMp3, false); + } +#endif } TrophyUI::~TrophyUI() { @@ -58,36 +112,94 @@ void TrophyUI::Finish() { RemoveLayer(this); } +float fade_opacity = 0.0f; // Initial opacity (invisible) +ImVec2 start_pos = ImVec2(1280.0f, 50.0f); // Starts off screen, right +ImVec2 target_pos = ImVec2(0.0f, 50.0f); // Final position +float animation_duration = 0.5f; // Animation duration +float elapsed_time = 0.0f; // Animation time +float fade_out_duration = 0.5f; // Final fade duration + void TrophyUI::Draw() { const auto& io = GetIO(); - float AdjustWidth = io.DisplaySize.x / 1280; - float AdjustHeight = io.DisplaySize.y / 720; + float AdjustWidth = io.DisplaySize.x / 1920; + float AdjustHeight = io.DisplaySize.y / 1080; const ImVec2 window_size{ std::min(io.DisplaySize.x, (350 * AdjustWidth)), std::min(io.DisplaySize.y, (70 * AdjustHeight)), }; + elapsed_time += io.DeltaTime; + float progress = std::min(elapsed_time / animation_duration, 1.0f); + + float final_pos_x, start_x; + float final_pos_y, start_y; + + if (side == "top") { + start_x = (io.DisplaySize.x - window_size.x) * 0.5f; + start_y = -window_size.y; + final_pos_x = start_x; + final_pos_y = 20 * AdjustHeight; + } else if (side == "left") { + start_x = -window_size.x; + start_y = 50 * AdjustHeight; + final_pos_x = 20 * AdjustWidth; + final_pos_y = start_y; + } else if (side == "right") { + start_x = io.DisplaySize.x; + start_y = 50 * AdjustHeight; + final_pos_x = io.DisplaySize.x - window_size.x - 20 * AdjustWidth; + final_pos_y = start_y; + } else if (side == "bottom") { + start_x = (io.DisplaySize.x - window_size.x) * 0.5f; + start_y = io.DisplaySize.y; + final_pos_x = start_x; + final_pos_y = io.DisplaySize.y - window_size.y - 20 * AdjustHeight; + } + + ImVec2 current_pos = ImVec2(start_x + (final_pos_x - start_x) * progress, + start_y + (final_pos_y - start_y) * progress); + + trophy_timer -= io.DeltaTime; + + ImGui::SetNextWindowPos(current_pos); + + // If the remaining time of the trophy is less than or equal to 1 second, the fade-out begins. + if (trophy_timer <= 1.0f) { + float fade_out_time = 1.0f - (trophy_timer / 1.0f); + fade_opacity = 1.0f - fade_out_time; + } else { + // Fade in , 0 to 1 + fade_opacity = progress; + } + + fade_opacity = std::max(0.0f, std::min(fade_opacity, 1.0f)); + SetNextWindowSize(window_size); + SetNextWindowPos(current_pos); SetNextWindowCollapsed(false); - SetNextWindowPos(ImVec2(io.DisplaySize.x - (370 * AdjustWidth), (50 * AdjustHeight))); KeepNavHighlight(); + PushStyleVar(ImGuiStyleVar_Alpha, fade_opacity); + if (Begin("Trophy Window", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoInputs)) { + + // Displays the trophy icon if (trophy_type_icon) { SetCursorPosY((window_size.y * 0.5f) - (25 * AdjustHeight)); Image(trophy_type_icon.GetTexture().im_id, ImVec2((50 * AdjustWidth), (50 * AdjustHeight))); ImGui::SameLine(); } else { - // placeholder + // Placeholder const auto pos = GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + ImVec2{50.0f * AdjustHeight}, GetColorU32(ImVec4{0.7f})); ImGui::Indent(60); } + // Displays the name of the trophy const std::string combinedString = "Trophy earned!\n%s" + trophy_name; const float wrap_width = CalcWrapWidthForPos(GetCursorScreenPos(), (window_size.x - (60 * AdjustWidth))); @@ -104,15 +216,23 @@ void TrophyUI::Draw() { const float text_height = ImGui::CalcTextSize(combinedString.c_str()).y; SetCursorPosY((window_size.y - text_height) * 0.5); } + + if (side == "top" || side == "bottom") { + float text_width = ImGui::CalcTextSize(trophy_name.c_str()).x; + float centered_x = (window_size.x - text_width) * 0.5f; + ImGui::SetCursorPosX(std::max(centered_x, 10.0f * AdjustWidth)); + } + ImGui::PushTextWrapPos(window_size.x - (60 * AdjustWidth)); TextWrapped("Trophy earned!\n%s", trophy_name.c_str()); ImGui::SameLine(window_size.x - (60 * AdjustWidth)); + // Displays the trophy icon if (trophy_icon) { SetCursorPosY((window_size.y * 0.5f) - (25 * AdjustHeight)); Image(trophy_icon.GetTexture().im_id, ImVec2((50 * AdjustWidth), (50 * AdjustHeight))); } else { - // placeholder + // Placeholder const auto pos = GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled(pos, pos + ImVec2{50.0f * AdjustHeight}, GetColorU32(ImVec4{0.7f})); @@ -120,7 +240,8 @@ void TrophyUI::Draw() { } End(); - trophy_timer -= io.DeltaTime; + PopStyleVar(); + if (trophy_timer <= 0) { std::lock_guard lock(queueMtx); if (!trophy_queue.empty()) { @@ -141,13 +262,27 @@ void AddTrophyToQueue(const std::filesystem::path& trophyIconPath, const std::st if (Config::getisTrophyPopupDisabled()) { return; } else if (current_trophy_ui.has_value()) { - TrophyInfo new_trophy; - new_trophy.trophy_icon_path = trophyIconPath; - new_trophy.trophy_name = trophyName; - new_trophy.trophy_type = rarity; - trophy_queue.push(new_trophy); - } else { - current_trophy_ui.emplace(trophyIconPath, trophyName, rarity); + current_trophy_ui.reset(); + } + + TrophyInfo new_trophy; + new_trophy.trophy_icon_path = trophyIconPath; + new_trophy.trophy_name = trophyName; + new_trophy.trophy_type = rarity; + trophy_queue.push(new_trophy); + + if (!current_trophy_ui.has_value()) { +#ifdef ENABLE_QT_GUI + BackgroundMusicPlayer::getInstance().stopMusic(); +#endif + // Resetting the animation for the next trophy + elapsed_time = 0.0f; // Resetting animation time + fade_opacity = 0.0f; // Starts invisible + start_pos = ImVec2(1280.0f, 50.0f); // Starts off screen, right + TrophyInfo next_trophy = trophy_queue.front(); + trophy_queue.pop(); + current_trophy_ui.emplace(next_trophy.trophy_icon_path, next_trophy.trophy_name, + next_trophy.trophy_type); } } diff --git a/src/core/libraries/np_trophy/trophy_ui.h b/src/core/libraries/np_trophy/trophy_ui.h index 16e707059..553c99f6f 100644 --- a/src/core/libraries/np_trophy/trophy_ui.h +++ b/src/core/libraries/np_trophy/trophy_ui.h @@ -28,7 +28,6 @@ public: private: std::string trophy_name; std::string_view trophy_type; - float trophy_timer = 5.0f; ImGui::RefCountedTexture trophy_icon; ImGui::RefCountedTexture trophy_type_icon; }; diff --git a/src/core/libraries/system/sysmodule.cpp b/src/core/libraries/system/sysmodule.cpp index 350f1317b..6c73764f2 100644 --- a/src/core/libraries/system/sysmodule.cpp +++ b/src/core/libraries/system/sysmodule.cpp @@ -7,6 +7,7 @@ #include "common/logging/log.h" #include "core/libraries/error_codes.h" +#include "core/libraries/kernel/process.h" #include "core/libraries/libs.h" #include "core/libraries/system/sysmodule.h" #include "core/libraries/system/system_error.h" @@ -18,9 +19,12 @@ int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal() { return ORBIS_OK; } -int PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind() { +s32 PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(VAddr addr, s32 flags, void* info) { LOG_ERROR(Lib_SysModule, "(STUBBED) called"); - return ORBIS_OK; + Kernel::OrbisModuleInfoForUnwind module_info; + module_info.st_size = 0x130; + s32 res = Kernel::sceKernelGetModuleInfoForUnwind(addr, flags, &module_info); + return res; } int PS4_SYSV_ABI sceSysmoduleIsCalledFromSysModule() { diff --git a/src/core/libraries/system/sysmodule.h b/src/core/libraries/system/sysmodule.h index 3630fb58e..dfbdca162 100644 --- a/src/core/libraries/system/sysmodule.h +++ b/src/core/libraries/system/sysmodule.h @@ -152,7 +152,7 @@ enum class OrbisSysModuleInternal : u32 { }; int PS4_SYSV_ABI sceSysmoduleGetModuleHandleInternal(); -int PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(); +s32 PS4_SYSV_ABI sceSysmoduleGetModuleInfoForUnwind(VAddr addr, s32 flags, void* info); int PS4_SYSV_ABI sceSysmoduleIsCalledFromSysModule(); int PS4_SYSV_ABI sceSysmoduleIsCameraPreloaded(); int PS4_SYSV_ABI sceSysmoduleIsLoaded(OrbisSysModule id); diff --git a/src/core/libraries/system/systemservice.cpp b/src/core/libraries/system/systemservice.cpp index a67b9a2fc..9dc9dd781 100644 --- a/src/core/libraries/system/systemservice.cpp +++ b/src/core/libraries/system/systemservice.cpp @@ -1893,7 +1893,7 @@ int PS4_SYSV_ABI sceSystemServiceNavigateToGoHome() { s32 PS4_SYSV_ABI sceSystemServiceParamGetInt(OrbisSystemServiceParamId param_id, int* value) { // TODO this probably should be stored in config for UI configuration - LOG_INFO(Lib_SystemService, "called param_id {}", u32(param_id)); + LOG_DEBUG(Lib_SystemService, "called param_id {}", u32(param_id)); if (value == nullptr) { LOG_ERROR(Lib_SystemService, "value is null"); return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER; diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index e1f9f75e8..b4bf189ea 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -1043,7 +1043,7 @@ int PS4_SYSV_ABI sceUserServiceGetTraditionalChineseInputType() { s32 PS4_SYSV_ABI sceUserServiceGetUserColor(int user_id, OrbisUserServiceUserColor* color) { // TODO fix me better - LOG_INFO(Lib_UserService, "called user_id = {}", user_id); + LOG_DEBUG(Lib_UserService, "called user_id = {}", user_id); if (color == nullptr) { LOG_ERROR(Lib_UserService, "color is null"); return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT; @@ -1068,7 +1068,7 @@ int PS4_SYSV_ABI sceUserServiceGetUserGroupNum() { } s32 PS4_SYSV_ABI sceUserServiceGetUserName(int user_id, char* user_name, std::size_t size) { - LOG_INFO(Lib_UserService, "called user_id = {} ,size = {} ", user_id, size); + LOG_DEBUG(Lib_UserService, "called user_id = {} ,size = {} ", user_id, size); if (user_name == nullptr) { LOG_ERROR(Lib_UserService, "user_name is null"); return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT; diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index 0f832910c..8f725e549 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -63,6 +63,7 @@ void VideoOutDriver::Close(s32 handle) { main_port.is_open = false; main_port.flip_rate = 0; + main_port.prev_index = -1; ASSERT(main_port.flip_events.empty()); } @@ -133,6 +134,10 @@ int VideoOutDriver::RegisterBuffers(VideoOutPort* port, s32 startIndex, void* co .address_right = 0, }; + // Reset flip label also when registering buffer + port->buffer_labels[startIndex + i] = 0; + port->SignalVoLabel(); + presenter->RegisterVideoOutSurface(group, address); LOG_INFO(Lib_VideoOut, "buffers[{}] = {:#x}", i + startIndex, address); } @@ -160,11 +165,8 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) { } void VideoOutDriver::Flip(const Request& req) { - // Whatever the game is rendering show splash if it is active - if (!presenter->ShowSplash(req.frame)) { - // Present the frame. - presenter->Present(req.frame); - } + // Present the frame. + presenter->Present(req.frame); // Update flip status. auto* port = req.port; @@ -193,17 +195,16 @@ void VideoOutDriver::Flip(const Request& req) { } } - // Reset flip label - if (req.index != -1) { - port->buffer_labels[req.index] = 0; + // Reset prev flip label + if (port->prev_index != -1) { + port->buffer_labels[port->prev_index] = 0; port->SignalVoLabel(); } + // save to prev buf index + port->prev_index = req.index; } void VideoOutDriver::DrawBlankFrame() { - if (presenter->ShowSplash(nullptr)) { - return; - } const auto empty_frame = presenter->PrepareBlankFrame(false); presenter->Present(empty_frame); } diff --git a/src/core/libraries/videoout/driver.h b/src/core/libraries/videoout/driver.h index e57b189b5..3b0df43b9 100644 --- a/src/core/libraries/videoout/driver.h +++ b/src/core/libraries/videoout/driver.h @@ -32,6 +32,7 @@ struct VideoOutPort { std::condition_variable vo_cv; std::condition_variable vblank_cv; int flip_rate = 0; + int prev_index = -1; bool is_open = false; bool is_mode_changing = false; // Used to prevent flip during mode change diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 91616a5ae..219d0886b 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -295,10 +295,16 @@ s32 PS4_SYSV_ABI sceVideoOutUnregisterBuffers(s32 handle, s32 attributeIndex) { return driver->UnregisterBuffers(port, attributeIndex); } -void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr) { +s32 PS4_SYSV_ABI sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr) { + if (label_addr == nullptr) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; + } auto* port = driver->GetPort(handle); - ASSERT(port); + if (!port) { + return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE; + } *label_addr = reinterpret_cast(port->buffer_labels.data()); + return 16; } s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** unk) { @@ -360,7 +366,7 @@ s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettin return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE; } - presenter->GetGammaRef() = settings->gamma; + presenter->GetPPSettingsRef().gamma = settings->gamma; return ORBIS_OK; } @@ -430,6 +436,8 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { sceVideoOutIsFlipPending); LIB_FUNCTION("N5KDtkIjjJ4", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutUnregisterBuffers); + LIB_FUNCTION("OcQybQejHEY", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, + sceVideoOutGetBufferLabelAddress); LIB_FUNCTION("uquVH4-Du78", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutClose); LIB_FUNCTION("1FZBKy8HeNU", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetVblankStatus); @@ -460,6 +468,8 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { sceVideoOutSetBufferAttribute); LIB_FUNCTION("w3BY+tAEiQY", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutRegisterBuffers); + LIB_FUNCTION("OcQybQejHEY", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, + sceVideoOutGetBufferLabelAddress); LIB_FUNCTION("U46NwOiJpys", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutSubmitFlip); LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutGetFlipStatus); diff --git a/src/core/libraries/videoout/video_out.h b/src/core/libraries/videoout/video_out.h index ad8ce9ed2..f3e661de4 100644 --- a/src/core/libraries/videoout/video_out.h +++ b/src/core/libraries/videoout/video_out.h @@ -118,6 +118,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata); s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses, s32 bufferNum, const BufferAttribute* attribute); +s32 PS4_SYSV_ABI sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr); s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate); s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle); s32 PS4_SYSV_ABI sceVideoOutWaitVblank(s32 handle); @@ -133,7 +134,6 @@ s32 PS4_SYSV_ABI sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* sett s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings); // Internal system functions -void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr); s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** unk); void RegisterLib(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 2461edcb2..18ae62f4b 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -139,6 +139,35 @@ s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) { return m_modules.size() - 1; } +s32 Linker::LoadAndStartModule(const std::filesystem::path& path, u64 args, const void* argp, + int* pRes) { + u32 handle = FindByName(path); + if (handle != -1) { + return handle; + } + handle = LoadModule(path, true); + if (handle == -1) { + return -1; + } + auto* module = GetModule(handle); + RelocateAnyImports(module); + + // If the new module has a TLS image, trigger its load when TlsGetAddr is called. + if (module->tls.image_size != 0) { + AdvanceGenerationCounter(); + } + + // Retrieve and verify proc param according to libkernel. + u64* param = module->GetProcParam(); + ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]); + s32 ret = module->Start(args, argp, param); + if (pRes) { + *pRes = ret; + } + + return handle; +} + Module* Linker::FindByAddress(VAddr address) { for (auto& module : m_modules) { const VAddr base = module->GetBaseAddress(); diff --git a/src/core/linker.h b/src/core/linker.h index 9c07400c4..63dfc37e8 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -144,6 +144,8 @@ public: void FreeTlsForNonPrimaryThread(void* pointer); s32 LoadModule(const std::filesystem::path& elf_name, bool is_dynamic = false); + s32 LoadAndStartModule(const std::filesystem::path& path, u64 args, const void* argp, + int* pRes); Module* FindByAddress(VAddr address); void Relocate(Module* module); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 98d587e00..8b108a654 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -38,6 +38,16 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1 bool use_extended_mem2) { const bool is_neo = ::Libraries::Kernel::sceKernelIsNeoMode(); auto total_size = is_neo ? SCE_KERNEL_TOTAL_MEM_PRO : SCE_KERNEL_TOTAL_MEM; + if (Config::isDevKitConsole()) { + const auto old_size = total_size; + // Assuming 2gb is neo for now, will need to link it with sceKernelIsDevKit + total_size += is_neo ? 2_GB : 768_MB; + LOG_WARNING(Kernel_Vmm, + "Config::isDevKitConsole is enabled! Added additional {:s} of direct memory.", + is_neo ? "2 GB" : "768 MB"); + LOG_WARNING(Kernel_Vmm, "Old Direct Size: {:#x} -> New Direct Size: {:#x}", old_size, + total_size); + } if (!use_extended_mem1 && is_neo) { total_size -= 256_MB; } diff --git a/src/core/module.cpp b/src/core/module.cpp index 70afb932c..a18c1141a 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -94,7 +94,7 @@ Module::Module(Core::MemoryManager* memory_, const std::filesystem::path& file_, Module::~Module() = default; -s32 Module::Start(size_t args, const void* argp, void* param) { +s32 Module::Start(u64 args, const void* argp, void* param) { LOG_INFO(Core_Linker, "Module started : {}", name); const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress(); return ExecuteGuest(reinterpret_cast(addr), args, argp, param); diff --git a/src/core/module.h b/src/core/module.h index 630c5d583..320680485 100644 --- a/src/core/module.h +++ b/src/core/module.h @@ -203,7 +203,7 @@ public: return (rela_bits[index >> 3] >> (index & 7)) & 1; } - s32 Start(size_t args, const void* argp, void* param); + s32 Start(u64 args, const void* argp, void* param); void LoadModuleToMemory(u32& max_tls_index); void LoadDynamicInfo(); void LoadSymbols(); diff --git a/src/emulator.cpp b/src/emulator.cpp index 68c1e332c..5f94f008a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -24,7 +24,6 @@ #include "common/singleton.h" #include "common/version.h" #include "core/file_format/psf.h" -#include "core/file_format/splash.h" #include "core/file_format/trp.h" #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" @@ -81,7 +80,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/sce_sys/param.sfo"); + if (!std::filesystem::exists(param_sfo_path) || !Config::getSeparateLogFilesEnabled()) { + Common::Log::Initialize(); + Common::Log::Start(); + } + if (std::filesystem::exists(param_sfo_path)) { auto* param_sfo = Common::Singleton::Instance(); const bool success = param_sfo->Open(param_sfo_path); @@ -117,10 +121,8 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/sce_sys/pic1.png"); if (std::filesystem::exists(pic1_path)) { - auto* splash = Common::Singleton::Instance(); - if (!splash->IsLoaded()) { - if (!splash->Open(pic1_path)) { - LOG_ERROR(Loader, "Game splash: unable to open file"); - } - } + game_info.splash_path = pic1_path; } game_info.initialized = true; @@ -292,13 +289,12 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector ModulesToLoad{ + constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {"libSceUlt.sprx", nullptr}, {"libSceJson.sprx", nullptr}, {"libSceJson2.sprx", nullptr}, {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal}, - {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, {"libSceCesCs.sprx", nullptr}, {"libSceFont.sprx", nullptr}, diff --git a/src/images/KBM.png b/src/images/KBM.png new file mode 100644 index 000000000..37f52d549 Binary files /dev/null and b/src/images/KBM.png differ diff --git a/src/images/controller_icon.png b/src/images/controller_icon.png index 40c92a89b..0d5556329 100644 Binary files a/src/images/controller_icon.png and b/src/images/controller_icon.png differ diff --git a/src/images/fullscreen_icon.png b/src/images/fullscreen_icon.png new file mode 100644 index 000000000..719ffe4a1 Binary files /dev/null and b/src/images/fullscreen_icon.png differ diff --git a/src/images/pause_icon.png b/src/images/pause_icon.png index 5375689b7..86bbc6acb 100644 Binary files a/src/images/pause_icon.png and b/src/images/pause_icon.png differ diff --git a/src/images/play_icon.png b/src/images/play_icon.png index 2815be39d..d50d404b7 100644 Binary files a/src/images/play_icon.png and b/src/images/play_icon.png differ diff --git a/src/images/refresh_icon.png b/src/images/refresh_icon.png deleted file mode 100644 index 00fe69c20..000000000 Binary files a/src/images/refresh_icon.png and /dev/null differ diff --git a/src/images/refreshlist_icon.png b/src/images/refreshlist_icon.png new file mode 100644 index 000000000..7de6685b2 Binary files /dev/null and b/src/images/refreshlist_icon.png differ diff --git a/src/images/restart_game_icon.png b/src/images/restart_game_icon.png new file mode 100644 index 000000000..1e549e101 Binary files /dev/null and b/src/images/restart_game_icon.png differ diff --git a/src/images/settings_icon.png b/src/images/settings_icon.png index c88cd7a6f..81127bfa3 100644 Binary files a/src/images/settings_icon.png and b/src/images/settings_icon.png differ diff --git a/src/images/stop_icon.png b/src/images/stop_icon.png index 74c615f65..55b6b01c7 100644 Binary files a/src/images/stop_icon.png and b/src/images/stop_icon.png differ diff --git a/src/images/trophy_icon.png b/src/images/trophy_icon.png new file mode 100644 index 000000000..559e7dbb2 Binary files /dev/null and b/src/images/trophy_icon.png differ diff --git a/src/imgui/renderer/texture_manager.cpp b/src/imgui/renderer/texture_manager.cpp index e217cd130..49f912a92 100644 --- a/src/imgui/renderer/texture_manager.cpp +++ b/src/imgui/renderer/texture_manager.cpp @@ -175,6 +175,7 @@ void WorkerLoop() { auto texture = Vulkan::UploadTexture(pixels, vk::Format::eR8G8B8A8Unorm, width, height, width * height * 4 * sizeof(stbi_uc)); + stbi_image_free((void*)pixels); core->upload_data = texture; core->width = width; diff --git a/src/qt_gui/background_music_player.cpp b/src/qt_gui/background_music_player.cpp index a40c5bfae..e2b7177b3 100644 --- a/src/qt_gui/background_music_player.cpp +++ b/src/qt_gui/background_music_player.cpp @@ -7,7 +7,6 @@ BackgroundMusicPlayer::BackgroundMusicPlayer(QObject* parent) : QObject(parent) m_mediaPlayer = new QMediaPlayer(this); m_audioOutput = new QAudioOutput(this); m_mediaPlayer->setAudioOutput(m_audioOutput); - m_mediaPlayer->setLoops(QMediaPlayer::Infinite); } void BackgroundMusicPlayer::setVolume(int volume) { @@ -16,7 +15,7 @@ void BackgroundMusicPlayer::setVolume(int volume) { m_audioOutput->setVolume(linearVolume); } -void BackgroundMusicPlayer::playMusic(const QString& snd0path) { +void BackgroundMusicPlayer::playMusic(const QString& snd0path, bool loops) { if (snd0path.isEmpty()) { stopMusic(); return; @@ -28,6 +27,12 @@ void BackgroundMusicPlayer::playMusic(const QString& snd0path) { return; } + if (loops) { + m_mediaPlayer->setLoops(QMediaPlayer::Infinite); + } else { + m_mediaPlayer->setLoops(1); + } + m_currentMusic = newMusic; m_mediaPlayer->setSource(newMusic); m_mediaPlayer->play(); @@ -35,4 +40,5 @@ void BackgroundMusicPlayer::playMusic(const QString& snd0path) { void BackgroundMusicPlayer::stopMusic() { m_mediaPlayer->stop(); + m_mediaPlayer->setSource(QUrl("")); } diff --git a/src/qt_gui/background_music_player.h b/src/qt_gui/background_music_player.h index 6d70fe68c..078710a01 100644 --- a/src/qt_gui/background_music_player.h +++ b/src/qt_gui/background_music_player.h @@ -17,7 +17,7 @@ public: } void setVolume(int volume); - void playMusic(const QString& snd0path); + void playMusic(const QString& snd0path, bool loops = true); void stopMusic(); private: diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index bf7877f18..7239affd5 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -1082,7 +1082,11 @@ void CheatsPatches::addCheatsToLayout(const QJsonArray& modsArray, const QJsonAr QLabel* creditsLabel = new QLabel(); QString creditsText = tr("Author: "); if (!creditsArray.isEmpty()) { - creditsText += creditsArray[0].toString(); + QStringList authors; + for (const QJsonValue& credit : creditsArray) { + authors << credit.toString(); + } + creditsText += authors.join(", "); } creditsLabel->setText(creditsText); creditsLabel->setAlignment(Qt::AlignLeft); diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index 5cae6c41a..e73a66a71 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -247,7 +247,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate, bool latest_isWIP = latestRev.endsWith("WIP", Qt::CaseInsensitive); if (current_isWIP && !latest_isWIP) { } else { - QTextEdit* textField = new QTextEdit(this); + QTextBrowser* textField = new QTextBrowser(this); textField->setReadOnly(true); textField->setFixedWidth(500); textField->setFixedHeight(200); @@ -349,8 +349,28 @@ void CheckUpdate::requestChangelog(const QString& currentRev, const QString& lat } // Update the text field with the changelog - QTextEdit* textField = findChild(); + QTextBrowser* textField = findChild(); if (textField) { + QRegularExpression re("\\(\\#(\\d+)\\)"); + QString newChanges; + int lastIndex = 0; + QRegularExpressionMatchIterator i = re.globalMatch(changes); + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + newChanges += changes.mid(lastIndex, match.capturedStart() - lastIndex); + QString num = match.captured(1); + newChanges += + QString( + "(#%1)") + .arg(num); + lastIndex = match.capturedEnd(); + } + + newChanges += changes.mid(lastIndex); + changes = newChanges; + + textField->setOpenExternalLinks(true); textField->setHtml("

" + tr("Changes") + ":

" + changes); } diff --git a/src/qt_gui/compatibility_info.cpp b/src/qt_gui/compatibility_info.cpp index 443d56a20..da32f24ae 100644 --- a/src/qt_gui/compatibility_info.cpp +++ b/src/qt_gui/compatibility_info.cpp @@ -78,25 +78,38 @@ void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent, bool f CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::string& serial) { QString title_id = QString::fromStdString(serial); if (m_compatibility_database.contains(title_id)) { - { - QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject(); - for (int os_int = 0; os_int != static_cast(OSType::Last); os_int++) { - QString os_string = OSTypeToString.at(static_cast(os_int)); - if (compatibility_obj.contains(os_string)) { - QJsonObject compatibility_entry_obj = compatibility_obj[os_string].toObject(); - CompatibilityEntry compatibility_entry{ - LabelToCompatStatus.at(compatibility_entry_obj["status"].toString()), - compatibility_entry_obj["version"].toString(), - QDateTime::fromString(compatibility_entry_obj["last_tested"].toString(), - Qt::ISODate), - compatibility_entry_obj["url"].toString(), - compatibility_entry_obj["issue_number"].toString()}; - return compatibility_entry; - } - } + QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject(); + + // Set current_os automatically + QString current_os; +#ifdef Q_OS_WIN + current_os = "os-windows"; +#elif defined(Q_OS_MAC) + current_os = "os-macOS"; +#elif defined(Q_OS_LINUX) + current_os = "os-linux"; +#else + current_os = "os-unknown"; +#endif + // Check if the game is compatible with the current operating system + if (compatibility_obj.contains(current_os)) { + QJsonObject compatibility_entry_obj = compatibility_obj[current_os].toObject(); + CompatibilityEntry compatibility_entry{ + LabelToCompatStatus.at(compatibility_entry_obj["status"].toString()), + compatibility_entry_obj["version"].toString(), + QDateTime::fromString(compatibility_entry_obj["last_tested"].toString(), + Qt::ISODate), + compatibility_entry_obj["url"].toString(), + compatibility_entry_obj["issue_number"].toString()}; + return compatibility_entry; + } else { + // If there is no entry for the current operating system, return "Unknown" + return CompatibilityEntry{CompatibilityStatus::Unknown, "", + QDateTime::currentDateTime(), "", 0}; } } + // If title not found, return "Unknown" return CompatibilityEntry{CompatibilityStatus::Unknown, "", QDateTime::currentDateTime(), "", 0}; } @@ -200,4 +213,4 @@ const QString CompatibilityInfoClass::GetCompatStatusString(const CompatibilityS default: return tr("Unknown"); } -} \ No newline at end of file +} diff --git a/src/qt_gui/control_settings.cpp b/src/qt_gui/control_settings.cpp index 820a490a0..4206e45b8 100644 --- a/src/qt_gui/control_settings.cpp +++ b/src/qt_gui/control_settings.cpp @@ -28,6 +28,11 @@ ControlSettings::ControlSettings(std::shared_ptr game_info_get, Q } }); + ui->buttonBox->button(QDialogButtonBox::Save)->setText(tr("Save")); + ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Apply")); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults")); + ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); connect(ui->ProfileComboBox, &QComboBox::currentTextChanged, this, [this] { @@ -100,8 +105,8 @@ void ControlSettings::SaveControllerConfig(bool CloseOnSave) { if (count_axis_left_x > 1 | count_axis_left_y > 1 | count_axis_right_x > 1 | count_axis_right_y > 1) { QMessageBox::StandardButton nosave; - nosave = QMessageBox::information(this, "Unable to Save", - "Cannot bind axis values more than once"); + nosave = QMessageBox::information(this, tr("Unable to Save"), + tr("Cannot bind axis values more than once")); return; } diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index 947f25d84..19b6adc1e 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -20,7 +20,7 @@ void ScanDirectoryRecursively(const QString& dir, QStringList& filePaths, int cu QFileInfoList entries = directory.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (const auto& entry : entries) { - if (entry.fileName().endsWith("-UPDATE")) { + if (entry.fileName().endsWith("-UPDATE") || entry.fileName().endsWith("-patch")) { continue; } diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h index 0759ccc52..09e5a4557 100644 --- a/src/qt_gui/game_info.h +++ b/src/qt_gui/game_info.h @@ -34,6 +34,12 @@ public: game_update_path += "-UPDATE"; if (std::filesystem::exists(game_update_path / "sce_sys" / "param.sfo")) { sce_folder_path = game_update_path / "sce_sys" / "param.sfo"; + } else { + game_update_path = filePath; + game_update_path += "-patch"; + if (std::filesystem::exists(game_update_path / "sce_sys" / "param.sfo")) { + sce_folder_path = game_update_path / "sce_sys" / "param.sfo"; + } } PSF psf; diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 1a059a850..0cc0e48dc 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -10,8 +10,10 @@ #include #include +#include #include "cheats_patches.h" #include "common/config.h" +#include "common/path_util.h" #include "common/version.h" #include "compatibility_info.h" #include "game_info.h" @@ -25,12 +27,11 @@ #include #include #endif -#include "common/path_util.h" class GuiContextMenus : public QObject { Q_OBJECT public: - void RequestGameMenu(const QPoint& pos, QVector m_games, + void RequestGameMenu(const QPoint& pos, QVector& m_games, std::shared_ptr m_compat_info, QTableWidget* widget, bool isList) { QPoint global_pos = widget->viewport()->mapToGlobal(pos); @@ -97,11 +98,13 @@ public: QAction* deleteUpdate = new QAction(tr("Delete Update"), widget); QAction* deleteSaveData = new QAction(tr("Delete Save Data"), widget); QAction* deleteDLC = new QAction(tr("Delete DLC"), widget); + QAction* deleteTrophy = new QAction(tr("Delete Trophy"), widget); deleteMenu->addAction(deleteGame); deleteMenu->addAction(deleteUpdate); deleteMenu->addAction(deleteSaveData); deleteMenu->addAction(deleteDLC); + deleteMenu->addAction(deleteTrophy); menu.addMenu(deleteMenu); @@ -113,7 +116,9 @@ public: compatibilityMenu->addAction(updateCompatibility); compatibilityMenu->addAction(viewCompatibilityReport); - compatibilityMenu->addAction(submitCompatibilityReport); + if (Common::isRelease) { + compatibilityMenu->addAction(submitCompatibilityReport); + } menu.addMenu(compatibilityMenu); @@ -137,8 +142,12 @@ public: Common::FS::PathToQString(open_update_path, m_games[itemID].path); open_update_path += "-UPDATE"; if (!std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) { - QMessageBox::critical(nullptr, tr("Error"), - QString(tr("This game has no update folder to open!"))); + Common::FS::PathToQString(open_update_path, m_games[itemID].path); + open_update_path += "-patch"; + if (!std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) { + QMessageBox::critical(nullptr, tr("Error"), + QString(tr("This game has no update folder to open!"))); + } } else { QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path)); } @@ -155,10 +164,54 @@ public: } if (selected == openLogFolder) { - QString userPath; - Common::FS::PathToQString(userPath, - Common::FS::GetUserPath(Common::FS::PathType::UserDir)); - QDesktopServices::openUrl(QUrl::fromLocalFile(userPath + "/log")); + QString logPath; + Common::FS::PathToQString(logPath, + Common::FS::GetUserPath(Common::FS::PathType::LogDir)); + if (!Config::getSeparateLogFilesEnabled()) { + QDesktopServices::openUrl(QUrl::fromLocalFile(logPath)); + } else { + QString fileName = QString::fromStdString(m_games[itemID].serial) + ".log"; + QString filePath = logPath + "/" + fileName; + QStringList arguments; + if (QFile::exists(filePath)) { +#ifdef Q_OS_WIN + arguments << "/select," << filePath.replace("/", "\\"); + QProcess::startDetached("explorer", arguments); + +#elif defined(Q_OS_MAC) + arguments << "-R" << filePath; + QProcess::startDetached("open", arguments); + +#elif defined(Q_OS_LINUX) + QStringList arguments; + arguments << "--select" << filePath; + if (!QProcess::startDetached("nautilus", arguments)) { + // Failed to open Nautilus to select file + arguments.clear(); + arguments << logPath; + if (!QProcess::startDetached("xdg-open", arguments)) { + // Failed to open directory on Linux + } + } +#else + QDesktopServices::openUrl(QUrl::fromLocalFile(logPath)); +#endif + } else { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Information); + msgBox.setText(tr("No log file found for this game!")); + + QPushButton* okButton = msgBox.addButton(QMessageBox::Ok); + QPushButton* openFolderButton = + msgBox.addButton(tr("Open Log Folder"), QMessageBox::ActionRole); + + msgBox.exec(); + + if (msgBox.clickedButton() == openFolderButton) { + QDesktopServices::openUrl(QUrl::fromLocalFile(logPath)); + } + } + } } if (selected == &openSfoViewer) { @@ -169,6 +222,12 @@ public: game_update_path += "-UPDATE"; if (std::filesystem::exists(game_update_path)) { game_folder_path = game_update_path; + } else { + game_update_path = game_folder_path; + game_update_path += "-patch"; + if (std::filesystem::exists(game_update_path)) { + game_folder_path = game_update_path; + } } if (psf.Open(game_folder_path / "sce_sys" / "param.sfo")) { int rows = psf.GetEntries().size(); @@ -265,8 +324,40 @@ public: game_update_path += "-UPDATE"; if (std::filesystem::exists(game_update_path)) { Common::FS::PathToQString(gameTrpPath, game_update_path); + } else { + game_update_path = Common::FS::PathFromQString(gameTrpPath); + game_update_path += "-patch"; + if (std::filesystem::exists(game_update_path)) { + Common::FS::PathToQString(gameTrpPath, game_update_path); + } } - TrophyViewer* trophyViewer = new TrophyViewer(trophyPath, gameTrpPath); + + // Array with all games and their trophy information + QVector allTrophyGames; + for (const auto& game : m_games) { + TrophyGameInfo gameInfo; + gameInfo.name = QString::fromStdString(game.name); + Common::FS::PathToQString(gameInfo.trophyPath, game.serial); + Common::FS::PathToQString(gameInfo.gameTrpPath, game.path); + + auto update_path = Common::FS::PathFromQString(gameInfo.gameTrpPath); + update_path += "-UPDATE"; + if (std::filesystem::exists(update_path)) { + Common::FS::PathToQString(gameInfo.gameTrpPath, update_path); + } else { + update_path = Common::FS::PathFromQString(gameInfo.gameTrpPath); + update_path += "-patch"; + if (std::filesystem::exists(update_path)) { + Common::FS::PathToQString(gameInfo.gameTrpPath, update_path); + } + } + + allTrophyGames.append(gameInfo); + } + + QString gameName = QString::fromStdString(m_games[itemID].name); + TrophyViewer* trophyViewer = + new TrophyViewer(trophyPath, gameTrpPath, gameName, allTrophyGames); trophyViewer->show(); connect(widget->parent(), &QWidget::destroyed, trophyViewer, [trophyViewer]() { trophyViewer->deleteLater(); }); @@ -380,20 +471,31 @@ public: } if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC || - selected == deleteSaveData) { + selected == deleteSaveData || selected == deleteTrophy) { bool error = false; - QString folder_path, game_update_path, dlc_path, save_data_path; + QString folder_path, game_update_path, dlc_path, save_data_path, trophy_data_path; Common::FS::PathToQString(folder_path, m_games[itemID].path); game_update_path = folder_path + "-UPDATE"; + if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) { + game_update_path = folder_path + "-patch"; + } Common::FS::PathToQString( dlc_path, Config::getAddonInstallDir() / Common::FS::PathFromQString(folder_path).parent_path().filename()); Common::FS::PathToQString(save_data_path, Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "savedata/1" / m_games[itemID].serial); - QString message_type = tr("Game"); - if (selected == deleteUpdate) { + Common::FS::PathToQString(trophy_data_path, + Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / + m_games[itemID].serial / "TrophyFiles"); + + QString message_type; + + if (selected == deleteGame) { + BackgroundMusicPlayer::getInstance().stopMusic(); + message_type = tr("Game"); + } else if (selected == deleteUpdate) { if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) { QMessageBox::critical(nullptr, tr("Error"), QString(tr("This game has no update to delete!"))); @@ -420,6 +522,16 @@ public: folder_path = save_data_path; message_type = tr("Save Data"); } + } else if (selected == deleteTrophy) { + if (!std::filesystem::exists(Common::FS::PathFromQString(trophy_data_path))) { + QMessageBox::critical( + nullptr, tr("Error"), + QString(tr("This game has no saved trophies to delete!"))); + error = true; + } else { + folder_path = trophy_data_path; + message_type = tr("Trophy"); + } } if (!error) { QString gameName = QString::fromStdString(m_games[itemID].name); @@ -456,7 +568,7 @@ public: "title", QString("%1 - %2").arg(QString::fromStdString(m_games[itemID].serial), QString::fromStdString(m_games[itemID].name))); query.addQueryItem("game-name", QString::fromStdString(m_games[itemID].name)); - query.addQueryItem("game-code", QString::fromStdString(m_games[itemID].serial)); + query.addQueryItem("game-serial", QString::fromStdString(m_games[itemID].serial)); query.addQueryItem("game-version", QString::fromStdString(m_games[itemID].version)); query.addQueryItem("emulator-version", QString(Common::VERSION)); url.setQuery(query); diff --git a/src/qt_gui/kbm_config_dialog.cpp b/src/qt_gui/kbm_config_dialog.cpp index cfff056a0..1851c591d 100644 --- a/src/qt_gui/kbm_config_dialog.cpp +++ b/src/qt_gui/kbm_config_dialog.cpp @@ -28,7 +28,7 @@ HelpDialog* helpDialog; EditorDialog::EditorDialog(QWidget* parent) : QDialog(parent) { - setWindowTitle("Edit Keyboard + Mouse and Controller input bindings"); + setWindowTitle(tr("Edit Keyboard + Mouse and Controller input bindings")); resize(600, 400); // Create the editor widget @@ -42,7 +42,7 @@ EditorDialog::EditorDialog(QWidget* parent) : QDialog(parent) { // Load all installed games loadInstalledGames(); - QCheckBox* unifiedInputCheckBox = new QCheckBox("Use Per-Game configs", this); + QCheckBox* unifiedInputCheckBox = new QCheckBox(tr("Use Per-Game configs"), this); unifiedInputCheckBox->setChecked(!Config::GetUseUnifiedInputConfig()); // Connect checkbox signal @@ -94,7 +94,7 @@ void EditorDialog::loadFile(QString game) { originalConfig = editor->toPlainText(); file.close(); } else { - QMessageBox::warning(this, "Error", "Could not open the file for reading"); + QMessageBox::warning(this, tr("Error"), tr("Could not open the file for reading")); } } @@ -108,7 +108,7 @@ void EditorDialog::saveFile(QString game) { out << editor->toPlainText(); file.close(); } else { - QMessageBox::warning(this, "Error", "Could not open the file for writing"); + QMessageBox::warning(this, tr("Error"), tr("Could not open the file for writing")); } } @@ -121,7 +121,7 @@ void EditorDialog::closeEvent(QCloseEvent* event) { } if (hasUnsavedChanges()) { QMessageBox::StandardButton reply; - reply = QMessageBox::question(this, "Save Changes", "Do you want to save changes?", + reply = QMessageBox::question(this, tr("Save Changes"), tr("Do you want to save changes?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (reply == QMessageBox::Yes) { @@ -168,7 +168,7 @@ void EditorDialog::onCancelClicked() { void EditorDialog::onHelpClicked() { if (!isHelpOpen) { helpDialog = new HelpDialog(&isHelpOpen, this); - helpDialog->setWindowTitle("Help"); + helpDialog->setWindowTitle(tr("Help")); helpDialog->setAttribute(Qt::WA_DeleteOnClose); // Clean up on close // Get the position and size of the Config window QRect configGeometry = this->geometry(); @@ -188,10 +188,10 @@ void EditorDialog::onResetToDefaultClicked() { bool default_default = gameComboBox->currentText() == "default"; QString prompt = default_default - ? "Do you want to reset your custom default config to the original default config?" - : "Do you want to reset this config to your custom default config?"; - QMessageBox::StandardButton reply = - QMessageBox::question(this, "Reset to Default", prompt, QMessageBox::Yes | QMessageBox::No); + ? tr("Do you want to reset your custom default config to the original default config?") + : tr("Do you want to reset this config to your custom default config?"); + QMessageBox::StandardButton reply = QMessageBox::question(this, tr("Reset to Default"), prompt, + QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { if (default_default) { @@ -206,7 +206,7 @@ void EditorDialog::onResetToDefaultClicked() { editor->setPlainText(in.readAll()); file.close(); } else { - QMessageBox::warning(this, "Error", "Could not open the file for reading"); + QMessageBox::warning(this, tr("Error"), tr("Could not open the file for reading")); } // saveFile(gameComboBox->currentText()); } @@ -225,7 +225,8 @@ void EditorDialog::loadInstalledGames() { QDir parentFolder(installDir); QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (const auto& fileInfo : fileList) { - if (fileInfo.isDir() && !fileInfo.filePath().endsWith("-UPDATE")) { + if (fileInfo.isDir() && (!fileInfo.filePath().endsWith("-UPDATE") || + !fileInfo.filePath().endsWith("-patch"))) { gameComboBox->addItem(fileInfo.fileName()); // Add game name to combo box } } diff --git a/src/qt_gui/kbm_gui.cpp b/src/qt_gui/kbm_gui.cpp new file mode 100644 index 000000000..8777dda95 --- /dev/null +++ b/src/qt_gui/kbm_gui.cpp @@ -0,0 +1,1050 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include + +#include "common/path_util.h" +#include "kbm_config_dialog.h" +#include "kbm_gui.h" +#include "kbm_help_dialog.h" +#include "ui_kbm_gui.h" + +HelpDialog* HelpWindow; +KBMSettings::KBMSettings(std::shared_ptr game_info_get, QWidget* parent) + : QDialog(parent), m_game_info(game_info_get), ui(new Ui::KBMSettings) { + + ui->setupUi(this); + ui->PerGameCheckBox->setChecked(!Config::GetUseUnifiedInputConfig()); + ui->TextEditorButton->setFocus(); + this->setFocusPolicy(Qt::StrongFocus); + + ui->MouseJoystickBox->addItem("none"); + ui->MouseJoystickBox->addItem("right"); + ui->MouseJoystickBox->addItem("left"); + + ui->ProfileComboBox->addItem("Common Config"); + for (int i = 0; i < m_game_info->m_games.size(); i++) { + ui->ProfileComboBox->addItem(QString::fromStdString(m_game_info->m_games[i].serial)); + } + + ButtonsList = { + ui->CrossButton, ui->CircleButton, ui->TriangleButton, ui->SquareButton, + ui->L1Button, ui->R1Button, ui->L2Button, ui->R2Button, + ui->L3Button, ui->R3Button, ui->TouchpadButton, ui->OptionsButton, + ui->TouchpadButton, ui->DpadUpButton, ui->DpadDownButton, ui->DpadLeftButton, + ui->DpadRightButton, ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, + ui->LStickRightButton, ui->RStickUpButton, ui->RStickDownButton, ui->RStickLeftButton, + ui->RStickRightButton, ui->LHalfButton, ui->RHalfButton}; + + ButtonConnects(); + SetUIValuestoMappings("default"); + installEventFilter(this); + + ui->ProfileComboBox->setCurrentText("Common Config"); + ui->TitleLabel->setText("Common Config"); + config_id = "default"; + + connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) { + if (button == ui->buttonBox->button(QDialogButtonBox::Save)) { + if (HelpWindowOpen) { + HelpWindow->close(); + HelpWindowOpen = false; + } + SaveKBMConfig(true); + } else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) { + SetDefault(); + } else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) { + SaveKBMConfig(false); + } + }); + + ui->buttonBox->button(QDialogButtonBox::Save)->setText(tr("Save")); + ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Apply")); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults")); + ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); + + connect(ui->HelpButton, &QPushButton::clicked, this, &KBMSettings::onHelpClicked); + connect(ui->TextEditorButton, &QPushButton::clicked, this, [this]() { + auto kbmWindow = new EditorDialog(this); + kbmWindow->exec(); + SetUIValuestoMappings(config_id); + }); + + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { + QWidget::close(); + if (HelpWindowOpen) { + HelpWindow->close(); + HelpWindowOpen = false; + } + }); + + connect(ui->ProfileComboBox, &QComboBox::currentTextChanged, this, [this] { + GetGameTitle(); + SetUIValuestoMappings(config_id); + }); + + connect(ui->CopyCommonButton, &QPushButton::clicked, this, [this] { + if (ui->ProfileComboBox->currentText() == "Common Config") { + QMessageBox::information(this, tr("Common Config Selected"), + // clang-format off +tr("This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config.")); + // clang-format on + } else { + QMessageBox::StandardButton reply = + QMessageBox::question(this, tr("Copy values from Common Config"), + // clang-format off +tr("Do you want to overwrite existing mappings with the mappings from the Common Config?"), + // clang-format on + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::Yes) { + SetUIValuestoMappings("default"); + } + } + }); + + connect(ui->DeadzoneOffsetSlider, &QSlider::valueChanged, this, [this](int value) { + QString DOSValue = QString::number(value / 100.0, 'f', 2); + QString DOSString = tr("Deadzone Offset (def 0.50):") + " " + DOSValue; + ui->DeadzoneOffsetLabel->setText(DOSString); + }); + + connect(ui->SpeedMultiplierSlider, &QSlider::valueChanged, this, [this](int value) { + QString SMSValue = QString::number(value / 10.0, 'f', 1); + QString SMSString = tr("Speed Multiplier (def 1.0):") + " " + SMSValue; + ui->SpeedMultiplierLabel->setText(SMSString); + }); + + connect(ui->SpeedOffsetSlider, &QSlider::valueChanged, this, [this](int value) { + QString SOSValue = QString::number(value / 1000.0, 'f', 3); + QString SOSString = tr("Speed Offset (def 0.125):") + " " + SOSValue; + ui->SpeedOffsetLabel->setText(SOSString); + }); +} + +void KBMSettings::ButtonConnects() { + connect(ui->CrossButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->CrossButton); }); + connect(ui->CircleButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->CircleButton); }); + connect(ui->TriangleButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->TriangleButton); }); + connect(ui->SquareButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->SquareButton); }); + + connect(ui->L1Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->L1Button); }); + connect(ui->L2Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->L2Button); }); + connect(ui->L3Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->L3Button); }); + connect(ui->R1Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->R1Button); }); + connect(ui->R2Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->R2Button); }); + connect(ui->R3Button, &QPushButton::clicked, this, [this]() { StartTimer(ui->R3Button); }); + + connect(ui->TouchpadButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->TouchpadButton); }); + connect(ui->OptionsButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->OptionsButton); }); + + connect(ui->DpadUpButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->DpadUpButton); }); + connect(ui->DpadDownButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->DpadDownButton); }); + connect(ui->DpadLeftButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->DpadLeftButton); }); + connect(ui->DpadRightButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->DpadRightButton); }); + + connect(ui->LStickUpButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->LStickUpButton); }); + connect(ui->LStickDownButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->LStickDownButton); }); + connect(ui->LStickLeftButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->LStickLeftButton); }); + connect(ui->LStickRightButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->LStickRightButton); }); + + connect(ui->RStickUpButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->RStickUpButton); }); + connect(ui->RStickDownButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->RStickDownButton); }); + connect(ui->RStickLeftButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->RStickLeftButton); }); + connect(ui->RStickRightButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->RStickRightButton); }); + + connect(ui->LHalfButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->LHalfButton); }); + connect(ui->RHalfButton, &QPushButton::clicked, this, + [this]() { StartTimer(ui->RHalfButton); }); +} + +void KBMSettings::DisableMappingButtons() { + for (const auto& i : ButtonsList) { + i->setEnabled(false); + } +} + +void KBMSettings::EnableMappingButtons() { + for (const auto& i : ButtonsList) { + i->setEnabled(true); + } +} + +void KBMSettings::SaveKBMConfig(bool CloseOnSave) { + std::string output_string = "", input_string = ""; + std::vector lines, inputs; + + lines.push_back("#Feeling lost? Check out the Help section!"); + lines.push_back(""); + lines.push_back("#Keyboard bindings"); + lines.push_back(""); + + input_string = ui->CrossButton->text().toStdString(); + output_string = "cross"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->CircleButton->text().toStdString(); + output_string = "circle"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->TriangleButton->text().toStdString(); + output_string = "triangle"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->SquareButton->text().toStdString(); + output_string = "square"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->DpadUpButton->text().toStdString(); + output_string = "pad_up"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->DpadDownButton->text().toStdString(); + output_string = "pad_down"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->DpadLeftButton->text().toStdString(); + output_string = "pad_left"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->DpadRightButton->text().toStdString(); + output_string = "pad_right"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->L1Button->text().toStdString(); + output_string = "l1"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->R1Button->text().toStdString(); + output_string = "r1"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->L2Button->text().toStdString(); + output_string = "l2"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->R2Button->text().toStdString(); + output_string = "r2"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->L3Button->text().toStdString(); + output_string = "l3"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->R3Button->text().toStdString(); + output_string = "r3"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->OptionsButton->text().toStdString(); + output_string = "options"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->TouchpadButton->text().toStdString(); + output_string = "touchpad"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->LStickUpButton->text().toStdString(); + output_string = "axis_left_y_minus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->LStickDownButton->text().toStdString(); + output_string = "axis_left_y_plus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->LStickLeftButton->text().toStdString(); + output_string = "axis_left_x_minus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->LStickRightButton->text().toStdString(); + output_string = "axis_left_x_plus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->RStickUpButton->text().toStdString(); + output_string = "axis_right_y_minus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->RStickDownButton->text().toStdString(); + output_string = "axis_right_y_plus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->RStickLeftButton->text().toStdString(); + output_string = "axis_right_x_minus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->RStickRightButton->text().toStdString(); + output_string = "axis_right_x_plus"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + lines.push_back(""); + + input_string = ui->MouseJoystickBox->currentText().toStdString(); + output_string = "mouse_to_joystick"; + if (input_string != "unmapped") + lines.push_back(output_string + " = " + input_string); + + input_string = ui->LHalfButton->text().toStdString(); + output_string = "leftjoystick_halfmode"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + input_string = ui->RHalfButton->text().toStdString(); + output_string = "rightjoystick_halfmode"; + lines.push_back(output_string + " = " + input_string); + if (input_string != "unmapped") + inputs.push_back(input_string); + + std::string DOString = std::format("{:.2f}", (ui->DeadzoneOffsetSlider->value() / 100.f)); + std::string SMString = std::format("{:.1f}", (ui->SpeedMultiplierSlider->value() / 10.f)); + std::string SOString = std::format("{:.3f}", (ui->SpeedOffsetSlider->value() / 1000.f)); + input_string = DOString + ", " + SMString + ", " + SOString; + output_string = "mouse_movement_params"; + lines.push_back(output_string + " = " + input_string); + + lines.push_back(""); + const auto config_file = Config::GetFoolproofKbmConfigFile(config_id); + std::fstream file(config_file); + int lineCount = 0; + std::string line; + while (std::getline(file, line)) { + lineCount++; + + if (line.empty()) { + lines.push_back(line); + continue; + } + + std::size_t comment_pos = line.find('#'); + if (comment_pos != std::string::npos) { + if (!line.contains("Keyboard bindings") && !line.contains("Feeling lost") && + !line.contains("Alternatives for users")) + lines.push_back(line); + continue; + } + + std::size_t equal_pos = line.find('='); + if (equal_pos == std::string::npos) { + lines.push_back(line); + continue; + } + + output_string = line.substr(0, equal_pos - 1); + input_string = line.substr(equal_pos + 2); + + if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) != + ControllerInputs.end() || + output_string == "analog_deadzone" || output_string == "override_controller_color") { + lines.push_back(line); + } + } + file.close(); + + // Prevent duplicate inputs for KBM as this breaks the engine + for (auto it = inputs.begin(); it != inputs.end(); ++it) { + if (std::find(it + 1, inputs.end(), *it) != inputs.end()) { + QMessageBox::information(this, tr("Unable to Save"), + tr("Cannot bind any unique input more than once")); + return; + } + } + + std::vector save; + bool CurrentLineEmpty = false, LastLineEmpty = false; + for (auto const& line : lines) { + LastLineEmpty = CurrentLineEmpty ? true : false; + CurrentLineEmpty = line.empty() ? true : false; + if (!CurrentLineEmpty || !LastLineEmpty) + save.push_back(line); + } + + std::ofstream output_file(config_file); + for (auto const& line : save) { + output_file << line << '\n'; + } + output_file.close(); + + Config::SetUseUnifiedInputConfig(!ui->PerGameCheckBox->isChecked()); + Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml"); + + if (CloseOnSave) + QWidget::close(); +} + +void KBMSettings::SetDefault() { + ui->CrossButton->setText("kp2"); + ui->CircleButton->setText("kp6"); + ui->TriangleButton->setText("kp8"); + ui->SquareButton->setText("kp4"); + + ui->L1Button->setText("q"); + ui->L2Button->setText("e"); + ui->L3Button->setText("x"); + ui->R1Button->setText("u"); + ui->R2Button->setText("o"); + ui->R3Button->setText("m"); + + ui->TouchpadButton->setText("space"); + ui->OptionsButton->setText("enter"); + + ui->DpadUpButton->setText("up"); + ui->DpadDownButton->setText("down"); + ui->DpadLeftButton->setText("left"); + ui->DpadRightButton->setText("right"); + + ui->LStickUpButton->setText("w"); + ui->LStickDownButton->setText("s"); + ui->LStickLeftButton->setText("a"); + ui->LStickRightButton->setText("d"); + + ui->RStickUpButton->setText("i"); + ui->RStickDownButton->setText("k"); + ui->RStickLeftButton->setText("j"); + ui->RStickRightButton->setText("l"); + + ui->LHalfButton->setText("unmapped"); + ui->RHalfButton->setText("unmapped"); + + ui->MouseJoystickBox->setCurrentText("none"); + ui->DeadzoneOffsetSlider->setValue(50); + ui->SpeedMultiplierSlider->setValue(10); + ui->SpeedOffsetSlider->setValue(125); +} + +void KBMSettings::SetUIValuestoMappings(std::string config_id) { + const auto config_file = Config::GetFoolproofKbmConfigFile(config_id); + std::ifstream file(config_file); + + int lineCount = 0; + std::string line = ""; + while (std::getline(file, line)) { + lineCount++; + + std::size_t comment_pos = line.find('#'); + if (comment_pos != std::string::npos) + line = line.substr(0, comment_pos); + + std::size_t equal_pos = line.find('='); + if (equal_pos == std::string::npos) + continue; + + std::string output_string = line.substr(0, equal_pos - 1); + std::string input_string = line.substr(equal_pos + 2); + + if (std::find(ControllerInputs.begin(), ControllerInputs.end(), input_string) == + ControllerInputs.end()) { + + if (output_string == "cross") { + ui->CrossButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "circle") { + ui->CircleButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "square") { + ui->SquareButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "triangle") { + ui->TriangleButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "l1") { + ui->L1Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "l2") { + ui->L2Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "r1") { + ui->R1Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "r2") { + ui->R2Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "l3") { + ui->L3Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "r3") { + ui->R3Button->setText(QString::fromStdString(input_string)); + } else if (output_string == "pad_up") { + ui->DpadUpButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "pad_down") { + ui->DpadDownButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "pad_left") { + ui->DpadLeftButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "pad_right") { + ui->DpadRightButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "options") { + ui->OptionsButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "touchpad") { + ui->TouchpadButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_left_x_minus") { + ui->LStickLeftButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_left_x_plus") { + ui->LStickRightButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_left_y_minus") { + ui->LStickUpButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_left_y_plus") { + ui->LStickDownButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_right_x_minus") { + ui->RStickLeftButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_right_x_plus") { + ui->RStickRightButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_right_y_minus") { + ui->RStickUpButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "axis_right_y_plus") { + ui->RStickDownButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "mouse_to_joystick") { + ui->MouseJoystickBox->setCurrentText(QString::fromStdString(input_string)); + } else if (output_string == "leftjoystick_halfmode") { + ui->LHalfButton->setText(QString::fromStdString(input_string)); + } else if (output_string == "rightjoystick_halfmode") { + ui->RHalfButton->setText(QString::fromStdString(input_string)); + } else if (output_string.contains("mouse_movement_params")) { + std::size_t comma_pos = line.find(','); + if (comma_pos != std::string::npos) { + std::string DOstring = line.substr(equal_pos + 1, comma_pos); + float DOffsetValue = std::stof(DOstring) * 100.0; + int DOffsetInt = int(DOffsetValue); + ui->DeadzoneOffsetSlider->setValue(DOffsetInt); + QString LabelValue = QString::number(DOffsetInt / 100.0, 'f', 2); + QString LabelString = tr("Deadzone Offset (def 0.50):") + " " + LabelValue; + ui->DeadzoneOffsetLabel->setText(LabelString); + + std::string SMSOstring = line.substr(comma_pos + 1); + std::size_t comma_pos2 = SMSOstring.find(','); + if (comma_pos2 != std::string::npos) { + std::string SMstring = SMSOstring.substr(0, comma_pos2); + float SpeedMultValue = std::stof(SMstring) * 10.0; + int SpeedMultInt = int(SpeedMultValue); + if (SpeedMultInt < 1) + SpeedMultInt = 1; + if (SpeedMultInt > 50) + SpeedMultInt = 50; + ui->SpeedMultiplierSlider->setValue(SpeedMultInt); + LabelValue = QString::number(SpeedMultInt / 10.0, 'f', 1); + LabelString = tr("Speed Multiplier (def 1.0):") + " " + LabelValue; + ui->SpeedMultiplierLabel->setText(LabelString); + + std::string SOstring = SMSOstring.substr(comma_pos2 + 1); + float SOffsetValue = std::stof(SOstring) * 1000.0; + int SOffsetInt = int(SOffsetValue); + ui->SpeedOffsetSlider->setValue(SOffsetInt); + LabelValue = QString::number(SOffsetInt / 1000.0, 'f', 3); + LabelString = tr("Speed Offset (def 0.125):") + " " + LabelValue; + ui->SpeedOffsetLabel->setText(LabelString); + } + } + } + } + } + file.close(); +} + +void KBMSettings::GetGameTitle() { + if (ui->ProfileComboBox->currentText() == "Common Config") { + ui->TitleLabel->setText("Common Config"); + } else { + for (int i = 0; i < m_game_info->m_games.size(); i++) { + if (m_game_info->m_games[i].serial == + ui->ProfileComboBox->currentText().toStdString()) { + ui->TitleLabel->setText(QString::fromStdString(m_game_info->m_games[i].name)); + } + } + } + config_id = (ui->ProfileComboBox->currentText() == "Common Config") + ? "default" + : ui->ProfileComboBox->currentText().toStdString(); +} + +void KBMSettings::onHelpClicked() { + if (!HelpWindowOpen) { + HelpWindow = new HelpDialog(&HelpWindowOpen, this); + HelpWindow->setWindowTitle(tr("Help")); + HelpWindow->setAttribute(Qt::WA_DeleteOnClose); // Clean up on close + HelpWindow->show(); + HelpWindowOpen = true; + } else { + HelpWindow->close(); + HelpWindowOpen = false; + } +} + +void KBMSettings::StartTimer(QPushButton*& button) { + MappingTimer = 3; + EnableMapping = true; + MappingCompleted = false; + modifier = ""; + mapping = button->text(); + + DisableMappingButtons(); + button->setText(tr("Press a key") + " [" + QString::number(MappingTimer) + "]"); + timer = new QTimer(this); + MappingButton = button; + connect(timer, &QTimer::timeout, this, [this]() { CheckMapping(MappingButton); }); + timer->start(1000); +} + +void KBMSettings::CheckMapping(QPushButton*& button) { + MappingTimer -= 1; + button->setText(tr("Press a key") + " [" + QString::number(MappingTimer) + "]"); + + if (MappingCompleted) { + EnableMapping = false; + EnableMappingButtons(); + timer->stop(); + + if (mapping == "lshift" || mapping == "lalt" || mapping == "lctrl" || mapping == "lmeta" || + mapping == "lwin") { + modifier = ""; + } + + if (modifier != "") { + button->setText(modifier + ", " + mapping); + } else { + button->setText(mapping); + } + } + + if (MappingTimer <= 0) { + button->setText(mapping); + EnableMapping = false; + EnableMappingButtons(); + timer->stop(); + } +} + +void KBMSettings::SetMapping(QString input) { + mapping = input; + MappingCompleted = true; +} + +bool KBMSettings::eventFilter(QObject* obj, QEvent* event) { + if (event->type() == QEvent::Close) { + if (HelpWindowOpen) { + HelpWindow->close(); + HelpWindowOpen = false; + } + } + + if (EnableMapping) { + if (Qt::ShiftModifier & QApplication::keyboardModifiers()) { + modifier = "lshift"; + } else if (Qt::AltModifier & QApplication::keyboardModifiers()) { + modifier = "lalt"; + } else if (Qt::ControlModifier & QApplication::keyboardModifiers()) { + modifier = "lctrl"; + } else if (Qt::MetaModifier & QApplication::keyboardModifiers()) { +#ifdef _WIN32 + modifier = "lwin"; +#else + modifier = "lmeta"; +#endif + } + + if (event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + + switch (keyEvent->key()) { + case Qt::Key_Space: + SetMapping("space"); + break; + case Qt::Key_Comma: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kpcomma"); + } else { + SetMapping("comma"); + } + break; + case Qt::Key_Period: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kpperiod"); + } else { + SetMapping("period"); + } + break; + case Qt::Key_Slash: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) + SetMapping("kpdivide"); + break; + case Qt::Key_Asterisk: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) + SetMapping("kpmultiply"); + break; + case Qt::Key_Question: + SetMapping("question"); + break; + case Qt::Key_Semicolon: + SetMapping("semicolon"); + break; + case Qt::Key_Minus: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kpminus"); + } else { + SetMapping("minus"); + } + break; + case Qt::Key_Plus: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kpplus"); + } else { + SetMapping("plus"); + } + break; + case Qt::Key_ParenLeft: + SetMapping("lparenthesis"); + break; + case Qt::Key_ParenRight: + SetMapping("rparenthesis"); + break; + case Qt::Key_BracketLeft: + SetMapping("lbracket"); + break; + case Qt::Key_BracketRight: + SetMapping("rbracket"); + break; + case Qt::Key_BraceLeft: + SetMapping("lbrace"); + break; + case Qt::Key_BraceRight: + SetMapping("rbrace"); + break; + case Qt::Key_Backslash: + SetMapping("backslash"); + break; + case Qt::Key_Tab: + SetMapping("tab"); + break; + case Qt::Key_Backspace: + SetMapping("backspace"); + break; + case Qt::Key_Return: + SetMapping("enter"); + break; + case Qt::Key_Enter: + SetMapping("kpenter"); + break; + case Qt::Key_Escape: + SetMapping("unmapped"); + break; + case Qt::Key_Shift: + SetMapping("lshift"); + break; + case Qt::Key_Alt: + SetMapping("lalt"); + break; + case Qt::Key_Control: + SetMapping("lctrl"); + break; + case Qt::Key_Meta: + activateWindow(); +#ifdef _WIN32 + SetMapping("lwin"); +#else + SetMapping("lmeta"); +#endif + case Qt::Key_1: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp1"); + } else { + SetMapping("1"); + } + break; + case Qt::Key_2: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp2"); + } else { + SetMapping("2"); + } + break; + case Qt::Key_3: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp3"); + } else { + SetMapping("3"); + } + break; + case Qt::Key_4: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp4"); + } else { + SetMapping("4"); + } + break; + case Qt::Key_5: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp5"); + } else { + SetMapping("5"); + } + break; + case Qt::Key_6: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp6"); + } else { + SetMapping("6"); + } + break; + case Qt::Key_7: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp7"); + } else { + SetMapping("7"); + } + break; + case Qt::Key_8: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp8"); + } else { + SetMapping("8"); + } + break; + case Qt::Key_9: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp9"); + } else { + SetMapping("9"); + } + break; + case Qt::Key_0: + if (Qt::KeypadModifier & QApplication::keyboardModifiers()) { + SetMapping("kp0"); + } else { + SetMapping("0"); + } + break; + case Qt::Key_Up: + activateWindow(); + SetMapping("up"); + break; + case Qt::Key_Down: + SetMapping("down"); + break; + case Qt::Key_Left: + SetMapping("left"); + break; + case Qt::Key_Right: + SetMapping("right"); + break; + case Qt::Key_A: + SetMapping("a"); + break; + case Qt::Key_B: + SetMapping("b"); + break; + case Qt::Key_C: + SetMapping("c"); + break; + case Qt::Key_D: + SetMapping("d"); + break; + case Qt::Key_E: + SetMapping("e"); + break; + case Qt::Key_F: + SetMapping("f"); + break; + case Qt::Key_G: + SetMapping("g"); + break; + case Qt::Key_H: + SetMapping("h"); + break; + case Qt::Key_I: + SetMapping("i"); + break; + case Qt::Key_J: + SetMapping("j"); + break; + case Qt::Key_K: + SetMapping("k"); + break; + case Qt::Key_L: + SetMapping("l"); + break; + case Qt::Key_M: + SetMapping("m"); + break; + case Qt::Key_N: + SetMapping("n"); + break; + case Qt::Key_O: + SetMapping("o"); + break; + case Qt::Key_P: + SetMapping("p"); + break; + case Qt::Key_Q: + SetMapping("q"); + break; + case Qt::Key_R: + SetMapping("r"); + break; + case Qt::Key_S: + SetMapping("s"); + break; + case Qt::Key_T: + SetMapping("t"); + break; + case Qt::Key_U: + SetMapping("u"); + break; + case Qt::Key_V: + SetMapping("v"); + break; + case Qt::Key_W: + SetMapping("w"); + break; + case Qt::Key_X: + SetMapping("x"); + break; + case Qt::Key_Y: + SetMapping("Y"); + break; + case Qt::Key_Z: + SetMapping("z"); + break; + default: + break; + } + return true; + } + + if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent* mouseEvent = static_cast(event); + switch (mouseEvent->button()) { + case Qt::LeftButton: + SetMapping("leftbutton"); + break; + case Qt::RightButton: + SetMapping("rightbutton"); + break; + case Qt::MiddleButton: + SetMapping("middlebutton"); + break; + default: + break; + } + return true; + } + + const QList AxisList = { + ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->LStickRightButton, + ui->RStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->RStickRightButton}; + + if (event->type() == QEvent::Wheel) { + QWheelEvent* wheelEvent = static_cast(event); + if (wheelEvent->angleDelta().y() > 5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { + SetMapping("mousewheelup"); + } else { + QMessageBox::information(this, tr("Cannot set mapping"), + tr("Mousewheel cannot be mapped to stick outputs")); + } + } else if (wheelEvent->angleDelta().y() < -5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { + SetMapping("mousewheeldown"); + } else { + QMessageBox::information(this, tr("Cannot set mapping"), + tr("Mousewheel cannot be mapped to stick outputs")); + } + } + if (wheelEvent->angleDelta().x() > 5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { + // QT changes scrolling to horizontal for all widgets with the alt modifier + if (Qt::AltModifier & QApplication::keyboardModifiers()) { + SetMapping("mousewheelup"); + } else { + SetMapping("mousewheelright"); + } + } else { + QMessageBox::information(this, tr("Cannot set mapping"), + tr("Mousewheel cannot be mapped to stick outputs")); + } + } else if (wheelEvent->angleDelta().x() < -5) { + if (std::find(AxisList.begin(), AxisList.end(), MappingButton) == AxisList.end()) { + if (Qt::AltModifier & QApplication::keyboardModifiers()) { + SetMapping("mousewheeldown"); + } else { + SetMapping("mousewheelleft"); + } + } else { + QMessageBox::information(this, tr("Cannot set mapping"), + tr("Mousewheel cannot be mapped to stick outputs")); + } + } + return true; + } + } + return QDialog::eventFilter(obj, event); +} + +KBMSettings::~KBMSettings() {} diff --git a/src/qt_gui/kbm_gui.h b/src/qt_gui/kbm_gui.h new file mode 100644 index 000000000..06e58eef6 --- /dev/null +++ b/src/qt_gui/kbm_gui.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "game_info.h" + +namespace Ui { +class KBMSettings; +} + +class KBMSettings : public QDialog { + Q_OBJECT +public: + explicit KBMSettings(std::shared_ptr game_info_get, QWidget* parent = nullptr); + ~KBMSettings(); + +private Q_SLOTS: + void SaveKBMConfig(bool CloseOnSave); + void SetDefault(); + void CheckMapping(QPushButton*& button); + void StartTimer(QPushButton*& button); + void onHelpClicked(); + +private: + std::unique_ptr ui; + std::shared_ptr m_game_info; + + bool eventFilter(QObject* obj, QEvent* event) override; + void ButtonConnects(); + void SetUIValuestoMappings(std::string config_id); + void GetGameTitle(); + void DisableMappingButtons(); + void EnableMappingButtons(); + void SetMapping(QString input); + + bool EnableMapping = false; + bool MappingCompleted = false; + bool HelpWindowOpen = false; + QString mapping; + QString modifier; + int MappingTimer; + QTimer* timer; + QPushButton* MappingButton; + QList ButtonsList; + std::string config_id; + const std::vector ControllerInputs = { + "cross", "circle", "square", "triangle", "l1", + "r1", "l2", "r2", "l3", + + "r3", "options", "pad_up", + + "pad_down", + + "pad_left", "pad_right", "axis_left_x", "axis_left_y", "axis_right_x", + "axis_right_y", "back"}; +}; diff --git a/src/qt_gui/kbm_gui.ui b/src/qt_gui/kbm_gui.ui new file mode 100644 index 000000000..c8d63cd00 --- /dev/null +++ b/src/qt_gui/kbm_gui.ui @@ -0,0 +1,1942 @@ + + + + KBMSettings + + + Qt::WindowModality::WindowModal + + + + 0 + 0 + 1234 + 796 + + + + + 0 + 0 + + + + Qt::FocusPolicy::StrongFocus + + + Configure Controls + + + true + + + + + + Qt::FocusPolicy::NoFocus + + + true + + + + + 0 + 0 + 1214 + 746 + + + + + + 0 + 0 + 1211 + 741 + + + + + + + 5 + + + + + true + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + D-Pad + + + + 6 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 0 + 16777215 + + + + Up + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + 160 + 0 + + + + Left + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 160 + 0 + + + + Right + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 124 + 16777215 + + + + Down + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Maximum + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 344 + 16777215 + + + + Left Analog Halfmode + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + true + + + + hold to move left stick at half-speed + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Left Stick + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 16777215 + 2121 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 124 + 16777215 + + + + Up + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + 160 + 0 + + + + Left + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 160 + 0 + + + + + 179 + 16777215 + + + + Right + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 124 + 21212 + + + + Down + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 12 + true + + + + Config Selection + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + 9 + false + + + + Qt::FocusPolicy::NoFocus + + + + + + -1 + + + Common Config + + + + + + + + 10 + true + + + + Common Config + + + Qt::AlignmentFlag::AlignCenter + + + true + + + + + + + + + + + + 0 + 0 + + + + + 9 + false + + + + Qt::FocusPolicy::NoFocus + + + Use per-game configs + + + + + + + + 9 + false + + + + Qt::FocusPolicy::NoFocus + + + Copy from Common Config + + + + + + + + + + + + 0 + + + + + + + + 0 + 0 + + + + + 160 + 0 + + + + L1 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 0 + 0 + + + + + 160 + 0 + + + + L2 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + true + + + + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + Text Editor + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + Help + + + + + + + + + + + + + + + 0 + 0 + + + + + 160 + 0 + + + + R1 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 0 + 0 + + + + + 160 + 0 + + + + R2 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + 0 + 200 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 500 + 200 + + + + :/images/KBM.png + + + true + + + Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter + + + + + + + + + + 0 + + + QLayout::SizeConstraint::SetDefaultConstraint + + + + + + + + 0 + 0 + + + + + 160 + 0 + + + + L3 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 0 + 0 + + + + + 160 + 0 + + + + Touchpad Click + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Mouse to Joystick + + + + + + Qt::FocusPolicy::NoFocus + + + + + + + + true + + + + *press F7 ingame to activate + + + true + + + + + + + + + + + + + + + 0 + 0 + + + + + 160 + 0 + + + + R3 + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 0 + 0 + + + + + 160 + 0 + + + + Options + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + Mouse Movement Parameters + + + + + + + + + false + + + + Deadzone Offset (def 0.50): 0.50 + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + 100 + + + 50 + + + Qt::Orientation::Horizontal + + + + + + + + + + + + false + + + + Speed Multiplier (def 1.0): 1.0 + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + 1 + + + 50 + + + 5 + + + 10 + + + Qt::Orientation::Horizontal + + + + + + + + + + + + false + + + + Speed Offset (def 0.125): 0.125 + + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + 1000 + + + 100 + + + 125 + + + Qt::Orientation::Horizontal + + + + + + + + + + 0 + 0 + + + + + true + false + + + + note: click Help Button/Special Keybindings for more information + + + + + + + + + + + + + + + + 5 + + + + + + 0 + 0 + + + + Face Buttons + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 160 + 0 + + + + + 0 + 16777215 + + + + Triangle + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + 160 + 0 + + + + Square + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 160 + 0 + + + + Circle + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 124 + 16777215 + + + + Cross + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Maximum + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 344 + 16777215 + + + + Right Analog Halfmode + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + true + + + + hold to move right stick at half-speed + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Right Stick + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 160 + 0 + + + + + 124 + 1231321 + + + + Up + + + + + + + 0 + 0 + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + 160 + 0 + + + + Left + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + 160 + 0 + + + + Right + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 160 + 0 + + + + + 124 + 2121 + + + + Down + + + + + + Qt::FocusPolicy::NoFocus + + + unmapped + + + + + + + + + + + + + + + + + + + + + + + QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::RestoreDefaults|QDialogButtonBox::StandardButton::Save + + + false + + + + + + + + + + diff --git a/src/qt_gui/kbm_help_dialog.cpp b/src/qt_gui/kbm_help_dialog.cpp index 44f75f6f8..c13e18b59 100644 --- a/src/qt_gui/kbm_help_dialog.cpp +++ b/src/qt_gui/kbm_help_dialog.cpp @@ -77,11 +77,11 @@ HelpDialog::HelpDialog(bool* open_flag, QWidget* parent) : QDialog(parent) { QVBoxLayout* containerLayout = new QVBoxLayout(containerWidget); // Add expandable sections to container layout - auto* quickstartSection = new ExpandableSection("Quickstart", quickstart()); - auto* faqSection = new ExpandableSection("FAQ", faq()); - auto* syntaxSection = new ExpandableSection("Syntax", syntax()); - auto* specialSection = new ExpandableSection("Special Bindings", special()); - auto* bindingsSection = new ExpandableSection("Keybindings", bindings()); + auto* quickstartSection = new ExpandableSection(tr("Quickstart"), quickstart()); + auto* faqSection = new ExpandableSection(tr("FAQ"), faq()); + auto* syntaxSection = new ExpandableSection(tr("Syntax"), syntax()); + auto* specialSection = new ExpandableSection(tr("Special Bindings"), special()); + auto* bindingsSection = new ExpandableSection(tr("Keybindings"), bindings()); containerLayout->addWidget(quickstartSection); containerLayout->addWidget(faqSection); @@ -109,4 +109,4 @@ HelpDialog::HelpDialog(bool* open_flag, QWidget* parent) : QDialog(parent) { connect(syntaxSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize); connect(specialSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize); connect(bindingsSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize); -} \ No newline at end of file +} diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 36dc226ae..bd9dca6ce 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -157,8 +157,8 @@ int main(int argc, char* argv[]) { } } - // If no game directory is set and no command line argument, prompt for it - if (Config::getGameInstallDirs().empty() && !has_command_line_argument) { + // If no game directories are set and no command line argument, prompt for it + if (Config::getGameInstallDirsEnabled().empty() && !has_command_line_argument) { GameInstallDialog dlg; dlg.exec(); } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 96bd1d9e5..3420e933e 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "SDL3/SDL_events.h" + #include #include #include @@ -21,11 +23,10 @@ #include "core/loader.h" #include "game_install_dialog.h" #include "install_dir_select.h" +#include "kbm_gui.h" #include "main_window.h" #include "settings_dialog.h" -#include "kbm_config_dialog.h" - #include "video_core/renderer_vulkan/vk_instance.h" #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" @@ -130,25 +131,163 @@ void MainWindow::CreateActions() { m_theme_act_group->addAction(ui->setThemeViolet); m_theme_act_group->addAction(ui->setThemeGruvbox); m_theme_act_group->addAction(ui->setThemeTokyoNight); + m_theme_act_group->addAction(ui->setThemeOled); +} + +void MainWindow::PauseGame() { + SDL_Event event; + SDL_memset(&event, 0, sizeof(event)); + event.type = SDL_EVENT_TOGGLE_PAUSE; + is_paused = !is_paused; + UpdateToolbarButtons(); + SDL_PushEvent(&event); +} + +void MainWindow::toggleLabelsUnderIcons() { + bool showLabels = ui->toggleLabelsAct->isChecked(); + Config::setShowLabelsUnderIcons(); + UpdateToolbarLabels(); + if (isGameRunning) { + UpdateToolbarButtons(); + } +} + +void MainWindow::toggleFullscreen() { + SDL_Event event; + SDL_memset(&event, 0, sizeof(event)); + event.type = SDL_EVENT_TOGGLE_FULLSCREEN; + SDL_PushEvent(&event); +} + +QWidget* MainWindow::createButtonWithLabel(QPushButton* button, const QString& labelText, + bool showLabel) { + QWidget* container = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(container); + layout->setAlignment(Qt::AlignCenter | Qt::AlignBottom); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(button); + + QLabel* label = nullptr; + if (showLabel && ui->toggleLabelsAct->isChecked()) { + label = new QLabel(labelText, this); + label->setAlignment(Qt::AlignCenter | Qt::AlignBottom); + layout->addWidget(label); + button->setToolTip(""); + } else { + button->setToolTip(labelText); + } + + container->setLayout(layout); + container->setProperty("buttonLabel", QVariant::fromValue(label)); + return container; +} + +QWidget* createSpacer(QWidget* parent) { + QWidget* spacer = new QWidget(parent); + spacer->setFixedWidth(15); + spacer->setFixedHeight(15); + return spacer; } void MainWindow::AddUiWidgets() { // add toolbar widgets QApplication::setStyle("Fusion"); - ui->toolBar->setObjectName("mw_toolbar"); - ui->toolBar->addWidget(ui->playButton); - ui->toolBar->addWidget(ui->pauseButton); - ui->toolBar->addWidget(ui->stopButton); - ui->toolBar->addWidget(ui->refreshButton); - ui->toolBar->addWidget(ui->settingsButton); - ui->toolBar->addWidget(ui->controllerButton); - ui->toolBar->addWidget(ui->keyboardButton); + + bool showLabels = ui->toggleLabelsAct->isChecked(); + ui->toolBar->clear(); + + ui->toolBar->addWidget(createSpacer(this)); + ui->toolBar->addWidget(createButtonWithLabel(ui->playButton, tr("Play"), showLabels)); + ui->toolBar->addWidget(createButtonWithLabel(ui->pauseButton, tr("Pause"), showLabels)); + ui->toolBar->addWidget(createButtonWithLabel(ui->stopButton, tr("Stop"), showLabels)); + ui->toolBar->addWidget(createButtonWithLabel(ui->restartButton, tr("Restart"), showLabels)); + ui->toolBar->addWidget(createSpacer(this)); + ui->toolBar->addWidget(createButtonWithLabel(ui->settingsButton, tr("Settings"), showLabels)); + ui->toolBar->addWidget( + createButtonWithLabel(ui->fullscreenButton, tr("Full Screen"), showLabels)); + ui->toolBar->addWidget(createSpacer(this)); + ui->toolBar->addWidget( + createButtonWithLabel(ui->controllerButton, tr("Controllers"), showLabels)); + ui->toolBar->addWidget(createButtonWithLabel(ui->keyboardButton, tr("Keyboard"), showLabels)); + ui->toolBar->addWidget(createSpacer(this)); QFrame* line = new QFrame(this); - line->setFrameShape(QFrame::StyledPanel); + line->setFrameShape(QFrame::VLine); line->setFrameShadow(QFrame::Sunken); + line->setMinimumWidth(2); ui->toolBar->addWidget(line); - ui->toolBar->addWidget(ui->sizeSliderContainer); - ui->toolBar->addWidget(ui->mw_searchbar); + ui->toolBar->addWidget(createSpacer(this)); + if (showLabels) { + QLabel* pauseButtonLabel = ui->pauseButton->parentWidget()->findChild(); + if (pauseButtonLabel) { + pauseButtonLabel->setVisible(false); + } + } + ui->toolBar->addWidget( + createButtonWithLabel(ui->refreshButton, tr("Refresh List"), showLabels)); + ui->toolBar->addWidget(createSpacer(this)); + + QBoxLayout* toolbarLayout = new QBoxLayout(QBoxLayout::TopToBottom); + toolbarLayout->setSpacing(2); + toolbarLayout->setContentsMargins(2, 2, 2, 2); + ui->sizeSliderContainer->setFixedWidth(150); + + QWidget* searchSliderContainer = new QWidget(this); + QBoxLayout* searchSliderLayout = new QBoxLayout(QBoxLayout::TopToBottom); + searchSliderLayout->setContentsMargins(0, 0, 6, 6); + searchSliderLayout->setSpacing(2); + ui->mw_searchbar->setFixedWidth(150); + + searchSliderLayout->addWidget(ui->sizeSliderContainer); + searchSliderLayout->addWidget(ui->mw_searchbar); + + searchSliderContainer->setLayout(searchSliderLayout); + + ui->toolBar->addWidget(searchSliderContainer); + + if (!showLabels) { + toolbarLayout->addWidget(searchSliderContainer); + } + + ui->playButton->setVisible(true); + ui->pauseButton->setVisible(false); +} + +void MainWindow::UpdateToolbarButtons() { + // add toolbar widgets when game is running + bool showLabels = ui->toggleLabelsAct->isChecked(); + + ui->playButton->setVisible(false); + ui->pauseButton->setVisible(true); + + if (showLabels) { + QLabel* playButtonLabel = ui->playButton->parentWidget()->findChild(); + if (playButtonLabel) + playButtonLabel->setVisible(false); + } + + if (is_paused) { + ui->pauseButton->setIcon(ui->playButton->icon()); + ui->pauseButton->setToolTip(tr("Resume")); + } else { + if (isIconBlack) { + ui->pauseButton->setIcon(QIcon(":images/pause_icon.png")); + } else { + ui->pauseButton->setIcon(RecolorIcon(QIcon(":images/pause_icon.png"), isWhite)); + } + ui->pauseButton->setToolTip(tr("Pause")); + } + + if (showLabels) { + QLabel* pauseButtonLabel = ui->pauseButton->parentWidget()->findChild(); + if (pauseButtonLabel) { + pauseButtonLabel->setText(is_paused ? tr("Resume") : tr("Pause")); + pauseButtonLabel->setVisible(true); + } + } +} + +void MainWindow::UpdateToolbarLabels() { + AddUiWidgets(); } void MainWindow::CreateDockWindows() { @@ -253,6 +392,8 @@ void MainWindow::CreateConnects() { connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable); connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList); connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable); + connect(ui->toggleLabelsAct, &QAction::toggled, this, &MainWindow::toggleLabelsUnderIcons); + connect(ui->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen); connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) { if (isTableList) { @@ -276,6 +417,7 @@ void MainWindow::CreateConnects() { }); connect(ui->playButton, &QPushButton::clicked, this, &MainWindow::StartGame); + connect(ui->pauseButton, &QPushButton::clicked, this, &MainWindow::PauseGame); connect(m_game_grid_frame.get(), &QTableWidget::cellDoubleClicked, this, &MainWindow::StartGame); connect(m_game_list_frame.get(), &QTableWidget::cellDoubleClicked, this, @@ -347,14 +489,13 @@ void MainWindow::CreateConnects() { settingsDialog->exec(); }); - // this is the editor for kbm keybinds connect(ui->controllerButton, &QPushButton::clicked, this, [this]() { auto configWindow = new ControlSettings(m_game_info, this); configWindow->exec(); }); connect(ui->keyboardButton, &QPushButton::clicked, this, [this]() { - auto kbmWindow = new EditorDialog(this); + auto kbmWindow = new KBMSettings(m_game_info, this); kbmWindow->exec(); }); @@ -594,6 +735,60 @@ void MainWindow::CreateConnects() { pkgViewer->show(); }); + // Trophy Viewer + connect(ui->trophyViewerAct, &QAction::triggered, this, [this]() { + if (m_game_info->m_games.empty()) { + QMessageBox::information( + this, tr("Trophy Viewer"), + tr("No games found. Please add your games to your library first.")); + return; + } + + const auto& firstGame = m_game_info->m_games[0]; + QString trophyPath, gameTrpPath; + Common::FS::PathToQString(trophyPath, firstGame.serial); + Common::FS::PathToQString(gameTrpPath, firstGame.path); + + auto game_update_path = Common::FS::PathFromQString(gameTrpPath); + game_update_path += "-UPDATE"; + if (std::filesystem::exists(game_update_path)) { + Common::FS::PathToQString(gameTrpPath, game_update_path); + } else { + game_update_path = Common::FS::PathFromQString(gameTrpPath); + game_update_path += "-patch"; + if (std::filesystem::exists(game_update_path)) { + Common::FS::PathToQString(gameTrpPath, game_update_path); + } + } + + QVector allTrophyGames; + for (const auto& game : m_game_info->m_games) { + TrophyGameInfo gameInfo; + gameInfo.name = QString::fromStdString(game.name); + Common::FS::PathToQString(gameInfo.trophyPath, game.serial); + Common::FS::PathToQString(gameInfo.gameTrpPath, game.path); + + auto update_path = Common::FS::PathFromQString(gameInfo.gameTrpPath); + update_path += "-UPDATE"; + if (std::filesystem::exists(update_path)) { + Common::FS::PathToQString(gameInfo.gameTrpPath, update_path); + } else { + update_path = Common::FS::PathFromQString(gameInfo.gameTrpPath); + update_path += "-patch"; + if (std::filesystem::exists(update_path)) { + Common::FS::PathToQString(gameInfo.gameTrpPath, update_path); + } + } + + allTrophyGames.append(gameInfo); + } + + QString gameName = QString::fromStdString(firstGame.name); + TrophyViewer* trophyViewer = + new TrophyViewer(trophyPath, gameTrpPath, gameName, allTrophyGames); + trophyViewer->show(); + }); + // Themes connect(ui->setThemeDark, &QAction::triggered, &m_window_themes, [this]() { m_window_themes.SetWindowTheme(Theme::Dark, ui->mw_searchbar); @@ -651,6 +846,14 @@ void MainWindow::CreateConnects() { isIconBlack = false; } }); + connect(ui->setThemeOled, &QAction::triggered, &m_window_themes, [this]() { + m_window_themes.SetWindowTheme(Theme::Oled, ui->mw_searchbar); + Config::setMainWindowTheme(static_cast(Theme::Oled)); + if (isIconBlack) { + SetUiIcons(false); + isIconBlack = false; + } + }); } void MainWindow::StartGame() { @@ -682,6 +885,8 @@ void MainWindow::StartGame() { return; } StartEmulator(path); + + UpdateToolbarButtons(); } } @@ -833,7 +1038,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int // Default paths auto game_folder_path = game_install_dir / pkg.GetTitleID(); auto game_update_path = use_game_update ? game_folder_path.parent_path() / - (std::string{pkg.GetTitleID()} + "-UPDATE") + (std::string{pkg.GetTitleID()} + "-patch") : game_folder_path; const int max_depth = 5; @@ -844,7 +1049,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int if (found_game.has_value()) { game_folder_path = found_game.value().parent_path(); game_update_path = use_game_update ? game_folder_path.parent_path() / - (std::string{pkg.GetTitleID()} + "-UPDATE") + (std::string{pkg.GetTitleID()} + "-patch") : game_folder_path; } } else { @@ -859,7 +1064,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int game_folder_path = game_install_dir / pkg.GetTitleID(); } game_update_path = use_game_update ? game_folder_path.parent_path() / - (std::string{pkg.GetTitleID()} + "-UPDATE") + (std::string{pkg.GetTitleID()} + "-patch") : game_folder_path; } @@ -1098,6 +1303,11 @@ void MainWindow::SetLastUsedTheme() { isIconBlack = false; SetUiIcons(false); break; + case Theme::Oled: + ui->setThemeOled->setChecked(true); + isIconBlack = false; + SetUiIcons(false); + break; } } @@ -1151,12 +1361,15 @@ void MainWindow::SetUiIcons(bool isWhite) { ui->pauseButton->setIcon(RecolorIcon(ui->pauseButton->icon(), isWhite)); ui->stopButton->setIcon(RecolorIcon(ui->stopButton->icon(), isWhite)); ui->refreshButton->setIcon(RecolorIcon(ui->refreshButton->icon(), isWhite)); + ui->restartButton->setIcon(RecolorIcon(ui->restartButton->icon(), isWhite)); ui->settingsButton->setIcon(RecolorIcon(ui->settingsButton->icon(), isWhite)); + ui->fullscreenButton->setIcon(RecolorIcon(ui->fullscreenButton->icon(), isWhite)); ui->controllerButton->setIcon(RecolorIcon(ui->controllerButton->icon(), isWhite)); ui->keyboardButton->setIcon(RecolorIcon(ui->keyboardButton->icon(), isWhite)); ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite)); ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite)); ui->pkgViewerAct->setIcon(RecolorIcon(ui->pkgViewerAct->icon(), isWhite)); + ui->trophyViewerAct->setIcon(RecolorIcon(ui->trophyViewerAct->icon(), isWhite)); ui->configureAct->setIcon(RecolorIcon(ui->configureAct->icon(), isWhite)); ui->addElfFolderAct->setIcon(RecolorIcon(ui->addElfFolderAct->icon(), isWhite)); } diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 5ac56e44c..bcd5e53ba 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -5,6 +5,7 @@ #include #include +#include #include #include "background_music_player.h" @@ -38,6 +39,8 @@ public: void InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg); void InstallDirectory(); void StartGame(); + void PauseGame(); + bool showLabels; private Q_SLOTS: void ConfigureGuiFromSettings(); @@ -47,15 +50,21 @@ private Q_SLOTS: void RefreshGameTable(); void HandleResize(QResizeEvent* event); void OnLanguageChanged(const std::string& locale); + void toggleLabelsUnderIcons(); private: Ui_MainWindow* ui; void AddUiWidgets(); + void UpdateToolbarLabels(); + void UpdateToolbarButtons(); + QWidget* createButtonWithLabel(QPushButton* button, const QString& labelText, bool showLabel); void CreateActions(); + void toggleFullscreen(); void CreateRecentGameActions(); void CreateDockWindows(); void GetPhysicalDevices(); void LoadGameLists(); + #ifdef ENABLE_UPDATER void CheckUpdateMain(bool checkSave); #endif @@ -73,6 +82,9 @@ private: bool isIconBlack = false; bool isTableList = true; bool isGameRunning = false; + bool isWhite = false; + bool is_paused = false; + QActionGroup* m_icon_size_act_group = nullptr; QActionGroup* m_list_mode_act_group = nullptr; QActionGroup* m_theme_act_group = nullptr; diff --git a/src/qt_gui/main_window_themes.cpp b/src/qt_gui/main_window_themes.cpp index 5fffd4c9e..624673cba 100644 --- a/src/qt_gui/main_window_themes.cpp +++ b/src/qt_gui/main_window_themes.cpp @@ -6,6 +6,7 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { QPalette themePalette; + qApp->setStyleSheet(""); switch (theme) { case Theme::Dark: mw_searchbar->setStyleSheet( @@ -18,7 +19,7 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, Qt::white); themePalette.setColor(QPalette::Base, QColor(20, 20, 20)); themePalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53)); - themePalette.setColor(QPalette::ToolTipBase, Qt::white); + themePalette.setColor(QPalette::ToolTipBase, QColor(20, 20, 20)); themePalette.setColor(QPalette::ToolTipText, Qt::white); themePalette.setColor(QPalette::Text, Qt::white); themePalette.setColor(QPalette::Button, QColor(53, 53, 53)); @@ -36,18 +37,18 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { "border-radius: 4px; padding: 5px; }" "QLineEdit:focus {" "border: 1px solid #2A82DA; }"); - themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray - themePalette.setColor(QPalette::WindowText, Qt::black); // Black - themePalette.setColor(QPalette::Base, QColor(230, 230, 230, 80)); // Grayish - themePalette.setColor(QPalette::ToolTipBase, Qt::black); // Black - themePalette.setColor(QPalette::ToolTipText, Qt::black); // Black - themePalette.setColor(QPalette::Text, Qt::black); // Black - themePalette.setColor(QPalette::Button, QColor(240, 240, 240)); // Light gray - themePalette.setColor(QPalette::ButtonText, Qt::black); // Black - themePalette.setColor(QPalette::BrightText, Qt::red); // Red - themePalette.setColor(QPalette::Link, QColor(42, 130, 218)); // Blue - themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); // Blue - themePalette.setColor(QPalette::HighlightedText, Qt::white); // White + themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray + themePalette.setColor(QPalette::WindowText, Qt::black); // Black + themePalette.setColor(QPalette::Base, QColor(230, 230, 230, 80)); // Grayish + themePalette.setColor(QPalette::ToolTipBase, QColor(230, 230, 230, 80)); // Grayish + themePalette.setColor(QPalette::ToolTipText, Qt::black); // Black + themePalette.setColor(QPalette::Text, Qt::black); // Black + themePalette.setColor(QPalette::Button, QColor(240, 240, 240)); // Light gray + themePalette.setColor(QPalette::ButtonText, Qt::black); // Black + themePalette.setColor(QPalette::BrightText, Qt::red); // Red + themePalette.setColor(QPalette::Link, QColor(42, 130, 218)); // Blue + themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); // Blue + themePalette.setColor(QPalette::HighlightedText, Qt::white); // White qApp->setPalette(themePalette); break; case Theme::Green: @@ -61,8 +62,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(25, 40, 25)); // Darker green base themePalette.setColor(QPalette::AlternateBase, - QColor(53, 69, 53)); // Dark green alternate base - themePalette.setColor(QPalette::ToolTipBase, Qt::white); // White tooltip background + QColor(53, 69, 53)); // Dark green alternate base + themePalette.setColor(QPalette::ToolTipBase, + QColor(25, 40, 25)); // White tooltip background themePalette.setColor(QPalette::ToolTipText, Qt::white); // White tooltip text themePalette.setColor(QPalette::Text, Qt::white); // White text themePalette.setColor(QPalette::Button, QColor(53, 69, 53)); // Dark green button @@ -84,8 +86,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(20, 40, 60)); // Darker blue base themePalette.setColor(QPalette::AlternateBase, - QColor(40, 60, 90)); // Dark blue alternate base - themePalette.setColor(QPalette::ToolTipBase, Qt::white); // White tooltip background + QColor(40, 60, 90)); // Dark blue alternate base + themePalette.setColor(QPalette::ToolTipBase, + QColor(20, 40, 60)); // White tooltip background themePalette.setColor(QPalette::ToolTipText, Qt::white); // White tooltip text themePalette.setColor(QPalette::Text, Qt::white); // White text themePalette.setColor(QPalette::Button, QColor(40, 60, 90)); // Dark blue button @@ -108,8 +111,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(80, 30, 90)); // Darker violet base themePalette.setColor(QPalette::AlternateBase, - QColor(100, 50, 120)); // Violet alternate base - themePalette.setColor(QPalette::ToolTipBase, Qt::white); // White tooltip background + QColor(100, 50, 120)); // Violet alternate base + themePalette.setColor(QPalette::ToolTipBase, + QColor(80, 30, 90)); // White tooltip background themePalette.setColor(QPalette::ToolTipText, Qt::white); // White tooltip text themePalette.setColor(QPalette::Text, Qt::white); // White text themePalette.setColor(QPalette::Button, QColor(100, 50, 120)); // Violet button @@ -132,7 +136,7 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, QColor(249, 245, 215)); themePalette.setColor(QPalette::Base, QColor(29, 32, 33)); themePalette.setColor(QPalette::AlternateBase, QColor(50, 48, 47)); - themePalette.setColor(QPalette::ToolTipBase, QColor(249, 245, 215)); + themePalette.setColor(QPalette::ToolTipBase, QColor(29, 32, 33)); themePalette.setColor(QPalette::ToolTipText, QColor(249, 245, 215)); themePalette.setColor(QPalette::Text, QColor(249, 245, 215)); themePalette.setColor(QPalette::Button, QColor(40, 40, 40)); @@ -154,7 +158,7 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, QColor(192, 202, 245)); themePalette.setColor(QPalette::Base, QColor(25, 28, 39)); themePalette.setColor(QPalette::AlternateBase, QColor(36, 40, 59)); - themePalette.setColor(QPalette::ToolTipBase, QColor(192, 202, 245)); + themePalette.setColor(QPalette::ToolTipBase, QColor(25, 28, 39)); themePalette.setColor(QPalette::ToolTipText, QColor(192, 202, 245)); themePalette.setColor(QPalette::Text, QColor(192, 202, 245)); themePalette.setColor(QPalette::Button, QColor(30, 30, 41)); @@ -165,5 +169,29 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::HighlightedText, Qt::black); qApp->setPalette(themePalette); break; + case Theme::Oled: + mw_searchbar->setStyleSheet("QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); + themePalette.setColor(QPalette::Window, Qt::black); + themePalette.setColor(QPalette::WindowText, Qt::white); + themePalette.setColor(QPalette::Base, Qt::black); + themePalette.setColor(QPalette::AlternateBase, Qt::black); + themePalette.setColor(QPalette::ToolTipBase, Qt::black); + themePalette.setColor(QPalette::ToolTipText, Qt::white); + themePalette.setColor(QPalette::Text, Qt::white); + themePalette.setColor(QPalette::Button, QColor(5, 5, 5)); + themePalette.setColor(QPalette::ButtonText, Qt::white); + themePalette.setColor(QPalette::BrightText, Qt::red); + themePalette.setColor(QPalette::Link, QColor(42, 130, 218)); + themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); + themePalette.setColor(QPalette::HighlightedText, Qt::black); + qApp->setPalette(themePalette); + qApp->setStyleSheet("QLineEdit {" + "background-color: #000000; color: #ffffff; border: 1px solid #a0a0a0; " + "border-radius: 4px; padding: 5px; }" + + "QCheckBox::indicator:unchecked {" + "border: 1px solid #808080; border-radius: 4px; }"); + break; } } \ No newline at end of file diff --git a/src/qt_gui/main_window_themes.h b/src/qt_gui/main_window_themes.h index 0ec2cce58..babde0f27 100644 --- a/src/qt_gui/main_window_themes.h +++ b/src/qt_gui/main_window_themes.h @@ -7,7 +7,7 @@ #include #include -enum class Theme : int { Dark, Light, Green, Blue, Violet, Gruvbox, TokyoNight }; +enum class Theme : int { Dark, Light, Green, Blue, Violet, Gruvbox, TokyoNight, Oled }; class WindowThemes : public QObject { Q_OBJECT diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index e74ffcacb..c4f47b636 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -20,6 +20,7 @@ public: QAction* setIconSizeSmallAct; QAction* setIconSizeMediumAct; QAction* setIconSizeLargeAct; + QAction* toggleLabelsAct; QAction* setlistModeListAct; QAction* setlistModeGridAct; QAction* setlistElfAct; @@ -27,6 +28,7 @@ public: QAction* downloadCheatsPatchesAct; QAction* dumpGameListAct; QAction* pkgViewerAct; + QAction* trophyViewerAct; #ifdef ENABLE_UPDATER QAction* updaterAct; #endif @@ -39,6 +41,7 @@ public: QAction* setThemeViolet; QAction* setThemeGruvbox; QAction* setThemeTokyoNight; + QAction* setThemeOled; QWidget* centralWidget; QLineEdit* mw_searchbar; QPushButton* playButton; @@ -48,6 +51,8 @@ public: QPushButton* settingsButton; QPushButton* controllerButton; QPushButton* keyboardButton; + QPushButton* fullscreenButton; + QPushButton* restartButton; QWidget* sizeSliderContainer; QHBoxLayout* sizeSliderContainer_layout; @@ -102,7 +107,15 @@ public: showGameListAct->setCheckable(true); refreshGameListAct = new QAction(MainWindow); refreshGameListAct->setObjectName("refreshGameListAct"); - refreshGameListAct->setIcon(QIcon(":images/refresh_icon.png")); + refreshGameListAct->setIcon(QIcon(":images/refreshlist_icon.png")); + + toggleLabelsAct = new QAction(MainWindow); + toggleLabelsAct->setObjectName("toggleLabelsAct"); + toggleLabelsAct->setText( + QCoreApplication::translate("MainWindow", "Show Labels Under Icons")); + toggleLabelsAct->setCheckable(true); + toggleLabelsAct->setChecked(Config::getShowLabelsUnderIcons()); + setIconSizeTinyAct = new QAction(MainWindow); setIconSizeTinyAct->setObjectName("setIconSizeTinyAct"); setIconSizeTinyAct->setCheckable(true); @@ -138,6 +151,10 @@ public: pkgViewerAct = new QAction(MainWindow); pkgViewerAct->setObjectName("pkgViewer"); pkgViewerAct->setIcon(QIcon(":images/file_icon.png")); + trophyViewerAct = new QAction(MainWindow); + trophyViewerAct->setObjectName("trophyViewer"); + trophyViewerAct->setIcon(QIcon(":images/trophy_icon.png")); + #ifdef ENABLE_UPDATER updaterAct = new QAction(MainWindow); updaterAct->setObjectName("updaterAct"); @@ -171,6 +188,9 @@ public: setThemeTokyoNight = new QAction(MainWindow); setThemeTokyoNight->setObjectName("setThemeTokyoNight"); setThemeTokyoNight->setCheckable(true); + setThemeOled = new QAction(MainWindow); + setThemeOled->setObjectName("setThemeOled"); + setThemeOled->setCheckable(true); centralWidget = new QWidget(MainWindow); centralWidget->setObjectName("centralWidget"); sizePolicy.setHeightForWidth(centralWidget->sizePolicy().hasHeightForWidth()); @@ -201,20 +221,28 @@ public: stopButton->setIconSize(QSize(40, 40)); refreshButton = new QPushButton(centralWidget); refreshButton->setFlat(true); - refreshButton->setIcon(QIcon(":images/refresh_icon.png")); - refreshButton->setIconSize(QSize(32, 32)); + refreshButton->setIcon(QIcon(":images/refreshlist_icon.png")); + refreshButton->setIconSize(QSize(40, 40)); + fullscreenButton = new QPushButton(centralWidget); + fullscreenButton->setFlat(true); + fullscreenButton->setIcon(QIcon(":images/fullscreen_icon.png")); + fullscreenButton->setIconSize(QSize(38, 38)); settingsButton = new QPushButton(centralWidget); settingsButton->setFlat(true); settingsButton->setIcon(QIcon(":images/settings_icon.png")); - settingsButton->setIconSize(QSize(44, 44)); + settingsButton->setIconSize(QSize(40, 40)); controllerButton = new QPushButton(centralWidget); controllerButton->setFlat(true); controllerButton->setIcon(QIcon(":images/controller_icon.png")); - controllerButton->setIconSize(QSize(40, 40)); + controllerButton->setIconSize(QSize(55, 48)); keyboardButton = new QPushButton(centralWidget); keyboardButton->setFlat(true); keyboardButton->setIcon(QIcon(":images/keyboard_icon.png")); - keyboardButton->setIconSize(QSize(48, 44)); + keyboardButton->setIconSize(QSize(50, 50)); + restartButton = new QPushButton(centralWidget); + restartButton->setFlat(true); + restartButton->setIcon(QIcon(":images/restart_game_icon.png")); + restartButton->setIconSize(QSize(40, 40)); sizeSliderContainer = new QWidget(centralWidget); sizeSliderContainer->setObjectName("sizeSliderContainer"); @@ -295,6 +323,7 @@ public: menuView->addAction(refreshGameListAct); menuView->addAction(menuGame_List_Mode->menuAction()); menuView->addAction(menuGame_List_Icons->menuAction()); + menuView->addAction(toggleLabelsAct); menuView->addAction(menuThemes->menuAction()); menuThemes->addAction(setThemeDark); menuThemes->addAction(setThemeLight); @@ -303,6 +332,7 @@ public: menuThemes->addAction(setThemeViolet); menuThemes->addAction(setThemeGruvbox); menuThemes->addAction(setThemeTokyoNight); + menuThemes->addAction(setThemeOled); menuGame_List_Icons->addAction(setIconSizeTinyAct); menuGame_List_Icons->addAction(setIconSizeSmallAct); menuGame_List_Icons->addAction(setIconSizeMediumAct); @@ -316,6 +346,7 @@ public: menuUtils->addAction(downloadCheatsPatchesAct); menuUtils->addAction(dumpGameListAct); menuUtils->addAction(pkgViewerAct); + menuUtils->addAction(trophyViewerAct); #ifdef ENABLE_UPDATER menuHelp->addAction(updaterAct); #endif @@ -374,6 +405,8 @@ public: dumpGameListAct->setText( QCoreApplication::translate("MainWindow", "Dump Game List", nullptr)); pkgViewerAct->setText(QCoreApplication::translate("MainWindow", "PKG Viewer", nullptr)); + trophyViewerAct->setText( + QCoreApplication::translate("MainWindow", "Trophy Viewer", nullptr)); mw_searchbar->setPlaceholderText( QCoreApplication::translate("MainWindow", "Search...", nullptr)); menuFile->setTitle(QCoreApplication::translate("MainWindow", "File", nullptr)); @@ -393,6 +426,7 @@ public: setThemeViolet->setText(QCoreApplication::translate("MainWindow", "Violet", nullptr)); setThemeGruvbox->setText("Gruvbox"); setThemeTokyoNight->setText("Tokyo Night"); + setThemeOled->setText("OLED"); toolBar->setWindowTitle(QCoreApplication::translate("MainWindow", "toolBar", nullptr)); } // retranslateUi }; diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 9a946658f..d789f6f48 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "common/config.h" @@ -225,6 +226,17 @@ SettingsDialog::SettingsDialog(std::span physical_devices, Config::setShowBackgroundImage(state == Qt::Checked); }); } + + // User TAB + { + connect(ui->OpenCustomTrophyLocationButton, &QPushButton::clicked, this, []() { + QString userPath; + Common::FS::PathToQString(userPath, + Common::FS::GetUserPath(Common::FS::PathType::CustomTrophy)); + QDesktopServices::openUrl(QUrl::fromLocalFile(userPath)); + }); + } + // Input TAB { connect(ui->hideCursorComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -234,12 +246,13 @@ SettingsDialog::SettingsDialog(std::span physical_devices, // PATH TAB { connect(ui->addFolderButton, &QPushButton::clicked, this, [this]() { - const auto config_dir = Config::getGameInstallDirs(); QString file_path_string = QFileDialog::getExistingDirectory(this, tr("Directory to install games")); auto file_path = Common::FS::PathFromQString(file_path_string); - if (!file_path.empty() && Config::addGameInstallDir(file_path)) { + if (!file_path.empty() && Config::addGameInstallDir(file_path, true)) { QListWidgetItem* item = new QListWidgetItem(file_path_string); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); ui->gameFoldersListWidget->addItem(item); } }); @@ -273,6 +286,21 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->currentSaveDataPath->setText(save_data_path_string); } }); + + connect(ui->PortableUserButton, &QPushButton::clicked, this, []() { + QString userDir; + Common::FS::PathToQString(userDir, std::filesystem::current_path() / "user"); + if (std::filesystem::exists(std::filesystem::current_path() / "user")) { + QMessageBox::information(NULL, tr("Cannot create portable user folder"), + tr("%1 already exists").arg(userDir)); + } else { + std::filesystem::copy(Common::FS::GetUserPath(Common::FS::PathType::UserDir), + std::filesystem::current_path() / "user", + std::filesystem::copy_options::recursive); + QMessageBox::information(NULL, tr("Portable user folder created"), + tr("%1 successfully created.").arg(userDir)); + } + }); } // DEBUG TAB @@ -280,8 +308,8 @@ SettingsDialog::SettingsDialog(std::span physical_devices, connect(ui->OpenLogLocationButton, &QPushButton::clicked, this, []() { QString userPath; Common::FS::PathToQString(userPath, - Common::FS::GetUserPath(Common::FS::PathType::UserDir)); - QDesktopServices::openUrl(QUrl::fromLocalFile(userPath + "/log")); + Common::FS::GetUserPath(Common::FS::PathType::LogDir)); + QDesktopServices::openUrl(QUrl::fromLocalFile(userPath)); }); } @@ -308,6 +336,9 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this); ui->updateCompatibilityButton->installEventFilter(this); + // User + ui->OpenCustomTrophyLocationButton->installEventFilter(this); + // Input ui->hideCursorGroupBox->installEventFilter(this); ui->idleTimeoutGroupBox->installEventFilter(this); @@ -330,6 +361,7 @@ SettingsDialog::SettingsDialog(std::span physical_devices, ui->saveDataGroupBox->installEventFilter(this); ui->currentSaveDataPath->installEventFilter(this); ui->browseButton->installEventFilter(this); + ui->PortableUserFolderGroupBox->installEventFilter(this); // Debug ui->debugDump->installEventFilter(this); @@ -399,10 +431,19 @@ void SettingsDialog::LoadValuesFromConfig() { ui->vblankSpinBox->setValue(toml::find_or(data, "GPU", "vblankDivider", 1)); ui->dumpShadersCheckBox->setChecked(toml::find_or(data, "GPU", "dumpShaders", false)); ui->nullGpuCheckBox->setChecked(toml::find_or(data, "GPU", "nullGpu", false)); - ui->enableHDRCheckBox->setChecked(toml::find_or(data, "General", "allowHDR", false)); + ui->enableHDRCheckBox->setChecked(toml::find_or(data, "GPU", "allowHDR", false)); ui->playBGMCheckBox->setChecked(toml::find_or(data, "General", "playBGM", false)); ui->disableTrophycheckBox->setChecked( toml::find_or(data, "General", "isTrophyPopupDisabled", false)); + ui->popUpDurationSpinBox->setValue(Config::getTrophyNotificationDuration()); + + QString side = QString::fromStdString(Config::sideTrophy()); + + ui->radioButton_Left->setChecked(side == "left"); + ui->radioButton_Right->setChecked(side == "right"); + ui->radioButton_Top->setChecked(side == "top"); + ui->radioButton_Bottom->setChecked(side == "bottom"); + ui->BGMVolumeSlider->setValue(toml::find_or(data, "General", "BGMvolume", 50)); ui->discordRPCCheckbox->setChecked( toml::find_or(data, "General", "enableDiscordRPC", true)); @@ -593,6 +634,11 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("Update Compatibility Database:\\nImmediately update the compatibility database."); } + //User + if (elementName == "OpenCustomTrophyLocationButton") { + text = tr("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."); + } + // Input if (elementName == "hideCursorGroupBox") { text = tr("Hide Cursor:\\nChoose when the cursor will disappear:\\nNever: You will always see the mouse.\\nidle: Set a time for it to disappear after being idle.\\nAlways: you will never see the mouse."); @@ -622,6 +668,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("Add:\\nAdd a folder to the list."); } else if (elementName == "removeFolderButton") { text = tr("Remove:\\nRemove a folder from the list."); + } else if (elementName == "PortableUserFolderGroupBox") { + text = tr("Portable user folder:\\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it."); } // Save Data @@ -683,6 +731,18 @@ void SettingsDialog::UpdateSettings() { screenModeMap.value(ui->displayModeComboBox->currentText()).toStdString()); Config::setIsMotionControlsEnabled(ui->motionControlsCheckBox->isChecked()); Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked()); + Config::setTrophyNotificationDuration(ui->popUpDurationSpinBox->value()); + + if (ui->radioButton_Top->isChecked()) { + Config::setSideTrophy("top"); + } else if (ui->radioButton_Left->isChecked()) { + Config::setSideTrophy("left"); + } else if (ui->radioButton_Right->isChecked()) { + Config::setSideTrophy("right"); + } else if (ui->radioButton_Bottom->isChecked()) { + Config::setSideTrophy("bottom"); + } + Config::setPlayBGM(ui->playBGMCheckBox->isChecked()); Config::setAllowHDR(ui->enableHDRCheckBox->isChecked()); Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString()); @@ -724,6 +784,17 @@ void SettingsDialog::UpdateSettings() { emit BackgroundOpacityChanged(ui->backgroundImageOpacitySlider->value()); Config::setShowBackgroundImage(ui->showBackgroundImageCheckBox->isChecked()); + std::vector dirs_with_states; + for (int i = 0; i < ui->gameFoldersListWidget->count(); i++) { + QListWidgetItem* item = ui->gameFoldersListWidget->item(i); + QString path_string = item->text(); + auto path = Common::FS::PathFromQString(path_string); + bool enabled = (item->checkState() == Qt::Checked); + + dirs_with_states.push_back({path, enabled}); + } + Config::setAllGameInstallDirs(dirs_with_states); + #ifdef ENABLE_DISCORD_RPC auto* rpc = Common::Singleton::Instance(); if (Config::getEnableDiscordRPC()) { @@ -738,6 +809,7 @@ void SettingsDialog::UpdateSettings() { } void SettingsDialog::ResetInstallFolders() { + ui->gameFoldersListWidget->clear(); std::filesystem::path userdir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); const toml::value data = toml::parse(userdir / "config.toml"); @@ -746,21 +818,36 @@ void SettingsDialog::ResetInstallFolders() { const toml::value& gui = data.at("GUI"); const auto install_dir_array = toml::find_or>(gui, "installDirs", {}); - std::vector settings_install_dirs_config = {}; - for (const auto& dir : install_dir_array) { - if (std::find(settings_install_dirs_config.begin(), settings_install_dirs_config.end(), - dir) == settings_install_dirs_config.end()) { - settings_install_dirs_config.push_back(dir); - } + std::vector install_dirs_enabled; + try { + install_dirs_enabled = Config::getGameInstallDirsEnabled(); + } catch (...) { + // If it does not exist, assume that all are enabled. + install_dirs_enabled.resize(install_dir_array.size(), true); } - for (const auto& dir : settings_install_dirs_config) { + if (install_dirs_enabled.size() < install_dir_array.size()) { + install_dirs_enabled.resize(install_dir_array.size(), true); + } + + std::vector settings_install_dirs_config; + + for (size_t i = 0; i < install_dir_array.size(); i++) { + std::filesystem::path dir = install_dir_array[i]; + bool enabled = install_dirs_enabled[i]; + + settings_install_dirs_config.push_back({dir, enabled}); + QString path_string; Common::FS::PathToQString(path_string, dir); + QListWidgetItem* item = new QListWidgetItem(path_string); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked); ui->gameFoldersListWidget->addItem(item); } - Config::setGameInstallDirs(settings_install_dirs_config); + + Config::setAllGameInstallDirs(settings_install_dirs_config); } } diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 2df328fbe..5600a0db7 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -31,7 +31,7 @@ Settings - + :/images/shadps4.ico:/images/shadps4.ico @@ -59,7 +59,7 @@ - 6 + 5 @@ -73,8 +73,8 @@ 0 0 - 718 - 332 + 946 + 536 @@ -130,9 +130,6 @@ 9 - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - @@ -454,8 +451,8 @@ 0 0 - 646 - 395 + 946 + 536 @@ -903,8 +900,8 @@ 0 0 - 545 - 141 + 946 + 536 @@ -1198,8 +1195,8 @@ 0 0 - 234 - 292 + 946 + 536 @@ -1264,30 +1261,128 @@ - Disable Trophy Pop-ups + Disable Trophy Notification - + + + 0 + + + + + + 0 + 0 + + + + Trophy Notification Position + + + + + + + Left + + + + + + + Right + + + + + + + Top + + + + + + + Bottom + + + + + + + + + 0 + + + + + + 0 + 0 + + + + Notification Duration + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + Trophy Key + + + + + + + + 0 + 0 + + + + + 10 + false + + + + + + + + - Trophy Key - - - - - - - - 0 - 0 - - - - - 10 - false - + Open the custom trophy images/sounds folder @@ -1342,8 +1437,8 @@ 0 0 - 455 - 252 + 946 + 536 @@ -1626,8 +1721,8 @@ 0 0 - 216 - 254 + 946 + 536 @@ -1701,6 +1796,58 @@ + + + + Portable User Folder + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Create Portable User Folder from Common User Folder + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + @@ -1969,6 +2116,8 @@ - + + + diff --git a/src/qt_gui/translations/ar_SA.ts b/src/qt_gui/translations/ar_SA.ts index f1b16d4b0..ac6920ea0 100644 --- a/src/qt_gui/translations/ar_SA.ts +++ b/src/qt_gui/translations/ar_SA.ts @@ -22,7 +22,7 @@ CheatsPatches Cheats / Patches for - Cheats / Patches for + الغِشّ / التصحيحات Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n @@ -407,139 +407,210 @@ ControlSettings Configure Controls - Configure Controls + تعديل عناصر التحكم D-Pad - D-Pad + الأسهم+عصا التحكم Up - Up + فوق Left - Left + يسار Right - Right + يمين Down - Down + تحت Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + مدى تسجيل الإدخال للعصا اليسرى (التلقائي:2 حد أقصى:127) Left Deadzone - Left Deadzone + إعدادات مدى تسجيل الإدخال لعصا التحكم اليسرى Left Stick - Left Stick + عصا التحكم اليسرى Config Selection - Config Selection + تحديد الإعدادات Common Config - Common Config + إعدادات عامة Use per-game configs - Use per-game configs + استخدام إعدادات كل لُعْبَة L1 / LB - L1 / LB + L1 / LB L2 / LT - L2 / LT + L2 / LT Back - Back + رجوع R1 / RB - R1 / RB + R1 / RB R2 / RT - R2 / RT + R2 / RT L3 - L3 + L3 Options / Start - Options / Start + الخيارات / البَدْء R3 - R3 + R3 Face Buttons - Face Buttons + الأزرار Triangle / Y - Triangle / Y + مثلث / Y Square / X - Square / X + مربع / X Circle / B - Circle / B + دائرة / B Cross / A - Cross / A + إكس / A Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + مدى تسجيل الإدخال للعصا اليمنى (التلقائي:2 حد أقصى:127) Right Deadzone - Right Deadzone + إعدادات مدى تسجيل الإدخال لعصا التحكم اليمنى Right Stick - Right Stick + عصا التحكم اليمنى Color Adjustment - Color Adjustment + تعديل الألوان R: - R: + أحمر: G: - G: + أخضر: B: - B: + أزرق: Override Lightbar Color - Override Lightbar Color + تجاوز لون شريط الإضاءة Override Color - Override Color + تجاوز اللون + + + Unable to Save + غير قادر على الحفظ + + + Cannot bind axis values more than once + لا يمكن ربط قيم المحور أكثر من مرة + + + Save + حفظ + + + Apply + تطبيق + + + Restore Defaults + استعادة الإعدادات الافتراضية + + + Cancel + إلغاء + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + تحرير أزرار الإدخال للوحة المفاتيح و الفأرة ووحدة التحكم + + + Use Per-Game configs + استخدام إعدادات كل لُعْبَة + + + Error + خطأ + + + Could not open the file for reading + تعذر فتح المِلَفّ للقراءة + + + Could not open the file for writing + تعذر فتح المِلَفّ للكتابة + + + Save Changes + حفظ التغييرات + + + Do you want to save changes? + هل تريد حفظ التغييرات؟ + + + Help + المساعدة + + + Do you want to reset your custom default config to the original default config? + هل تريد إعادة تعيين الإعدادات الافتراضية المخصصة الخاصة بك إلى الإعدادات الافتراضية الأصلية؟ + + + Do you want to reset this config to your custom default config? + هل تريد إعادة تعيين هذا الإعداد إلى الإعداد الافتراضي المخصص لك؟ + + + Reset to Default + إعادة تعيين إلى الافتراضي @@ -584,7 +655,7 @@ Directory to install DLC - Directory to install DLC + مكان تثبيت حزمات DLC @@ -603,7 +674,7 @@ Compatibility - Compatibility + التوافق Region @@ -631,43 +702,43 @@ Never Played - Never Played + لم تلعب أبداً h - h + ا m - m + ة s - s + ثانية/ثواني Compatibility is untested - Compatibility is untested + التوافق غير مختبر Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + اللعبة لا تهيئ بشكل صحيح / تعطل المحاكي Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + اللعبة تبدأ بالعمل، ولكن فقط تعرض شاشة فارغة Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + اللعبة تعرض صورة ولكن لا تتجاوز القائمة Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + اللعبة بها قلتشات أو أداء غير قابل للتشغيل Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + يمكن الانتهاء من اللعبة مع الأداء القابل للتشغيل و لا توجد قلتشات كبيرة Click to see details on github @@ -682,23 +753,23 @@ GameListUtils B - B + بايت KB - KB + كيلو بايت MB - MB + ميغابايت GB - GB + جيجابايت TB - TB + تيرابايت @@ -749,11 +820,11 @@ Copy Version - Copy Version + إصدار النسخة Copy Size - Copy Size + حجم النسخة Copy All @@ -761,35 +832,39 @@ Delete... - Delete... + حذف... Delete Game - Delete Game + حذف اللعبة Delete Update - Delete Update + حذف التحديث Delete DLC - Delete DLC + حذف DLC + + + Delete Trophy + حذف الكؤوس Compatibility... - Compatibility... + التوافق... Update database - Update database + تحديث قاعدة البيانات View report - View report + عرض التقرير Submit a report - Submit a report + إرسال بلاغ Shortcut creation @@ -813,59 +888,94 @@ Game - Game + اللعبة This game has no update to delete! - This game has no update to delete! + لا تحتوي اللعبة على تحديث لحذفه! Update - Update + تحديث This game has no DLC to delete! - This game has no DLC to delete! + لا تحتوي اللعبة على DLC لحذفه! DLC - DLC + DLC Delete %1 - Delete %1 + حذف %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + هل أنت متأكد من أنك تريد حذف دليل %1's %2؟ Open Update Folder - Open Update Folder + فتح مجلد التحديث Delete Save Data - Delete Save Data + حذف التخزينه This game has no update folder to open! - This game has no update folder to open! + لا تحتوي اللعبة على تحديث لفتحه! + + + No log file found for this game! + لم يتم العثور على ملف سجل لهذه اللعبة! Failed to convert icon. - Failed to convert icon. + فشل تحويل الأيقونة. This game has no save data to delete! - This game has no save data to delete! + هذه اللعبة لا تحتوي على أي تخزينات لحذفها! + + + This game has no saved trophies to delete! + هذه اللعبة ليس لديها كؤوس محفوظة للحذف! Save Data - Save Data + حفظ البيانات + + + Trophy + الكؤوس SFO Viewer for - SFO Viewer for + عارض SFO لـ + + + + HelpDialog + + Quickstart + التشغيل السريع + + + FAQ + الأسئلة الأكثر شيوعاً + + + Syntax + الصّيغة + + + Special Bindings + إدخالات خاصة + + + Keybindings + أزرار التحكم @@ -876,15 +986,226 @@ Select which directory you want to install to. - Select which directory you want to install to. + حدد الدليل الذي تريد تثبيت إليه. Install All Queued to Selected Folder - Install All Queued to Selected Folder + تثبيت كل قائمة الانتظار إلى المجلد المحدد Delete PKG File on Install - Delete PKG File on Install + حذف مِلَفّ PKG عند التثبيت + + + + KBMSettings + + Configure Controls + تعديل عناصر التحكم + + + D-Pad + الأسهم+عصا التحكم + + + Up + أعلى + + + unmapped + غير معين + + + Left + يسار + + + Right + يمين + + + Down + أسفل + + + Left Analog Halfmode + تقليل سرعة عصا التحكم اليسرى للنصف + + + hold to move left stick at half-speed + الاستمرار للتحرك إلى اليسار بنصف السرعة + + + Left Stick + عصا التحكم اليسرى + + + Config Selection + تحديد الإعدادات + + + Common Config + إعدادات عامة + + + Use per-game configs + استخدام إعدادات كل لُعْبَة + + + L1 + L1 + + + L2 + L2 + + + Text Editor + محرر النص + + + Help + المساعدة + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + النقر على لوحة اللمس + + + Mouse to Joystick + الفأرة إلى عصا التحكم + + + *press F7 ingame to activate + * اضغط على F7 للتفعيل + + + R3 + R3 + + + Options + الخيارات + + + Mouse Movement Parameters + معطيات حركة الفأرة + + + note: click Help Button/Special Keybindings for more information + ملاحظة: انقر فوق زر المساعدة/روابط المفاتيح الخاصة للحصول على مزيد من المعلومات + + + Face Buttons + أزرار الوجه + + + Triangle + مثلث + + + Square + مربع + + + Circle + دائرة + + + Cross + اكس + + + Right Analog Halfmode + تقليل سرعة عصا التحكم اليمنى للنصف + + + hold to move right stick at half-speed + الضغط باستمرار لتحريك العصا اليمنى بنصف السرعة + + + Right Stick + عصا التحكم اليمنى + + + Speed Offset (def 0.125): + إزاحة السرعة (تلقائي 0.125): + + + Copy from Common Config + نسخ من الإعدادات الشائعة + + + Deadzone Offset (def 0.50): + + + + Speed Multiplier (def 1.0): + معدل مضاعفة السرعة (التلقائي 1.0): + + + Common Config Selected + الإعدادات الشائعة محدده + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + نسخ من الإعدادات الشائعة + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + غير قادر على الحفظ + + + Cannot bind any unique input more than once + لا يمكن ربط أي إدخال فريد أكثر من مرة + + + Press a key + اضغط على مفتاح + + + Cannot set mapping + لا يمكن تعيين الأزرار + + + Mousewheel cannot be mapped to stick outputs + عجلة الفأرة لا يمكن تعيينها لعصا التحكم + + + Save + حفظ + + + Apply + تطبيق + + + Restore Defaults + استعادة الإعدادات الافتراضية + + + Cancel + إلغاء @@ -923,7 +1244,7 @@ Open shadPS4 Folder - Open shadPS4 Folder + فتح مجلد shadPS4 Exit @@ -985,6 +1306,14 @@ Dump Game List تفريغ قائمة الألعاب + + Trophy Viewer + عارض الجوائز + + + No games found. Please add your games to your library first. + لم يتم العثور على ألعاب. الرجاء إضافة ألعابك إلى مكتبتك أولاً. + PKG Viewer عارض PKG @@ -1163,27 +1492,27 @@ Run Game - Run Game + تشغيل اللعبة Eboot.bin file not found - Eboot.bin file not found + لم يتم العثور على ملف Eboot.bin PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) + ملف PKG (*.PKG *.pkg) PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! + PKG هو تصحيح أو DLC، يرجى تثبيت اللعبة أولاً! Game is already running! - Game is already running! + اللعبة قيد التشغيل بالفعل! shadPS4 - shadPS4 + shadPS4 @@ -1206,7 +1535,7 @@ Installed - Installed + مثبت Size @@ -1214,15 +1543,15 @@ Category - Category + الفئة Type - Type + النوع App Ver - App Ver + إصدار FW @@ -1309,7 +1638,11 @@ Trophy - Trophy + الكؤوس + + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1769,23 +2102,23 @@ Display Mode - Display Mode + طريقة العرض Windowed - Windowed + نافذة Fullscreen - Fullscreen + شاشة كاملة Fullscreen (Borderless) - Fullscreen (Borderless) + شاشة كاملة (دون حدود) Window Size - Window Size + حجم النافذة W: @@ -1797,12 +2130,68 @@ Separate Log Files - Separate Log Files + ملفات السجل المنفصل Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + موقع إشعار الكأس + + + Left + يسار + + + Right + يمين + + + Top + في الأعلى + + + Bottom + الأسفل + + + Notification Duration + مدة الإشعار + + + Portable User Folder + مجلد المستخدم المتنقل + + + Create Portable User Folder from Common User Folder + إنشاء مجلد مستخدم المتنقل من مجلد المستخدم الشائع + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + لا يمكن إنشاء مجلد المستخدم المتنقل + + + %1 already exists + %1 موجود مسبقاً + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + تم إنشاء %1 بنجاح. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer عارض الجوائز + + Select Game: + اختر الُعْبَه: + + + Progress + مقدار التقدُّم + + + Show Earned Trophies + عرض الكؤوس المكتسبة + + + Show Not Earned Trophies + عرض الكؤوس غير المكتسبة + + + Show Hidden Trophies + عرض الكؤوس المخفية + diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index e6af07e74..1835ba84c 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/de_DE.ts b/src/qt_gui/translations/de_DE.ts index 5d57b6361..6717a93ef 100644 --- a/src/qt_gui/translations/de_DE.ts +++ b/src/qt_gui/translations/de_DE.ts @@ -38,7 +38,7 @@ Version: - Version: + Version: Size: @@ -50,7 +50,7 @@ Repository: - Repository: + Quellen: Download Cheats @@ -86,11 +86,11 @@ Cheats - Cheats + Cheats Patches - Patches + Patches Error @@ -234,7 +234,7 @@ Name: - Name: + Name: Can't apply cheats before the game is started @@ -407,139 +407,210 @@ ControlSettings Configure Controls - Configure Controls + Steuerung einrichten D-Pad - D-Pad + Steuerkreuz Up - Up + Oben Left - Left + Links Right - Right + Rechts Down - Down + Runter Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + Linker Stick tote Zone (def:2 max:127) Left Deadzone - Left Deadzone + Linke Deadzone Left Stick - Left Stick + Linker Analogstick Config Selection - Config Selection + Konfigurationsauswahl Common Config - Common Config + Standard Konfiguration Use per-game configs - Use per-game configs + Benutze Per-Game Einstellungen L1 / LB - L1 / LB + L1 / LB L2 / LT - L2 / LT + L2 / LT Back - Back + Zurück R1 / RB - R1 / RB + R1 / RB R2 / RT - R2 / RT + R2 / RT L3 - L3 + L3 Options / Start - Options / Start + Options / Start R3 - R3 + R3 Face Buttons - Face Buttons + Aktionstasten Triangle / Y - Triangle / Y + Dreieck / Y Square / X - Square / X + Quadrat / X Circle / B - Circle / B + Kreis / B Cross / A - Cross / A + Kreuz / A Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + Rechter Stick tote Zone (def:2, max:127) Right Deadzone - Right Deadzone + Rechte tote Zone Right Stick - Right Stick + Rechter Analogstick Color Adjustment - Color Adjustment + Farbanpassung R: - R: + R: G: - G: + G: B: - B: + B: Override Lightbar Color - Override Lightbar Color + Farbe der Leuchtleiste überschreiben Override Color - Override Color + Farbe überschreiben + + + Unable to Save + Speichern nicht möglich + + + Cannot bind axis values more than once + Achsenwerte können nicht mehr als einmal gebunden werden + + + Save + Speichern + + + Apply + Übernehmen + + + Restore Defaults + Werkseinstellungen wiederherstellen + + + Cancel + Abbrechen + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Tastatur + Maus und Controller Eingabezuordnungen bearbeiten + + + Use Per-Game configs + Benutze Per-Game Einstellungen + + + Error + Fehler + + + Could not open the file for reading + Datei konnte nicht zum Lesen geöffnet werden + + + Could not open the file for writing + Datei konnte nicht zum Schreiben geöffnet werden + + + Save Changes + Änderungen Speichern + + + Do you want to save changes? + Sollen die Änderungen gespeichert werden? + + + Help + Hilfe + + + Do you want to reset your custom default config to the original default config? + Möchten Sie Ihre eigene Standardkonfiguration auf die ursprüngliche Standardkonfiguration zurücksetzen? + + + Do you want to reset this config to your custom default config? + Möchten Sie diese Konfiguration auf Ihre eigene Standardkonfiguration zurücksetzen? + + + Reset to Default + Auf Standard zurücksetzen @@ -584,7 +655,7 @@ Directory to install DLC - Directory to install DLC + Verzeichnis zum Installieren von DLC @@ -595,7 +666,7 @@ Name - Name + Name Serial @@ -607,11 +678,11 @@ Region - Region + Region Firmware - Firmware + Firmware Size @@ -619,7 +690,7 @@ Version - Version + Version Path @@ -635,15 +706,15 @@ h - h + h m - m + m s - s + s Compatibility is untested @@ -682,23 +753,23 @@ GameListUtils B - B + B KB - KB + KB MB - MB + MB GB - GB + GB TB - TB + TB @@ -709,7 +780,7 @@ Cheats / Patches - Cheats / Patches + Cheats / Patches SFO Viewer @@ -775,6 +846,10 @@ Delete DLC Lösche DLC + + Delete Trophy + Trophäe löschen + Compatibility... Kompatibilität... @@ -829,7 +904,7 @@ DLC - DLC + DLC Delete %1 @@ -849,23 +924,58 @@ This game has no update folder to open! - This game has no update folder to open! + Dieses Spiel hat keinen Update-Ordner zum öffnen! + + + No log file found for this game! + Keine Protokolldatei für dieses Spiel gefunden! Failed to convert icon. - Failed to convert icon. + Fehler beim Konvertieren des Symbols. This game has no save data to delete! - This game has no save data to delete! + Dieses Spiel hat keine Speicherdaten zum Löschen! + + + This game has no saved trophies to delete! + Dieses Spiel hat keine gespeicherten Trophäen zum Löschen! Save Data - Save Data + Gespeicherte Daten + + + Trophy + Trophäe SFO Viewer for - SFO Viewer for + SFO-Betrachter für + + + + HelpDialog + + Quickstart + Schnellstart + + + FAQ + Häufig gestellte Fragen + + + Syntax + Syntax + + + Special Bindings + Spezielle Zuordnungen + + + Keybindings + Tastenbelegung @@ -880,11 +990,222 @@ Install All Queued to Selected Folder - Install All Queued to Selected Folder + Installieren Sie alles aus der Warteschlange in den ausgewählten Ordner Delete PKG File on Install - Delete PKG File on Install + PKG-Datei beim Installieren löschen + + + + KBMSettings + + Configure Controls + Steuerung konfigurieren + + + D-Pad + Steuerkreuz + + + Up + Oben + + + unmapped + nicht zugeordnet + + + Left + Links + + + Right + Rechts + + + Down + Runter + + + Left Analog Halfmode + Linker Analog-Halbmodus + + + hold to move left stick at half-speed + Halten um den linken Analogstick mit Halbgeschwindigkeit zu bewegen + + + Left Stick + Linker Analogstick + + + Config Selection + Konfigurationsauswahl + + + Common Config + Allgemeine Konfiguration + + + Use per-game configs + Benutze Per-Game Einstellungen + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Textbearbeiter + + + Help + Hilfe + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad-Klick + + + Mouse to Joystick + Maus zu Joystick + + + *press F7 ingame to activate + *Zum Aktivieren F7 ingame drücken + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mausbewegungsparameter + + + note: click Help Button/Special Keybindings for more information + Hinweis: Klicken Sie auf Hilfe-Button/Special Tastaturbelegungen für weitere Informationen + + + Face Buttons + Aktionstasten + + + Triangle + Dreieck + + + Square + Quadrat + + + Circle + Kreis + + + Cross + Kreuz + + + Right Analog Halfmode + Rechter Analog-Halbmodus + + + hold to move right stick at half-speed + Halten um den rechten Analogstick mit Halbgeschwindigkeit zu bewegen + + + Right Stick + Rechter Analogstick + + + Speed Offset (def 0.125): + Geschwindigkeitsversatz (Def 0.125): + + + Copy from Common Config + Von allgemeiner Konfiguration kopieren + + + Deadzone Offset (def 0.50): + Tote Zone Versatz (Def 0.50): + + + Speed Multiplier (def 1.0): + Geschwindigkeit Multiplikator (def 1.0): + + + Common Config Selected + Allgemeine Konfiguration ausgewählt + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Diese Schaltfläche kopiert Zuordnungen aus der allgemeinen Konfiguration in das aktuell ausgewählte Profil, und kann nicht verwendet werden, wenn das aktuell ausgewählte Profil die allgemeine Konfiguration ist. + + + Copy values from Common Config + Werte von allgemeiner Konfiguration kopieren + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Möchten Sie die vorhandenen Zuordnungen mit den Zuordnungen der allgemeinen Konfigurationen überschreiben? + + + Unable to Save + Speichern nicht möglich + + + Cannot bind any unique input more than once + Kann keine eindeutige Eingabe mehr als einmal zuordnen + + + Press a key + Drücken Sie eine Taste + + + Cannot set mapping + Kann Zuordnung nicht festlegen + + + Mousewheel cannot be mapped to stick outputs + Mausrad kann nicht zu Analogstick Ausgabe zugeordnet werden + + + Save + Speichern + + + Apply + Übernehmen + + + Restore Defaults + Werkseinstellungen wiederherstellen + + + Cancel + Abbrechen @@ -985,6 +1306,14 @@ Dump Game List Spielliste ausgeben + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG-Anschauer @@ -1163,27 +1492,27 @@ Run Game - Run Game + Spiel ausführen Eboot.bin file not found - Eboot.bin file not found + Eboot.bin Datei nicht gefunden PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) + PKG-Datei (*.PKG *.pkg) PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! + PKG ist ein Patch oder DLC, bitte installieren Sie zuerst das Spiel! Game is already running! - Game is already running! + Spiel läuft bereits! shadPS4 - shadPS4 + shadPS4 @@ -1198,7 +1527,7 @@ Name - Name + Name Serial @@ -1206,7 +1535,7 @@ Installed - Installed + Installiert Size @@ -1214,27 +1543,27 @@ Category - Category + Kategorie Type - Type + Typ App Ver - App Ver + App Ver FW - FW + FW Region - Region + Region Flags - Flags + Markierungen Path @@ -1250,7 +1579,7 @@ Package - Package + Paket @@ -1265,7 +1594,7 @@ System - System + System Console Language @@ -1277,7 +1606,7 @@ Emulator - Emulator + Emulator Enable Separate Update Folder @@ -1311,9 +1640,13 @@ Trophy Trophäe + + Open the custom trophy images/sounds folder + Öffne den benutzerdefinierten Ordner für Trophäenbilder/Sounds + Logger - Logger + Protokollführer Log Type @@ -1333,7 +1666,7 @@ Cursor - Cursor + Mauszeiger Hide Cursor @@ -1345,11 +1678,11 @@ s - s + s Controller - Controller + Kontroller Back Button Behavior @@ -1389,7 +1722,7 @@ Enable HDR - Enable HDR + HDR aktivieren Paths @@ -1409,7 +1742,7 @@ Debug - Debug + Debug Enable Debug Dumping @@ -1476,20 +1809,20 @@ Titelmusik - Disable Trophy Pop-ups - Deaktiviere Trophäen Pop-ups + Disable Trophy Notification + Trophäen-Benachrichtigung deaktivieren Background Image - Background Image + Hintergrundbild Show Background Image - Show Background Image + Hintergrundbild anzeigen Opacity - Opacity + Transparenz Play title music @@ -1577,7 +1910,7 @@ Background Image:\nControl the opacity of the game background image. - Background Image:\nControl the opacity of the game background image. + Hintergrundbild:\nSteuere die Deckkraft des Spiel-Hintergrundbilds. Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. @@ -1661,7 +1994,7 @@ Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + HDR:\nAktiviert HDR in Spielen, die es unterstützen.\nIhr Monitor muss Unterstützung für den BT2020 PQ Farbraum und das RGB10A2 Swapchain Format haben. Game Folders:\nThe list of folders to check for installed games. @@ -1713,23 +2046,23 @@ Save Data Path:\nThe folder where game save data will be saved. - Save Data Path:\nThe folder where game save data will be saved. + Datenpfad speichern:\nDer Ordner, in dem Spieldaten gespeichert werden. Browse:\nBrowse for a folder to set as the save data path. - Browse:\nBrowse for a folder to set as the save data path. + Durchsuchen:\nDurchsuchen eines Ordners, um den Speicherdatenpfad festzulegen. Release - Release + Veröffentlichung Nightly - Nightly + Nightly Set the volume of the background music. - Set the volume of the background music. + Legen Sie die Lautstärke der Hintergrundmusik fest. Enable Motion Controls @@ -1745,15 +2078,15 @@ async - async + asynchron sync - sync + syncron Auto Select - Auto Select + Auto-Wählen Directory to install games @@ -1761,47 +2094,103 @@ Directory to save data - Directory to save data + Verzeichnis um Daten zu speichern Video - Video + Video Display Mode - Display Mode + Anzeigemodus Windowed - Windowed + Fenster Fullscreen - Fullscreen + Vollbild Fullscreen (Borderless) - Fullscreen (Borderless) + Vollbild (randlos) Window Size - Window Size + Fenstergröße W: - W: + W: H: - H: + H: Separate Log Files - Separate Log Files + Separate Protokolldateien Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Separate Protokolldateien:\nSchreibt für jedes Spiel eine separate Logdatei. + + + Trophy Notification Position + Trophäen-Benachrichtigungsposition + + + Left + Links + + + Right + Rechts + + + Top + Zuoberst + + + Bottom + Zuunterst + + + Notification Duration + Benachrichtigungsdauer + + + Portable User Folder + Portable Benutzerordner + + + Create Portable User Folder from Common User Folder + Erstellen eines portablen Benutzerordners aus dem allgemeinen Benutzer Ordner + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portablen Benutzerordner:\nspeichert ShadPS4 Einstellungen und Daten, die nur auf den ShadPS4 Build im aktuellen Ordner angewendet werden. Starten Sie die App nach dem Erstellen des tragbaren Benutzerordners neu, um sie zu verwenden. + + + Cannot create portable user folder + Kann keinen portablen Benutzerordner erstellen + + + %1 already exists + %1 existiert bereits + + + Portable user folder created + Portablen Benutzerordner erstellt + + + %1 successfully created. + %1 erfolgreich erstellt. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. @@ -1810,5 +2199,25 @@ Trophy Viewer Trophäenansicht + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/el_GR.ts b/src/qt_gui/translations/el_GR.ts index 3d2763409..6e1adaac9 100644 --- a/src/qt_gui/translations/el_GR.ts +++ b/src/qt_gui/translations/el_GR.ts @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/en_US.ts b/src/qt_gui/translations/en_US.ts index 263267aba..20cba0378 100644 --- a/src/qt_gui/translations/en_US.ts +++ b/src/qt_gui/translations/en_US.ts @@ -419,11 +419,11 @@ Left - + Left Right - + Right Down @@ -541,6 +541,77 @@ Override Color + + Unable to Save + + + + Cannot bind axis values more than once + + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + + + + Use Per-Game configs + + + + Error + Error + + + Could not open the file for reading + + + + Could not open the file for writing + + + + Save Changes + + + + Do you want to save changes? + + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + + + + Do you want to reset this config to your custom default config? + + + + Reset to Default + + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +926,10 @@ This game has no update folder to open! + + No log file found for this game! + + Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! + + This game has no saved trophies to delete! + + Save Data + + Trophy + Trophy + SFO Viewer for + + HelpDialog + + Quickstart + + + + FAQ + + + + Syntax + + + + Special Bindings + + + + Keybindings + + + InstallDirSelect @@ -887,6 +997,217 @@ + + KBMSettings + + Configure Controls + + + + D-Pad + + + + Up + + + + unmapped + + + + Left + Left + + + Right + Right + + + Down + + + + Left Analog Halfmode + + + + hold to move left stick at half-speed + + + + Left Stick + + + + Config Selection + + + + Common Config + + + + Use per-game configs + + + + L1 + + + + L2 + + + + Text Editor + + + + Help + Help + + + R1 + + + + R2 + + + + L3 + + + + Touchpad Click + + + + Mouse to Joystick + + + + *press F7 ingame to activate + + + + R3 + + + + Options + + + + Mouse Movement Parameters + + + + note: click Help Button/Special Keybindings for more information + + + + Face Buttons + + + + Triangle + + + + Square + + + + Circle + + + + Cross + + + + Right Analog Halfmode + + + + hold to move right stick at half-speed + + + + Right Stick + + + + Speed Offset (def 0.125): + + + + Copy from Common Config + + + + Deadzone Offset (def 0.50): + + + + Speed Multiplier (def 1.0): + + + + Common Config Selected + + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + + Copy values from Common Config + + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + + Unable to Save + + + + Cannot bind any unique input more than once + + + + Press a key + + + + Cannot set mapping + + + + Mousewheel cannot be mapped to stick outputs + + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + + + + Create Portable User Folder from Common User Folder + + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + + Cannot create portable user folder + + + + %1 already exists + + + + Portable user folder created + + + + %1 successfully created. + + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trophy Viewer + + Select Game: + + + + Progress + + + + Show Earned Trophies + + + + Show Not Earned Trophies + + + + Show Hidden Trophies + + diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 932d72d2a..24844d0a2 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -411,7 +411,7 @@ D-Pad - D-Pad + Botones de dirección Up @@ -419,67 +419,67 @@ Left - Left + Izquierda Right - Right + Derecha Down - Down + Abajo Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + Zona muerta del stick izquierdo (defecto: 2, máx.: 127) Left Deadzone - Left Deadzone + Zona muerta del stick izquierdo Left Stick - Left Stick + Stick izquierdo Config Selection - Config Selection + Selección de Configuraciones Common Config - Common Config + Configuración Estándar Use per-game configs - Use per-game configs + Usar configuraciones por juego L1 / LB - L1 / LB + L1/LB L2 / LT - L2 / LT + L2/LT Back - Back + Back R1 / RB - R1 / RB + R1/RB R2 / RT - R2 / RT + R2/RT L3 - L3 + L3 Options / Start - Options / Start + Options/Start R3 @@ -487,19 +487,19 @@ Face Buttons - Face Buttons + Botones de acción Triangle / Y - Triangle / Y + Triángulo/Y Square / X - Square / X + Cuadrado/X Circle / B - Circle / B + Círculo/B Cross / A @@ -507,39 +507,110 @@ Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + Zona muerta del stick derecho (defecto: 2, máx.: 127) Right Deadzone - Right Deadzone + Zona muerta del stick derecho Right Stick - Right Stick + Stick derecho Color Adjustment - Color Adjustment + Calibración de color R: - R: + R: G: - G: + V: B: - B: + A: Override Lightbar Color - Override Lightbar Color + Reemplazar el Color de la Barra de Luz Override Color - Override Color + Reemplazar Color + + + Unable to Save + No se Pudo Guardar + + + Cannot bind axis values more than once + No se pueden vincular valores del eje más de una vez + + + Save + Guardar + + + Apply + Aplicar + + + Restore Defaults + Restaurar Valores Por Defecto + + + Cancel + Cancelar + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Editar Asignaciones de Teclado + Ratón y Mando + + + Use Per-Game configs + Usar Configuraciones por Juego + + + Error + Error + + + Could not open the file for reading + No se pudo abrir el archivo para la lectura + + + Could not open the file for writing + No se pudo abrir el archivo para escritura + + + Save Changes + Guardar Cambios + + + Do you want to save changes? + ¿Quieres guardar los cambios? + + + Help + Ayuda + + + Do you want to reset your custom default config to the original default config? + ¿Desea restablecer su configuración predeterminada personalizada a la configuración por defecto original? + + + Do you want to reset this config to your custom default config? + ¿Quieres restablecer esta configuración a tu configuración por defecto personalizada? + + + Reset to Default + Resetear A Valores Originales @@ -580,11 +651,11 @@ Error - Error + Error Directory to install DLC - Directory to install DLC + Carpeta para instalar DLC @@ -603,7 +674,7 @@ Compatibility - Compatibility + Compatibilidad Region @@ -611,7 +682,7 @@ Firmware - Firmware + Firmware Size @@ -631,35 +702,35 @@ Never Played - Never Played + Nunca Jugado h - h + h m - m + m s - s + s Compatibility is untested - Compatibility is untested + Compatibilidad no comprobada Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + El juego no se inicia correctamente o cuelga el emulador Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + El juego arranca, pero se queda en blanco Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + El juego muestra imágenes, pero no va más allá de los menús Game has game-breaking glitches or unplayable performance @@ -667,7 +738,7 @@ Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + El juego puede completarse con un rendimiento jugable y sin errores de importancia Click to see details on github @@ -682,19 +753,19 @@ GameListUtils B - B + B KB - KB + KB MB - MB + MB GB - GB + GB TB @@ -717,7 +788,7 @@ Trophy Viewer - Ver trofeos + Expositor de Trofeos Open Folder... @@ -749,7 +820,7 @@ Copy Version - Copy Version + Copiar versión Copy Size @@ -761,11 +832,11 @@ Delete... - Delete... + Eliminar... Delete Game - Delete Game + Eliminar juego Delete Update @@ -773,23 +844,27 @@ Delete DLC - Delete DLC + Eliminar DLC + + + Delete Trophy + Eliminar Trofeo Compatibility... - Compatibility... + Compatibilidad... Update database - Update database + Actualizar base de datos View report - View report + Ver informe Submit a report - Submit a report + Enviar un informe Shortcut creation @@ -801,7 +876,7 @@ Error - Error + Error Error creating shortcut! @@ -813,59 +888,94 @@ Game - Game + Juego This game has no update to delete! - This game has no update to delete! + ¡Este juego no tiene actualizaciones! Update - Update + Actualización This game has no DLC to delete! - This game has no DLC to delete! + ¡Este juego no tiene DLCs! DLC - DLC + DLC Delete %1 - Delete %1 + Eliminar %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + ¿Seguro que quieres eliminar el directorio %2 de %1? Open Update Folder - Open Update Folder + Abrir carpeta de actualizaciones Delete Save Data - Delete Save Data + Eliminar datos guardados This game has no update folder to open! - This game has no update folder to open! + ¡Este juego no tiene carpeta de actualizaciones! + + + No log file found for this game! + ¡No se encontró un archivo de registro para este juego! Failed to convert icon. - Failed to convert icon. + Error al convertir el icono. This game has no save data to delete! - This game has no save data to delete! + ¡Este juego no tiene datos guardados! + + + This game has no saved trophies to delete! + ¡Este juego no tiene trofeos guardados para eliminar! Save Data - Save Data + Datos guardados + + + Trophy + Trofeo SFO Viewer for - SFO Viewer for + Visualizador de SFO para + + + + HelpDialog + + Quickstart + Inicio Rápido + + + FAQ + Preguntas Frecuentes (FAQ) + + + Syntax + Sintaxis + + + Special Bindings + Asignación de Teclas Especiales + + + Keybindings + Asignación de Teclas @@ -876,15 +986,226 @@ Select which directory you want to install to. - Select which directory you want to install to. + Selecciona el directorio de instalación. Install All Queued to Selected Folder - Install All Queued to Selected Folder + Instalar toda la cola en la carpeta seleccionada Delete PKG File on Install - Delete PKG File on Install + Eliminar archivo PKG tras la instalación + + + + KBMSettings + + Configure Controls + Configurar Controles + + + D-Pad + Cruceta + + + Up + Arriba + + + unmapped + sin vincular + + + Left + Izquierda + + + Right + Derecha + + + Down + Abajo + + + Left Analog Halfmode + Modo Reducido del Stick Izquierdo + + + hold to move left stick at half-speed + manten para mover el stick izquierdo a la mitad de la velocidad + + + Left Stick + Stick Izquierdo + + + Config Selection + Selección de Configuraciones + + + Common Config + Configuración Estándar + + + Use per-game configs + Usar configuraciones por juego + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Editor de Texto + + + Help + Ayuda + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Clic de pantalla táctil + + + Mouse to Joystick + Ratón a Joystick + + + *press F7 ingame to activate + * presiona F7 en el juego para activar + + + R3 + R3 + + + Options + Opciones + + + Mouse Movement Parameters + Parámetros Movimiento del Ratón + + + note: click Help Button/Special Keybindings for more information + nota: haga clic en Botón de ayuda/Asignación de Teclas Especiales para más información + + + Face Buttons + Botones de Acción + + + Triangle + Triángulo + + + Square + Cuadrado + + + Circle + Círculo + + + Cross + Equis + + + Right Analog Halfmode + Modo Reducido del Stick Derecho + + + hold to move right stick at half-speed + manten para mover el stick derecho a la mitad de la velocidad + + + Right Stick + Stick Derecho + + + Speed Offset (def 0.125): + Compensación de Velocidad (def 0.125): + + + Copy from Common Config + Copiar desde la Configuración Estándar + + + Deadzone Offset (def 0.50): + Zona Muerta (def 0.50): + + + Speed Multiplier (def 1.0): + Multiplicador de Velocidad (def 1.0): + + + Common Config Selected + Configuración Estándar Seleccionada + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Este botón copia mapeos de la Configuración Estándar al perfil seleccionado actualmente, y no se puede utilizar cuando el perfil seleccionado es la Configuración Estándar. + + + Copy values from Common Config + Copiar valores de la Configuración Estándar + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + ¿Quiere sobrescribir los mapeos existentes con los mapeos de la Configuración Estándar? + + + Unable to Save + No se Pudo Guardar + + + Cannot bind any unique input more than once + No se puede vincular ninguna entrada única más de una vez + + + Press a key + Pulsa una tecla + + + Cannot set mapping + No se pudo asignar el mapeo + + + Mousewheel cannot be mapped to stick outputs + La rueda del ratón no puede ser mapeada al stick + + + Save + Guardar + + + Apply + Aplicar + + + Restore Defaults + Restaurar Valores Por Defecto + + + Cancel + Cancelar @@ -923,7 +1244,7 @@ Open shadPS4 Folder - Open shadPS4 Folder + Abrir carpeta de shadPS4 Exit @@ -963,11 +1284,11 @@ List View - Vista de lista + Vista de Lista Grid View - Vista de cuadrícula + Vista de Cuadrícula Elf Viewer @@ -975,7 +1296,7 @@ Game Install Directory - Carpeta de instalación de los juegos + Carpeta de Instalación de Juegos Download Cheats/Patches @@ -983,7 +1304,15 @@ Dump Game List - Volcar lista de juegos + Volcar Lista de Juegos + + + Trophy Viewer + Expositor de Trofeos + + + No games found. Please add your games to your library first. + No se encontraron juegos. Por favor, añade tus juegos a tu biblioteca primero. PKG Viewer @@ -1003,11 +1332,11 @@ Game List Icons - Iconos de los juegos + Iconos de Juegos Game List Mode - Tipo de lista + Tipo de Lista Settings @@ -1051,7 +1380,7 @@ Game List - Lista de juegos + Lista de Juegos * Unsupported Vulkan Version @@ -1163,7 +1492,7 @@ Run Game - Run Game + Ejecutar juego Eboot.bin file not found @@ -1171,26 +1500,26 @@ PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) + Archivo PKG (*.PKG *.pkg) PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! + El archivo PKG es un parche o DLC, ¡debes instalar el juego primero! Game is already running! - Game is already running! + ¡El juego ya se está ejecutando! shadPS4 - shadPS4 + shadPS4 PKGViewer Open Folder - Abrir carpeta + Abrir Carpeta PKG ERROR @@ -1202,7 +1531,7 @@ Serial - Numero de serie + Número de Serie Installed @@ -1214,19 +1543,19 @@ Category - Category + Categoría Type - Type + Tipo App Ver - App Ver + Versión de la Aplicación FW - FW + FW Region @@ -1234,7 +1563,7 @@ Flags - Flags + Etiquetas Path @@ -1250,7 +1579,7 @@ Package - Package + Paquete @@ -1261,7 +1590,7 @@ General - General + General System @@ -1269,7 +1598,7 @@ Console Language - Idioma de la consola + Idioma de la Consola Emulator Language @@ -1281,7 +1610,7 @@ Enable Separate Update Folder - Enable Separate Update Folder + Habilitar Carpeta Independiente de Actualizaciones Default tab when opening settings @@ -1293,7 +1622,7 @@ Show Splash - Mostrar splash + Mostrar Pantalla de Bienvenida Enable Discord Rich Presence @@ -1301,15 +1630,19 @@ Username - Nombre de usuario + Nombre de Usuario Trophy Key - Trophy Key + Clave de Trofeos Trophy - Trophy + Trofeo + + + Open the custom trophy images/sounds folder + Abrir la carpeta de trofeos/sonidos personalizados Logger @@ -1317,15 +1650,15 @@ Log Type - Tipo de registro + Tipo de Registro Log Filter - Filtro de registro + Filtro de Registro Open Log Location - Abrir ubicación del registro + Abrir Ubicación del registro Input @@ -1333,19 +1666,19 @@ Cursor - Cursor + Cursor Hide Cursor - Ocultar cursor + Ocultar Cursor Hide Cursor Idle Timeout - Tiempo de espera para ocultar cursor inactivo + Tiempo de Espera para Ocultar Cursor Inactivo s - s + s Controller @@ -1353,7 +1686,7 @@ Back Button Behavior - Comportamiento del botón de retroceso + Comportamiento del Botón de Retroceso Graphics @@ -1369,7 +1702,7 @@ Graphics Device - Dispositivo gráfico + Dispositivo Gráfico Vblank Divider @@ -1381,7 +1714,7 @@ Enable Shaders Dumping - Habilitar volcado de shaders + Habilitar volcado de Shaders Enable NULL GPU @@ -1389,7 +1722,7 @@ Enable HDR - Enable HDR + Habilitar HDR Paths @@ -1397,7 +1730,7 @@ Game Folders - Carpetas de juego + Carpetas de Juegos Add... @@ -1413,39 +1746,39 @@ Enable Debug Dumping - Habilitar volcado de depuración + Habilitar Volcado de Depuración Enable Vulkan Validation Layers - Habilitar capas de validación de Vulkan + Habilitar Capas de Validación de Vulkan Enable Vulkan Synchronization Validation - Habilitar validación de sincronización de Vulkan + Habilitar Validación de Sincronización de Vulkan Enable RenderDoc Debugging - Habilitar depuración de RenderDoc + Habilitar Depuración de RenderDoc Enable Crash Diagnostics - Enable Crash Diagnostics + Habilitar Diagnóstico de Fallos Collect Shaders - Collect Shaders + Recopilar Shaders Copy GPU Buffers - Copy GPU Buffers + Copiar búferes de GPU Host Debug Markers - Host Debug Markers + Marcadores de Depuración del Host Guest Debug Markers - Guest Debug Markers + Marcadores de Depuración del Invitado Update @@ -1453,11 +1786,11 @@ Check for Updates at Startup - Buscar actualizaciones al iniciar + Buscar Actualizaciones al Iniciar Always Show Changelog - Mostrar siempre el registro de cambios + Mostrar Siempre el Registro de Cambios Update Channel @@ -1465,7 +1798,7 @@ Check for Updates - Verificar actualizaciones + Buscar Actualizaciones GUI Settings @@ -1473,15 +1806,15 @@ Title Music - Title Music + Música de título - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Desactivar Notificaciones de Trofeos Background Image - Imagen de fondo + Imagen de Fondo Show Background Image @@ -1497,19 +1830,19 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + Actualizar base de datos de compatibilidad al iniciar Game Compatibility - Game Compatibility + Compatibilidad Display Compatibility Data - Display Compatibility Data + Mostrar datos de compatibilidad Update Compatibility Database - Update Compatibility Database + Actualizar base de datos de compatibilidad Volume @@ -1533,7 +1866,7 @@ Point your mouse at an option to display its description. - Coloque el mouse sobre una opción para mostrar su descripción. + Coloque el ratón sobre una opción para mostrar su descripción. Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. @@ -1545,7 +1878,7 @@ Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. + Habilitar Carpeta Independiente de Actualizaciónes:\nHabilita el instalar actualizaciones del juego en una carpeta separada para mas facilidad en la gestión.\nPuede crearse manualmente añadiendo la actualización extraída a la carpeta del juego con el nombre "CUSA00000-UPDATE" donde el CUSA ID coincide con el ID del juego. Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. @@ -1561,7 +1894,7 @@ Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Clave de Trofeos:\nClave utilizada para descifrar trofeos. Debe obtenerse de tu consola desbloqueada.\nSolo debe contener caracteres hexadecimales. Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. @@ -1585,15 +1918,15 @@ Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Desactivar Notificaciones de trofeos:\nDesactiva las notificaciones de trofeos en el juego. El progreso de trofeos todavía puede ser rastreado usando el Expositor de Trofeos (haz clic derecho en el juego en la ventana principal). Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. - Ocultar Cursor:\nElija cuándo desaparecerá el cursor:\nNunca: Siempre verá el mouse.\nInactivo: Establezca un tiempo para que desaparezca después de estar inactivo.\nSiempre: nunca verá el mouse. + Ocultar Cursor:\nElija cuándo desaparecerá el cursor:\nNunca: Siempre verá el ratón.\nInactivo: Establezca un tiempo para que desaparezca después de estar inactivo.\nSiempre: nunca verá el ratón. Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. - Establezca un tiempo para que el mouse desaparezca después de estar inactivo. + Establezca un tiempo para que el ratón desaparezca después de estar inactivo. Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. @@ -1601,15 +1934,15 @@ Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Mostrar Datos de Compatibilidad:\nMuestra información de compatibilidad de juegos en vista de tabla. Habilite "Actualizar Compatibilidad al Iniciar" para obtener información actualizada. Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Actualizar Compatibilidad al Iniciar:\nActualiza automáticamente la base de datos de compatibilidad cuando shadPS4 se inicia. Update Compatibility Database:\nImmediately update the compatibility database. - Update Compatibility Database:\nImmediately update the compatibility database. + Actualizar Base de Datos de Compatibilidad:\nActualizar inmediatamente la base de datos de compatibilidad. Never @@ -1653,7 +1986,7 @@ Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. - Habilitar la Volcadura de Sombras:\nPor el bien de la depuración técnica, guarda las sombras del juego en una carpeta mientras se renderizan. + Habilitar la Volcadura de Shaders:\nPor el bien de la depuración técnica, guarda las sombras del juego en una carpeta mientras se renderizan. Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. @@ -1661,7 +1994,7 @@ Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + Habilitar HDR:\nHabilita HDR en juegos que lo soporten.\nTu monitor debe tener soporte para el espacio de color PQ BT2020 y el formato RGB10A2 de cadena de intercambio. Game Folders:\nThe list of folders to check for installed games. @@ -1693,51 +2026,51 @@ Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). - Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + Recopilar Shaders:\nNecesitas esto habilitado para editar shaders con el menú de depuración (Ctrl + F10). Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + Diagnóstico de cuelgues:\nCrea un archivo .yaml con información sobre el estado de Vulkan en el momento del cuelgue.\nÚtil para depurar errores de tipo 'Dispositivo perdido' . Con esto activado, deberías habilitar los marcadores de depuración de Host E Invitado.\nNo funciona en GPUs de Intel.\nNecesitas activar las Capas de Validación de Vulkan y el SDK de Vulkan para que funcione. Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. - Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + Copiar Búferes de GPU:\nSortea condiciones de carrera que implican envíos de GPU.\nPuede o no ayudar con cuelgues del tipo 0 de PM4. Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Marcadores de Depuración del Host:\n Inserta información del emulador como marcadores para comandos AMDGPU específicos, además de proporcionar nombres de depuración.\nCon esto activado, deberías habilitar el diagnóstico de fallos.\nUtil para programas como RenderDoc. Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Marcadores de Depuración del Invitado:\n Inserta cualquier marcador que el propio juego ha añadido al búfer de comandos.\nCon esto activado, deberías habilitar el diagnóstico de fallos.\nUtil para programas como RenderDoc. Save Data Path:\nThe folder where game save data will be saved. - Save Data Path:\nThe folder where game save data will be saved. + Ruta de Guardado de Datos:\nLa carpeta donde se guardarán los datos del juego. Browse:\nBrowse for a folder to set as the save data path. - Browse:\nBrowse for a folder to set as the save data path. + Buscar:\nBusque una carpeta para establecer como ruta de datos guardados. Release - Release + Principal Nightly - Nightly + Nightly Set the volume of the background music. - Set the volume of the background music. + Establece el volumen de la música de fondo. Enable Motion Controls - Enable Motion Controls + Habilitar Controles de Movimiento Save Data Path - Save Data Path + Ruta de datos guardados Browse @@ -1745,15 +2078,15 @@ async - async + asíncrono sync - sync + síncrono Auto Select - Auto Select + Selección Automática Directory to install games @@ -1761,54 +2094,130 @@ Directory to save data - Directory to save data + Directorio para guardar datos Video - Video + Vídeo Display Mode - Display Mode + Modo de Imagen Windowed - Windowed + Ventana Fullscreen - Fullscreen + Pantalla Completa Fullscreen (Borderless) - Fullscreen (Borderless) + Pantalla Completa (sin bordes) Window Size - Window Size + Tamaño de Ventana W: - W: + Ancho: H: - H: + Alto: Separate Log Files - Separate Log Files + Archivos de Registro Independientes Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Archivos de Registro Independientes:\nEscribe un archivo de registro separado para cada juego. + + + Trophy Notification Position + Posición de Notificación de Trofeos + + + Left + Izquierda + + + Right + Derecha + + + Top + Arriba + + + Bottom + Abajo + + + Notification Duration + Duración de Notificaciones + + + Portable User Folder + Carpeta Portable de Usuario + + + Create Portable User Folder from Common User Folder + Crear Carpeta Portable de Usuario a partir de la Carpeta de Usuario Estándar + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Carpeta portable de usuario:\nAlmacena la configuración de shadPS4 y los datos que se aplicarán sólo a la compilación shadPS4 ubicada en la carpeta actual. Reinicia la aplicación después de crear la carpeta portable de usuario para empezar a usarla. + + + Cannot create portable user folder + No se pudo crear la carpeta portable de usuario + + + %1 already exists + %1 ya existe + + + Portable user folder created + Carpeta Portable de Usuario Creada + + + %1 successfully created. + %1 creado correctamente. + + + 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. + 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. TrophyViewer Trophy Viewer - Vista de trofeos + Expositor de Trofeos + + + Select Game: + Selecciona un Juego: + + + Progress + Progreso + + + Show Earned Trophies + Mostrar Trofeos Obtenidos + + + Show Not Earned Trophies + Mostrar Trofeos No Obtenidos + + + Show Hidden Trophies + Mostrar Trofeos Ocultos diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index a173dc9d4..6b7af042e 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC حذف محتوای اضافی (DLC) + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List استخراج لیست بازی ها + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG مشاهده گر @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - غیرفعال کردن نمایش جوایز + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer مشاهده جوایز + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/fi_FI.ts b/src/qt_gui/translations/fi_FI.ts index bcbbd07f6..324cb6c49 100644 --- a/src/qt_gui/translations/fi_FI.ts +++ b/src/qt_gui/translations/fi_FI.ts @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Poista Lisäsisältö + + Delete Trophy + Delete Trophy + Compatibility... Yhteensopivuus... @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Kirjoita Pelilista Tiedostoon + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Selain @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Lokinkerääjä @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Poista Trophy Pop-upit Käytöstä + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trophy Selain + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/fr_FR.ts b/src/qt_gui/translations/fr_FR.ts index fef03d7bb..ec3f9f8b5 100644 --- a/src/qt_gui/translations/fr_FR.ts +++ b/src/qt_gui/translations/fr_FR.ts @@ -487,7 +487,7 @@ Face Buttons - Face Buttons + Touches d'action Triangle / Y @@ -519,27 +519,98 @@ Color Adjustment - Color Adjustment + Ajustement des couleurs R: - R: + R: G: - G: + G: B: - B: + B: Override Lightbar Color - Override Lightbar Color + Remplacer la couleur de la barre de lumière Override Color - Override Color + Remplacer la couleur + + + Unable to Save + Impossible de sauvegarder + + + Cannot bind axis values more than once + Impossible de lier les valeurs de l'axe plusieurs fois + + + Save + Enregistrer + + + Apply + Appliquer + + + Restore Defaults + Réinitialiser par défaut + + + Cancel + Annuler + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Modifier les raccourcis d'entrée clavier + souris et contrôleur + + + Use Per-Game configs + Utiliser les configurations pour chaque jeu + + + Error + Erreur + + + Could not open the file for reading + Impossible d'ouvrir le fichier pour la lecture + + + Could not open the file for writing + Impossible d'ouvrir le fichier pour l'écriture + + + Save Changes + Enregistrer les Modifications + + + Do you want to save changes? + Voulez-vous sauvegarder les modifications? + + + Help + Aide + + + Do you want to reset your custom default config to the original default config? + Voulez-vous réinitialiser votre configuration par défaut personnalisée à la configuration par défaut d'origine ? + + + Do you want to reset this config to your custom default config? + Voulez-vous réinitialiser cette configuration à votre configuration personnalisée par défaut ? + + + Reset to Default + Rétablir par défaut @@ -775,6 +846,10 @@ Delete DLC Supprimer DLC + + Delete Trophy + Supprimer Trophée + Compatibility... Compatibilité... @@ -851,6 +926,10 @@ This game has no update folder to open! Ce jeu n'a pas de dossier de mise à jour à ouvrir! + + No log file found for this game! + Aucun fichier journal trouvé pour ce jeu! + Failed to convert icon. Échec de la conversion de l'icône. @@ -859,15 +938,46 @@ This game has no save data to delete! Ce jeu n'a pas de mise à jour à supprimer! + + This game has no saved trophies to delete! + Ce jeu n'a aucun trophée sauvegardé à supprimer! + Save Data Enregistrer les Données + + Trophy + Trophée + SFO Viewer for Visionneuse SFO pour + + HelpDialog + + Quickstart + Démarrage rapide + + + FAQ + FAQ + + + Syntax + Syntaxe + + + Special Bindings + Special bindings + + + Keybindings + Raccourcis + + InstallDirSelect @@ -887,6 +997,217 @@ Supprimer le fichier PKG à l'installation + + KBMSettings + + Configure Controls + Configurer les Commandes + + + D-Pad + Croix directionnelle + + + Up + Haut + + + unmapped + non mappé + + + Left + Gauche + + + Right + Droite + + + Down + Bas + + + Left Analog Halfmode + Demi-mode analogique gauche + + + hold to move left stick at half-speed + maintenez pour déplacer le joystick gauche à mi-vitesse + + + Left Stick + Joystick gauche + + + Config Selection + Sélection de la Configuration + + + Common Config + Configuration Commune + + + Use per-game configs + Utiliser les configurations par jeu + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Éditeur de Texte + + + Help + Aide + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Clic tactile + + + Mouse to Joystick + Souris vers Joystick + + + *press F7 ingame to activate + *Appuyez sur F7 en jeu pour activer + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Paramètres du mouvement de la souris + + + note: click Help Button/Special Keybindings for more information + remarque: cliquez sur le bouton Aide / Raccourcis spéciaux pour plus d'informations + + + Face Buttons + Touches d'action + + + Triangle + Triangle + + + Square + Carré + + + Circle + Rond + + + Cross + Croix + + + Right Analog Halfmode + Demi-mode analogique droit + + + hold to move right stick at half-speed + maintenez pour déplacer le joystick droit à mi-vitesse + + + Right Stick + Joystick Droit + + + Speed Offset (def 0.125): + Décalage de vitesse (def 0,125) : + + + Copy from Common Config + Copier à partir de la configuration commune + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0,50): + + + Speed Multiplier (def 1.0): + Multiplicateur de vitesse (def 1,0) : + + + Common Config Selected + Configuration courante sélectionnée + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Ce bouton copie les mappings de la configuration commune vers le profil actuellement sélectionné, et ne peut pas être utilisé lorsque le profil actuellement sélectionné est la configuration commune. + + + Copy values from Common Config + Copier à partir de la configuration commune + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Voulez-vous remplacer les mappings existants par les mappings de la configuration commune ? + + + Unable to Save + Impossible de sauvegarder + + + Cannot bind any unique input more than once + Impossible de lier une entrée unique plus d'une fois + + + Press a key + Appuyez sur un bouton + + + Cannot set mapping + Impossible de définir le mapping + + + Mousewheel cannot be mapped to stick outputs + La molette de la souris ne peut pas être affectée aux sorties de la manette + + + Save + Enregistrer + + + Apply + Appliquer + + + Restore Defaults + Réinitialiser par défaut + + + Cancel + Annuler + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dumper la liste des jeux + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer Visionneuse PKG @@ -1234,7 +1563,7 @@ Flags - Flags + Les indicateurs Path @@ -1311,6 +1640,10 @@ Trophy Trophée + + Open the custom trophy images/sounds folder + Ouvrir le dossier des images/sons de trophée personnalisé + Logger Journalisation @@ -1476,8 +1809,8 @@ Musique du titre - Disable Trophy Pop-ups - Désactiver les notifications de trophées + Disable Trophy Notification + Désactiver la notification de trophée Background Image @@ -1577,7 +1910,7 @@ Background Image:\nControl the opacity of the game background image. - Background Image:\nControl the opacity of the game background image. + Image de fond :\nContrôle l'opacité de l'image de fond du jeu. Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. @@ -1661,7 +1994,7 @@ Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + Activer HDR:\nActive le HDR dans les jeux qui le supportent.\nVotre moniteur doit avoir la prise en charge de l'espace couleur PQ BT2020 et du format swapchain RGB10A2. Game Folders:\nThe list of folders to check for installed games. @@ -1717,15 +2050,15 @@ Browse:\nBrowse for a folder to set as the save data path. - Browse:\nBrowse for a folder to set as the save data path. + Parcourir:\nNaviguez pour trouver un dossier pour définir le chemin des données de sauvegarde. Release - Release + Sortie Nightly - Nightly + Nocturne Set the volume of the background music. @@ -1737,7 +2070,7 @@ Save Data Path - Save Data Path + Enregistrer le chemin vers les données Browse @@ -1745,15 +2078,15 @@ async - async + asynchrone sync - sync + synchrone Auto Select - Auto Select + Sélection automatique Directory to install games @@ -1761,47 +2094,103 @@ Directory to save data - Directory to save data + Répertoire d'enregistrement des données Video - Video + Vidéo Display Mode - Display Mode + Mode d'affichage Windowed - Windowed + Fenêtré Fullscreen - Fullscreen + Plein écran Fullscreen (Borderless) - Fullscreen (Borderless) + Plein écran (sans bordure) Window Size - Window Size + Taille de fenêtre W: - W: + W: H: - H: + H: Separate Log Files - Separate Log Files + Séparer les fichiers de log Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Fichiers journaux séparés :\nÉcrit un fichier journal séparé pour chaque jeu. + + + Trophy Notification Position + Position de notification du trophée + + + Left + Gauche + + + Right + Droite + + + Top + Haut + + + Bottom + Bas + + + Notification Duration + Durée de la notification + + + Portable User Folder + Dossier d'utilisateur portable + + + Create Portable User Folder from Common User Folder + Créer un dossier utilisateur portable à partir du dossier utilisateur commun + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Dossier utilisateur portable :\nStocke les paramètres et données shadPS4 qui seront appliqués uniquement à la version shadPS4 située dans le dossier actuel. Redémarrez l'application après avoir créé le dossier utilisateur portable pour commencer à l'utiliser. + + + Cannot create portable user folder + Impossible de créer le dossier utilisateur portable + + + %1 already exists + %1 existe déjà + + + Portable user folder created + Dossier utilisateur portable créé + + + %1 successfully created. + %1 a été créé avec succès. + + + 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. + 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. @@ -1810,5 +2199,25 @@ Trophy Viewer Visionneuse de trophées + + Select Game: + Select Game: + + + Progress + Progression + + + Show Earned Trophies + Afficher les trophées gagnés + + + Show Not Earned Trophies + Afficher les trophées non gagnés + + + Show Hidden Trophies + Afficher les trophées cachés + diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 93e266f2c..6672337a6 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC DLC-k törlése + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Játéklista Dumpolása + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Nézegető @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Naplózó @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trófeák Megtekintése + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/id_ID.ts b/src/qt_gui/translations/id_ID.ts index 335450ca2..e43d31976 100644 --- a/src/qt_gui/translations/id_ID.ts +++ b/src/qt_gui/translations/id_ID.ts @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/it_IT.ts b/src/qt_gui/translations/it_IT.ts index fd1f4d521..e63da05b8 100644 --- a/src/qt_gui/translations/it_IT.ts +++ b/src/qt_gui/translations/it_IT.ts @@ -541,6 +541,77 @@ Override Color Sostituisci Colore + + Unable to Save + Impossibile Salvare + + + Cannot bind axis values more than once + Impossibile associare i valori degli assi più di una volta + + + Save + Salva + + + Apply + Applica + + + Restore Defaults + Ripristina Impostazioni Predefinite + + + Cancel + Annulla + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Modifica le associazioni di input di tastiera + mouse e controller + + + Use Per-Game configs + Usa Configurazioni Per Gioco + + + Error + Errore + + + Could not open the file for reading + Impossibile aprire il file per la lettura + + + Could not open the file for writing + Impossibile aprire il file per la scrittura + + + Save Changes + Salva Modifiche + + + Do you want to save changes? + Vuoi salvare le modifiche? + + + Help + Aiuto + + + Do you want to reset your custom default config to the original default config? + Vuoi reimpostare la configurazione predefinita personalizzata alla configurazione predefinita originale? + + + Do you want to reset this config to your custom default config? + Vuoi reimpostare questa configurazione alla configurazione predefinita personalizzata? + + + Reset to Default + Ripristina a Predefinito + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Elimina DLC + + Delete Trophy + Elimina Trofei + Compatibility... Compatibilità... @@ -851,6 +926,10 @@ This game has no update folder to open! Questo gioco non ha nessuna cartella di aggiornamento da aprire! + + No log file found for this game! + Nessun file di log trovato per questo gioco! + Failed to convert icon. Impossibile convertire l'icona. @@ -859,15 +938,46 @@ This game has no save data to delete! Questo gioco non ha alcun salvataggio dati da eliminare! + + This game has no saved trophies to delete! + Questo gioco non ha nessun trofeo salvato da eliminare! + Save Data Dati Salvataggio + + Trophy + Trofei + SFO Viewer for Visualizzatore SFO per + + HelpDialog + + Quickstart + Avvio rapido + + + FAQ + FAQ + + + Syntax + Sintassi + + + Special Bindings + Associazioni Speciali + + + Keybindings + Associazioni dei pulsanti + + InstallDirSelect @@ -887,6 +997,217 @@ Elimina file PKG dopo Installazione + + KBMSettings + + Configure Controls + Configura Comandi + + + D-Pad + Croce direzionale + + + Up + Su + + + unmapped + non mappato + + + Left + Sinistra + + + Right + Destra + + + Down + Giù + + + Left Analog Halfmode + Mezza Modalità Analogico Sinistra + + + hold to move left stick at half-speed + tieni premuto per muovere la levetta analogica sinistra a metà velocità + + + Left Stick + Levetta Sinistra + + + Config Selection + Selezione Configurazione + + + Common Config + Configurazione Comune + + + Use per-game configs + Usa configurazioni per gioco + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Editor Testuale + + + Help + Aiuto + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Click Touchpad + + + Mouse to Joystick + Mouse a Joystick + + + *press F7 ingame to activate + *premere F7 in gioco per attivare + + + R3 + R3 + + + Options + Opzioni + + + Mouse Movement Parameters + Parametri Movimento Del Mouse + + + note: click Help Button/Special Keybindings for more information + nota: cliccare sul Pulsante Aiuto/Associazioni Speciali dei Tasti per maggiori informazioni + + + Face Buttons + Pulsanti Frontali + + + Triangle + Triangolo + + + Square + Quadrato + + + Circle + Cerchio + + + Cross + Croce + + + Right Analog Halfmode + Mezza Modalità Analogico Destra + + + hold to move right stick at half-speed + tieni premuto per muovere la levetta analogica destra a metà velocità + + + Right Stick + Levetta Destra + + + Speed Offset (def 0.125): + Scostamento Velocità (def 0,125): + + + Copy from Common Config + Copia da Configurazione Comune + + + Deadzone Offset (def 0.50): + Scostamento Zona Morta (def 0,50): + + + Speed Multiplier (def 1.0): + Moltiplicatore Di Velocità (def 1,0): + + + Common Config Selected + Configurazione Comune Selezionata + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Questo pulsante copia le mappature dalla Configurazione Comune al profilo attualmente selezionato, e non può essere usato quando il profilo attualmente selezionato è Configurazione Comune. + + + Copy values from Common Config + Copia valori da Configurazione Comune + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Vuoi sovrascrivere le mappature esistenti con le mappature dalla Configurazione Comune? + + + Unable to Save + Impossibile Salvare + + + Cannot bind any unique input more than once + Non è possibile associare qualsiasi input univoco più di una volta + + + Press a key + Premi un tasto + + + Cannot set mapping + Impossibile impostare la mappatura + + + Mousewheel cannot be mapped to stick outputs + La rotella del mouse non può essere associata ai comandi della levetta analogica + + + Save + Salva + + + Apply + Applica + + + Restore Defaults + Ripristina Impostazioni Predefinite + + + Cancel + Annulla + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Scarica Lista Giochi + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer Visualizzatore PKG @@ -1311,6 +1640,10 @@ Trophy Trofei + + Open the custom trophy images/sounds folder + Apri la cartella personalizzata delle immagini/suoni dei trofei + Logger Registro @@ -1476,8 +1809,8 @@ Musica del Titolo - Disable Trophy Pop-ups - Disabilita Notifica Trofei + Disable Trophy Notification + Disabilita Notifiche Trofei Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. File di registro separati:\nScrive un file di registro separato per ogni gioco. + + Trophy Notification Position + Posizione Notifica Trofei + + + Left + Sinistra + + + Right + Destra + + + Top + In alto + + + Bottom + In basso + + + Notification Duration + Durata Notifica + + + Portable User Folder + Cartella Utente Portatile + + + Create Portable User Folder from Common User Folder + Crea una Cartella Utente Portatile dalla Cartella Comune Utente + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Cartella utente portatile:\nMemorizza le impostazioni e i dati shadPS4 che saranno applicati solo alla build shadPS4 situata nella cartella attuale. Riavviare l'applicazione dopo aver creato la cartella utente portatile per iniziare a usarla. + + + Cannot create portable user folder + Impossibile creare la cartella utente portatile + + + %1 already exists + %1: esiste già + + + Portable user folder created + Cartella utente portatile creata + + + %1 successfully created. + %1 creato con successo. + + + 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. + 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. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Visualizzatore Trofei + + Select Game: + Select Game: + + + Progress + Progresso + + + Show Earned Trophies + Mostra Trofei Guadagnati + + + Show Not Earned Trophies + Mostra Trofei Non Guadagnati + + + Show Hidden Trophies + Mostra Trofei Nascosti + diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 6e11dfe0c..7cf9fc5c2 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC DLCを削除 + + Delete Trophy + Delete Trophy + Compatibility... 互換性... @@ -851,6 +926,10 @@ This game has no update folder to open! このゲームにはアップデートフォルダがありません! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. アイコンの変換に失敗しました。 @@ -859,15 +938,46 @@ This game has no save data to delete! このゲームには削除するセーブデータがありません! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ インストール時にPKGファイルを削除 + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List ゲームリストをダンプ + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKGビューアー @@ -1311,6 +1640,10 @@ Trophy トロフィー + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger ロガー @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - トロフィーのポップアップを無効化 + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer トロフィービューアー + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index ad24f6f54..d5289ace9 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index c1886a4fa..17133da35 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -7,7 +7,7 @@ AboutDialog About shadPS4 - About shadPS4 + Apie shadPS4 shadPS4 is an experimental open-source emulator for the PlayStation 4. @@ -15,7 +15,7 @@ This software should not be used to play games you have not legally obtained. - This software should not be used to play games you have not legally obtained. + Ši programa neturėtų būti naudojama žaidimams kurių neturite legaliai įsigiję. @@ -463,7 +463,7 @@ Back - Back + Atgal R1 / RB @@ -519,7 +519,7 @@ Color Adjustment - Color Adjustment + Spalvų Reguliavimas R: @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Nepavyko Išsaugoti + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Atšaukti + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Išsaugoti Pakeitimus + + + Do you want to save changes? + Ar norite išsaugoti pakeitimus? + + + Help + Pagalba + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -801,7 +876,7 @@ Error - Error + Klaida Error creating shortcut! @@ -813,7 +888,7 @@ Game - Game + Žaidimas This game has no update to delete! @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index daf44c3bb..e8ce99f90 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -22,11 +22,11 @@ CheatsPatches Cheats / Patches for - Juks / Programrettelser for + Juks og programrettelser for Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n - Juks/programrettelse er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned juks individuelt ved å velge pakkebrønn og klikke på nedlastingsknappen.\nPå fanen programrettelse kan du laste ned alle programrettelser samtidig, velge hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler Juks/Programrettelse,\nvær vennlig å rapportere problemer til juks/programrettelse utvikleren.\n\nHar du laget en ny juks? Besøk:\n + Juks og programrettelser er eksperimentelle.\nBruk med forsiktighet.\n\nLast ned juks individuelt ved å velge pakkebrønn og trykke på nedlastingsknappen.\nPå fanen programrettelser kan du laste ned alle programrettelser samtidig, velg hvilke du ønsker å bruke, og lagre valget ditt.\n\nSiden vi ikke utvikler juks eller programrettelser,\nmeld fra om feil til jukse eller programrettelse utvikleren.\n\nHar du utviklet en ny juks? Besøk:\n No Image Available @@ -90,7 +90,7 @@ Patches - Programrettelse + Programrettelser Error @@ -102,7 +102,7 @@ Unable to open files.json for reading. - Kan ikke åpne files.json for lesing. + Klarte ikke åpne files.json for lesing. No patch file found for the current serial. @@ -110,11 +110,11 @@ Unable to open the file for reading. - Kan ikke åpne fila for lesing. + Klarte ikke åpne fila for lesing. Unable to open the file for writing. - Kan ikke åpne fila for skriving. + Klarte ikke åpne fila for skriving. Failed to parse XML: @@ -146,11 +146,11 @@ Failed to save file: - Kunne ikke lagre fila: + Klarte ikke lagre fila: Failed to download file: - Kunne ikke laste ned fila: + Klarte ikke laste ned fila: Cheats Not Found @@ -170,11 +170,11 @@ Failed to save: - Kunne ikke lagre: + Klarte ikke lagre: Failed to download: - Kunne ikke laste ned: + Klarte ikke laste ned: Download Complete @@ -186,11 +186,11 @@ Failed to parse JSON data from HTML. - Kunne ikke analysere JSON-data fra HTML. + Klarte ikke analysere JSON-data fra HTML. Failed to retrieve HTML page. - Kunne ikke hente HTML-side. + Klarte ikke hente HTML-siden. The game is in version: %1 @@ -210,7 +210,7 @@ Failed to open file: - Kunne ikke åpne fila: + Klarte ikke åpne fila: XML ERROR: @@ -230,7 +230,7 @@ Failed to open files.json for reading. - Kunne ikke åpne files.json for lesing. + Klarte ikke åpne files.json for lesing. Name: @@ -265,7 +265,7 @@ Failed to parse update information. - Kunne ikke analysere oppdaterings-informasjonen. + Klarte ikke analysere oppdateringsinformasjon. No pre-releases found. @@ -337,26 +337,26 @@ The update has been downloaded, press OK to install. - Oppdateringen har blitt lastet ned, trykk OK for å installere. + Oppdateringen ble lastet ned, trykk OK for å installere. Failed to save the update file at - Kunne ikke lagre oppdateringsfila på + Klarte ikke lagre oppdateringsfila på Starting Update... - Starter oppdatering... + Starter oppdatering … Failed to create the update script file - Kunne ikke opprette oppdateringsskriptfila + Klarte ikke opprette oppdateringsskriptfila CompatibilityInfoClass Fetching compatibility data, please wait - Henter kompatibilitetsdata, vennligst vent + Henter kompatibilitetsdata, vent litt. Cancel @@ -364,7 +364,7 @@ Loading... - Laster... + Laster … Error @@ -372,11 +372,11 @@ Unable to update compatibility data! Try again later. - Kan ikke oppdatere kompatibilitetsdata! Prøv igjen senere. + Klarte ikke oppdatere kompatibilitetsdata! Prøv igjen senere. Unable to open compatibility_data.json for writing. - Kan ikke åpne compatibility_data.json for skriving. + Klarte ikke åpne compatibility_data.json for skriving. Unknown @@ -407,11 +407,11 @@ ControlSettings Configure Controls - Sett opp kontroller + Kontrolleroppsett D-Pad - D-Pad + Navigasjonsknapper Up @@ -443,7 +443,7 @@ Config Selection - Utvalg av oppsett + Valg av oppsett Common Config @@ -541,6 +541,77 @@ Override Color Overstyr farge + + Unable to Save + Klarte ikke lagre + + + Cannot bind axis values more than once + Kan ikke tildele akseverdier mer enn en gang + + + Save + Lagre + + + Apply + Bruk + + + Restore Defaults + Gjenopprett standardinnstillinger + + + Cancel + Avbryt + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Rediger oppsett for tastatur, mus og kontroller + + + Use Per-Game configs + Bruk oppsett per spill + + + Error + Feil + + + Could not open the file for reading + Klarte ikke åpne fila for lesing + + + Could not open the file for writing + Klarte ikke åpne fila for skriving + + + Save Changes + Lagre endringer + + + Do you want to save changes? + Vil du lagre endringene? + + + Help + Hjelp + + + Do you want to reset your custom default config to the original default config? + Vil du tilbakestille alle dine tilpassede innstillinger til standarden? + + + Do you want to reset this config to your custom default config? + Vil du tilbakestille dette oppsettet til standard oppsett? + + + Reset to Default + Tilbakestill + ElfViewer @@ -553,7 +624,7 @@ GameInfoClass Loading game list, please wait :3 - Laster spilliste, vennligst vent :3 + Laster spilliste, vent litt :3 Cancel @@ -561,7 +632,7 @@ Loading... - Laster... + Laster … @@ -651,7 +722,7 @@ Game does not initialize properly / crashes the emulator - Spillet initialiseres ikke riktig / krasjer emulatoren + Spillet initialiseres ikke riktig eller krasjer emulatoren Game boots, but only displays a blank screen @@ -671,7 +742,7 @@ Click to see details on github - Klikk for å se detaljer på GitHub + Trykk for å se detaljer på GitHub Last updated @@ -709,11 +780,11 @@ Cheats / Patches - Juks / Programrettelse + Juks og programrettelser SFO Viewer - SFO viser + SFO-viser Trophy Viewer @@ -721,7 +792,7 @@ Open Folder... - Åpne mappe... + Åpne mappe … Open Game Folder @@ -737,7 +808,7 @@ Copy info... - Kopier info... + Kopier info … Copy Name @@ -761,7 +832,7 @@ Delete... - Slett... + Slett … Delete Game @@ -775,9 +846,13 @@ Delete DLC Slett DLC + + Delete Trophy + Slett trofé + Compatibility... - Kompatibilitet... + Kompatibilitet … Update database @@ -837,7 +912,7 @@ Are you sure you want to delete %1's %2 directory? - Er du sikker på at du vil slette %1's %2 directory? + Er du sikker på at du vil slette %1's %2 mappa? Open Update Folder @@ -851,6 +926,10 @@ This game has no update folder to open! Dette spillet har ingen oppdateringsmappe å åpne! + + No log file found for this game! + Fant ingen loggfil for dette spillet! + Failed to convert icon. Klarte ikke konvertere ikon. @@ -859,15 +938,46 @@ This game has no save data to delete! Dette spillet har ingen lagret data å slette! + + This game has no saved trophies to delete! + Dette spillet har ingen lagrede trofeer å slette! + Save Data Lagret data + + Trophy + Trofé + SFO Viewer for SFO-viser for + + HelpDialog + + Quickstart + Hurtigstart + + + FAQ + Ofte stilte spørsmål + + + Syntax + Syntaks + + + Special Bindings + Spesielle hurtigtaster + + + Keybindings + Hurtigtast + + InstallDirSelect @@ -887,11 +997,222 @@ Slett PKG-fila ved installering + + KBMSettings + + Configure Controls + Tastaturoppsett + + + D-Pad + Navigasjonsknapper + + + Up + Opp + + + unmapped + Ikke satt opp + + + Left + Venstre + + + Right + Høyre + + + Down + Ned + + + Left Analog Halfmode + Venstre analog halvmodus + + + hold to move left stick at half-speed + Hold for å bevege venstre analog med halv hastighet + + + Left Stick + Venstre analog + + + Config Selection + Valg av oppsett + + + Common Config + Felles oppsett + + + Use per-game configs + Bruk oppsett per spill + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Skriveprogram + + + Help + Hjelp + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Berøringsplateknapp + + + Mouse to Joystick + Mus til styrespak + + + *press F7 ingame to activate + Trykk F7 i spillet for å bruke + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Oppsett av musebevegelse + + + note: click Help Button/Special Keybindings for more information + Merk: Trykk på hjelpeknappen for mer informasjon + + + Face Buttons + Handlingsknapper + + + Triangle + Triangel + + + Square + Firkant + + + Circle + Sirkel + + + Cross + Kryss + + + Right Analog Halfmode + Høyre analog halvmodus + + + hold to move right stick at half-speed + Hold for å bevege høyre analog med halv hastighet + + + Right Stick + Høyre analog + + + Speed Offset (def 0.125): + Hastighetsforskyvning (def 0.125): + + + Copy from Common Config + Kopier fra felles oppsettet + + + Deadzone Offset (def 0.50): + Dødsoneforskyvning (def 0.50): + + + Speed Multiplier (def 1.0): + Hastighetsmultiplikator (def 1.0): + + + Common Config Selected + Felles oppsett valgt + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Denne knappen kopierer oppsettet fra felles oppsettet til den valgte profilen, og kan ikke brukes når den gjeldende brukte profilen er felles oppsettet. + + + Copy values from Common Config + Kopier verdier fra felles oppsettet + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Vil du overskrive eksisterende valg av oppsett med felles oppsettet? + + + Unable to Save + Klarte ikke lagre + + + Cannot bind any unique input more than once + Kan ikke tildele unike oppsett mer enn en gang + + + Press a key + Trykk på en tast + + + Cannot set mapping + Klarte ikke tildele + + + Mousewheel cannot be mapped to stick outputs + Musehjulet kan ikke tildeles analogstikkene + + + Save + Lagre + + + Apply + Bruk + + + Restore Defaults + Gjenopprett standardinnstillinger + + + Cancel + Avbryt + + MainWindow Open/Add Elf Folder - Åpne/Legg til Elf-mappe + Åpne eller legg til Elf-mappe Install Packages (PKG) @@ -911,7 +1232,7 @@ Configure... - Sett opp... + Sett opp … Install application from a .pkg file @@ -979,19 +1300,27 @@ Download Cheats/Patches - Last ned juks/programrettelse + Last ned juks og programrettelser Dump Game List Dump spilliste + + Trophy Viewer + Troféviser + + + No games found. Please add your games to your library first. + Fant ingen spill. Legg til spillene dine i biblioteket først. + PKG Viewer PKG-viser Search... - Søk... + Søk … File @@ -1261,7 +1590,7 @@ General - Generell + Generelt System @@ -1281,7 +1610,7 @@ Enable Separate Update Folder - Bruk seperat oppdateringsmappe + Bruk separat oppdateringsmappe Default tab when opening settings @@ -1311,6 +1640,10 @@ Trophy Trofé + + Open the custom trophy images/sounds folder + Åpne mappa med tilpassede bilder og lyder for trofé + Logger Loggføring @@ -1401,7 +1734,7 @@ Add... - Legg til... + Legg til … Remove @@ -1409,11 +1742,11 @@ Debug - Feilretting + Feilsøking Enable Debug Dumping - Bruk feilrettingsdumping + Bruk feilsøkingsdumping Enable Vulkan Validation Layers @@ -1476,8 +1809,8 @@ Tittelmusikk - Disable Trophy Pop-ups - Deaktiver trofé hurtigmeny + Disable Trophy Notification + Slå av trofévarsler Background Image @@ -1493,7 +1826,7 @@ Play title music - Spill tittelmusikk + Spill av tittelmusikk Update Compatibility Database On Startup @@ -1585,7 +1918,7 @@ Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - Deaktiver trofé hurtigmeny:\nDeaktiver trofévarsler i spillet. Trofé-fremgang kan fortsatt ved help av troféviseren (høyreklikk på spillet i hovedvinduet). + Slå av trofévarsler:\nFjerner trofévarsler i spillet. Troféfremgang kan fortsatt vises ved hjelp av troféviseren (høyreklikk på spillet i hovedvinduet). Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. @@ -1625,15 +1958,15 @@ Touchpad Left - Berøringsplate Venstre + Berøringsplate venstre Touchpad Right - Berøringsplate Høyre + Berøringsplate høyre Touchpad Center - Berøringsplate Midt + Berøringsplate midten None @@ -1641,11 +1974,11 @@ Graphics Device:\nOn multiple GPU systems, select the GPU the emulator will use from the drop down list,\nor select "Auto Select" to automatically determine it. - Grafikkenhet:\nI systemer med flere GPU-er, velg GPU-en emulatoren skal bruke fra rullegardinlista,\neller "Velg automatisk&quot. + Grafikkenhet:\nSystemer med flere GPU-er, kan emulatoren velge hvilken enhet som skal brukes fra rullegardinlista,\neller velg "Velg automatisk". 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. - Bredde/Høyde:\nAngir størrelsen på emulatorvinduet ved oppstart, som kan endres under spillingen.\nDette er forskjellig fra oppløsningen i spillet. + Bredde / Høyde:\nAngir størrelsen på emulatorvinduet ved oppstart, som kan endres under spillingen.\nDette er annerledes fra oppløsningen i spillet. Vblank Divider:\nThe frame rate at which the emulator refreshes at is multiplied by this number. Changing this may have adverse effects, such as increasing the game speed, or breaking critical game functionality that does not expect this to change! @@ -1677,7 +2010,7 @@ Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. - Bruk feilrettingsdumping:\nLagrer import- og eksport-symbolene og filoverskrifts-informasjonen til det nåværende kjørende PS4-programmet i en mappe. + Bruk feilsøkingsdumping:\nLagrer import- og eksport-symbolene og filoverskrifts-informasjonen til det nåværende kjørende PS4-programmet i en mappe. Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation. @@ -1733,7 +2066,7 @@ Enable Motion Controls - Bruk bevegelseskontroller + Bruk bevegelsesstyring Save Data Path @@ -1757,7 +2090,7 @@ Directory to install games - Mappe for å installere spill + Mappe for installering av spill Directory to save data @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate loggfiler:\nOppretter en separat loggfil for hvert spill. + + Trophy Notification Position + Trofévarsel plassering + + + Left + Venstre + + + Right + Høyre + + + Top + Øverst + + + Bottom + Nederst + + + Notification Duration + Varslingsvarighet + + + Portable User Folder + Separat brukermappe + + + Create Portable User Folder from Common User Folder + Lag ny separat brukermappe fra fellesbrukermappa + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Separat brukermappe:\n Lagrer shadPS4-innstillinger og data som kun brukes til shadPS4 programmet i gjeldende mappe. Start programmet på nytt etter opprettelsen av mappa for å ta den i bruk. + + + Cannot create portable user folder + Klarte ikke opprette separat brukermappe + + + %1 already exists + %1 finnes allerede + + + Portable user folder created + Separat brukermappe opprettet + + + %1 successfully created. + %1 opprettet. + + + 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. + Å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. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Troféviser + + Select Game: + Velg spill: + + + Progress + Fremdrift + + + Show Earned Trophies + Vis opptjente trofeer + + + Show Not Earned Trophies + Vis ikke opptjente trofeer + + + Show Hidden Trophies + Vis skjulte trofeer + diff --git a/src/qt_gui/translations/nl_NL.ts b/src/qt_gui/translations/nl_NL.ts index 762254a6e..5c1725bd5 100644 --- a/src/qt_gui/translations/nl_NL.ts +++ b/src/qt_gui/translations/nl_NL.ts @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 05ce4d9be..033412efa 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -22,7 +22,7 @@ CheatsPatches Cheats / Patches for - Kody / Łatki dla + Kody / Poprawki dla 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 @@ -58,15 +58,15 @@ Delete File - Delete File + Usuń plik No files selected. - No files selected. + Nie wybrano pliku. You can delete the cheats you don't want after downloading them. - You can delete the cheats you don't want after downloading them. + Możesz usunąć kody, których nie chcesz po ich pobraniu. Do you want to delete the selected file?\n%1 @@ -202,7 +202,7 @@ You may need to update your game. - Możesz potrzebować zaktualizować swoją grę. + Może być konieczne uaktualnienie gry. Incompatibility Notice @@ -230,11 +230,11 @@ Failed to open files.json for reading. - Failed to open files.json for reading. + Nie można otworzyć pliku files.json do odczytu. Name: - Name: + Nazwa: Can't apply cheats before the game is started @@ -249,7 +249,7 @@ CheckUpdate Auto Updater - Automatyczne aktualizacje + Asystent aktualizacji Error @@ -289,7 +289,7 @@ Update Channel - Kanał Aktualizacji + Kanał aktualizacji Current Version @@ -297,7 +297,7 @@ Latest Version - Ostatnia wersja + Najnowsza wersja Do you want to update? @@ -388,7 +388,7 @@ Boots - Buty + Uruchamia się Menus @@ -400,146 +400,217 @@ Playable - Do grania + Grywalne ControlSettings Configure Controls - Configure Controls + Skonfiguruj sterowanie D-Pad - D-Pad + Krzyżak Up - Up + Góra Left - Left + Lewo Right - Right + Prawo Down - Down + Dół Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + Martwa strefa lewego drążka (def:2 max:127) Left Deadzone - Left Deadzone + Martwa strefa lewego drążka Left Stick - Left Stick + Lewy drążek Config Selection - Config Selection + Wybór konfiguracji Common Config - Common Config + Typowa konfiguracja Use per-game configs - Use per-game configs + Użyj osobnej konfiguracji dla każdej gry L1 / LB - L1 / LB + L1 / LB L2 / LT - L2 / LT + L2 / LT Back - Back + Wstecz R1 / RB - R1 / RB + R1 / RB R2 / RT - R2 / RT + R2 / RT L3 - L3 + L3 Options / Start - Options / Start + Opcje / Start R3 - R3 + R3 Face Buttons - Face Buttons + Przyciski akcji Triangle / Y - Triangle / Y + Trójkąt / Y Square / X - Square / X + Kwadrat / X Circle / B - Circle / B + Kółko / B Cross / A - Cross / A + Krzyżyk / A Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + Martwa strefa prawego drążka (def:2 max:127) Right Deadzone - Right Deadzone + Martwa strefa prawego drążka Right Stick - Right Stick + Prawy drążek Color Adjustment - Color Adjustment + Dostosowanie koloru R: - R: + Czerwony: G: - G: + Zielony: B: - B: + Niebieski: Override Lightbar Color - Override Lightbar Color + Zastąp kolor paska świetlnego Override Color - Override Color + Zastąp kolor + + + Unable to Save + Zapisywanie nie powiodło się + + + Cannot bind axis values more than once + Nie można powiązać wartości osi więcej niż raz + + + Save + Zapisz + + + Apply + Zastosuj + + + Restore Defaults + Przywróć ustawienia domyślne + + + Cancel + Anuluj + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edytuj przypisanie klawiszy klawiatury + myszy oraz kontrolera + + + Use Per-Game configs + Użyj osobnej konfiguracji dla każdej gry + + + Error + Błąd + + + Could not open the file for reading + Nie można otworzyć pliku do odczytu + + + Could not open the file for writing + Nie można otworzyć pliku do zapisu + + + Save Changes + Zapisać zmiany + + + Do you want to save changes? + Czy chcesz zapisać zmiany? + + + Help + Pomóc + + + Do you want to reset your custom default config to the original default config? + Czy chcesz zresetować Twoją domyślną konfigurację do oryginalnej domyślnej konfiguracji? + + + Do you want to reset this config to your custom default config? + Czy chcesz zresetować tę konfigurację do Twojej domyślnej konfiguracji? + + + Reset to Default + Zresetować do domyślnych @@ -584,7 +655,7 @@ Directory to install DLC - Directory to install DLC + Katalog do instalacji dodatkowej zawartości (DLC) @@ -603,11 +674,11 @@ Compatibility - Zgodność + Kompatybilność Region - Region + Region Firmware @@ -635,15 +706,15 @@ h - h + godz. m - m + min s - s + s Compatibility is untested @@ -682,23 +753,23 @@ GameListUtils B - B + B KB - KB + KB MB - MB + MB GB - GB + GB TB - TB + TB @@ -729,7 +800,7 @@ Open Save Data Folder - Otwórz Folder Danych Zapisów + Otwórz folder zapisanych danych Open Log Folder @@ -749,11 +820,11 @@ Copy Version - Copy Version + Kopiuj wersję Copy Size - Copy Size + Kopiuj rozmiar Copy All @@ -765,19 +836,23 @@ Delete Game - Usuń Grę + Usuń grę Delete Update - Usuń Aktualizację + Usuń aktualizację Delete DLC - Usuń DLC + Usuń dodatkową zawartość (DLC) + + + Delete Trophy + Usuń trofeum Compatibility... - kompatybilność... + Kompatybilność... Update database @@ -825,11 +900,11 @@ This game has no DLC to delete! - Ta gra nie ma DLC do usunięcia! + Ta gra nie ma dodatkowej zawartości (DLC) do usunięcia! DLC - DLC + Dodatkowa zawartość (DLC) Delete %1 @@ -841,31 +916,66 @@ Open Update Folder - Open Update Folder + Otwórz folder aktualizacji Delete Save Data - Delete Save Data + Usuń zapisane dane This game has no update folder to open! - This game has no update folder to open! + Ta gra nie ma folderu aktualizacji do otwarcia! + + + No log file found for this game! + Nie znaleziono pliku dziennika dla tej gry! Failed to convert icon. - Failed to convert icon. + Nie udało się przekonwertować ikony. This game has no save data to delete! - This game has no save data to delete! + Ta gra nie ma zapisów do usunięcia! + + + This game has no saved trophies to delete! + Ta gra nie ma zapisanych trofeuów do usunięcia! Save Data - Save Data + Zapisane dane + + + Trophy + Trofeum SFO Viewer for - SFO Viewer for + Menedżer plików SFO dla + + + + HelpDialog + + Quickstart + Szybki start + + + FAQ + Najczęściej zadawane pytania + + + Syntax + Składnia + + + Special Bindings + Specjalne wiązania + + + Keybindings + Przypisanie klawiszy @@ -880,11 +990,222 @@ Install All Queued to Selected Folder - Install All Queued to Selected Folder + Zainstaluj wszystkie oczekujące do wybranego folderu Delete PKG File on Install - Delete PKG File on Install + Usuń plik PKG po instalacji + + + + KBMSettings + + Configure Controls + Skonfiguruj sterowanie + + + D-Pad + Krzyżak + + + Up + Strzałka w górę + + + unmapped + nieprzypisane + + + Left + Strzałka w lewo + + + Right + Strzałka w prawo + + + Down + Strzałka w dół + + + Left Analog Halfmode + Połowiczny tryb lewego drążka + + + hold to move left stick at half-speed + przytrzymaj, aby przesuwać lewy drążek dwa razy wolniej + + + Left Stick + Lewy drążek + + + Config Selection + Wybór konfiguracji + + + Common Config + Typowa konfiguracja + + + Use per-game configs + Użyj osobnej konfiguracji dla każdej gry + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Edytor tekstu + + + Help + Pomoc + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Kliknięcie Touchpada + + + Mouse to Joystick + Mysz na Joystick + + + *press F7 ingame to activate + *naciśnij F7 w grze aby aktywować + + + R3 + R3 + + + Options + Opcje + + + Mouse Movement Parameters + Parametry ruchu myszy + + + note: click Help Button/Special Keybindings for more information + uwaga: kliknij przycisk Pomoc/Specjalne skróty klawiszowe, aby uzyskać więcej informacji + + + Face Buttons + Przednie przyciski + + + Triangle + Trójkąt + + + Square + Kwadrat + + + Circle + Kółko + + + Cross + Krzyżyk + + + Right Analog Halfmode + Połowiczny tryb prawego drążka + + + hold to move right stick at half-speed + przytrzymaj, aby przesuwać prawy drążek dwa razy wolniej + + + Right Stick + Prawy drążek + + + Speed Offset (def 0.125): + Offset prędkości (def 0,125): + + + Copy from Common Config + Kopiuj z typowej konfiguracji + + + Deadzone Offset (def 0.50): + Offset martwych stref (def 0,50): + + + Speed Multiplier (def 1.0): + Mnożnik prędkości (def1.0): + + + Common Config Selected + Wybrano typową konfigurację + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Przycisk ten kopiuje mapowanie z typowej konfiguracji do aktualnie wybranego profilu, i nie może być użyty, gdy aktualnie wybranym profilem jest typowa konfiguracja. + + + Copy values from Common Config + Kopiuj z typowej konfiguracji + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Czy chcesz nadpisać istniejące mapowania mapowaniem z typowej konfiguracji? + + + Unable to Save + Zapisywanie nie powiodło się + + + Cannot bind any unique input more than once + Nie można powiązać żadnych unikalnych danych wejściowych więcej niż raz + + + Press a key + Naciśnij klawisz + + + Cannot set mapping + Nie można ustawić mapowania + + + Mousewheel cannot be mapped to stick outputs + Kółko myszy nie może być przypisane do sterowania drążkiem + + + Save + Zapisz + + + Apply + Zastosuj + + + Restore Defaults + Przywróć ustawienia domyślne + + + Cancel + Anuluj @@ -985,6 +1306,14 @@ Dump Game List Zgraj listę gier + + Trophy Viewer + Menedżer trofeów + + + No games found. Please add your games to your library first. + Nie znaleziono gier. Najpierw dodaj swoje gry do swojej biblioteki. + PKG Viewer Menedżer plików PKG @@ -1127,15 +1456,15 @@ DLC Installation - Instalacja DLC + Instalacja dodatkowej zawartości (DLC) Would you like to install DLC: %1? - Czy chcesz zainstalować DLC: %1? + Czy chcesz zainstalować dodatkową zawartość (DLC): %1? DLC already installed: - DLC już zainstalowane: + Dodatkowa zawartość (DLC) już zainstalowana: Game already installed @@ -1163,27 +1492,27 @@ Run Game - Run Game + Uruchom grę Eboot.bin file not found - Eboot.bin file not found + Nie znaleziono pliku EBOOT.BIN PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) + Plik PKG (*.PKG *.pkg) PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! + PKG jest aktualizacją lub dodatkową zawartością (DLC), najpierw zainstaluj grę! Game is already running! - Game is already running! + Gra jest już uruchomiona! shadPS4 - shadPS4 + shadPS4 @@ -1206,7 +1535,7 @@ Installed - Installed + Zainstalowano Size @@ -1214,27 +1543,27 @@ Category - Category + Kategoria Type - Type + Typ App Ver - App Ver + Wersja aplikacji FW - FW + Oprogramowanie Region - Region + Region Flags - Flags + Flagi Path @@ -1250,7 +1579,7 @@ Package - Package + Paczka @@ -1265,7 +1594,7 @@ System - System + System Console Language @@ -1277,7 +1606,7 @@ Emulator - Emulator + Emulator Enable Separate Update Folder @@ -1309,7 +1638,11 @@ Trophy - Trofeum + Trofea + + + Open the custom trophy images/sounds folder + Otwórz niestandardowy folder obrazów/dźwięków trofeów Logger @@ -1329,7 +1662,7 @@ Input - Wejście + Sterowanie Cursor @@ -1345,7 +1678,7 @@ s - s + s Controller @@ -1389,7 +1722,7 @@ Enable HDR - Enable HDR + Włącz HDR Paths @@ -1429,23 +1762,23 @@ Enable Crash Diagnostics - Enable Crash Diagnostics + Włącz diagnostykę awarii Collect Shaders - Collect Shaders + Zbieraj cienie Copy GPU Buffers - Copy GPU Buffers + Kopiuj bufory GPU Host Debug Markers - Host Debug Markers + Znaczniki diagnostyczne gospodarza Guest Debug Markers - Guest Debug Markers + Znaczniki diagnostyczne gościa Update @@ -1473,23 +1806,23 @@ Title Music - Title Music + Muzyka tytułowa - Disable Trophy Pop-ups - Wyłącz wyskakujące okienka trofeów + Disable Trophy Notification + Wyłącz powiadomienia o trofeach Background Image - Background Image + Obraz tła Show Background Image - Show Background Image + Pokaż obraz tła Opacity - Opacity + Przezroczystość Play title music @@ -1577,7 +1910,7 @@ Background Image:\nControl the opacity of the game background image. - Background Image:\nControl the opacity of the game background image. + Obraz tła:\nKontroluj przezroczystość obrazu tła gry. Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. @@ -1661,7 +1994,7 @@ Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + Włącz HDR:\nWłącza HDR w grach, które go wspierają.\nTwój monitor musi mieć wsparcie dla przestrzeni kolorów BT2020 PQ oraz formatu RGB10A2 swapchain. Game Folders:\nThe list of folders to check for installed games. @@ -1693,51 +2026,51 @@ Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). - Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + Zbieranie cieni:\nPotrzebujesz tej opcji aby edytować cienie za pomocą menu debugowania (Ctrl + F10). Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + Diagnostyka awarii:\nTworzy plik .yaml z informacjami o stanie Vulkan w momencie awarii.\nPrzydatne do debugowania błędów 'DEVICE LOST' . Jeśli ta opcja jest włączona, powinieneś włączyć "Znaczniki błędów gospodarza" oraz "Znaczniki błędów gościa".\nNie działa na kartach graficznych Intela.\nOpcja "Włącz warstwy walidacji Vulkan" i Vulkan SDK jest wymagana do działania. Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. - Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + Kopiowanie buforów karty graficznej:\nOmija problemy wyścigów związane z przesyłaniem danych do karty graficznej.\nMoże, ale nie musi, pomóc w przypadku awarii typu PM4 0. Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Wskaźniki debugowania gospodarza:\nWstawia informacje emulatora, takie jak znaczniki dla konkretnych poleceń AMDGPU wokół poleceń Vulkan, a także nadaje nazwy debugowania zasobów.\nJeśli ta opcja jest włączona, powinieneś włączyć diagnostykę awarii.\nPrzydatne dla programów takich jak RenderDoc. Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Znaczniki debugowania gościa:\nWstawia wszystkie znaczniki debugowania, które gra dodała do buforu poleceń.\nJeśli ta opcja jest włączona, powinieneś włączyć diagnostykę awarii.\nPrzydatne dla programów takich jak RenderDoc. Save Data Path:\nThe folder where game save data will be saved. - Save Data Path:\nThe folder where game save data will be saved. + Ścieżka zapisu danych:\nFolder, w którym zapisywane będą dane gry. Browse:\nBrowse for a folder to set as the save data path. - Browse:\nBrowse for a folder to set as the save data path. + Przeglądaj:\nPrzeglądaj folder, aby ustawić ścieżkę zapisywania danych. Release - Release + Wersja stablina Nightly - Nightly + Wersja rozwojowa Set the volume of the background music. - Set the volume of the background music. + Wybierz poziom głośności muzyki w tle. Enable Motion Controls - Enable Motion Controls + Włącz sterowanie ruchem Save Data Path - Save Data Path + Ścieżka zapisanych danych Browse @@ -1745,15 +2078,15 @@ async - async + asynchroniczny sync - sync + synchroniczny Auto Select - Auto Select + Wybór automatyczny Directory to install games @@ -1761,47 +2094,103 @@ Directory to save data - Directory to save data + Katalog do zapisywania danych Video - Video + Wyświetlanie Display Mode - Display Mode + Tryb wyświetlania Windowed - Windowed + Tryb okna Fullscreen - Fullscreen + Tryb pełnoekranowy Fullscreen (Borderless) - Fullscreen (Borderless) + Tryb pełnoekranowy (bez obramowania) Window Size - Window Size + Rozmiar okna W: - W: + Szerokość: H: - H: + Wysokość: Separate Log Files - Separate Log Files + Oddzielne pliki dziennika Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Oddzielne pliki dziennika:\nZapisuje oddzielny plik dziennika dla każdej gry. + + + Trophy Notification Position + Pozycja powiadomień trofeów + + + Left + Z lewej + + + Right + Z prawej + + + Top + Z góry + + + Bottom + Z dołu + + + Notification Duration + Czas trwania powiadomienia + + + Portable User Folder + Przenośny folder użytkownika + + + Create Portable User Folder from Common User Folder + Utwórz przenośny folder użytkownika ze zwykłego folderu użytkownika + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Przenośny folder użytkownika:\nPrzechowuje ustawienia shadPS4 i dane, które zostaną zastosowane tylko do kompilacji shadPS4 znajdującej się w bieżącym folderze. Uruchom ponownie aplikację po utworzeniu przenośnego folderu użytkownika, aby zacząć z niego korzystać. + + + Cannot create portable user folder + Nie można utworzyć przenośnego folderu użytkownika + + + %1 already exists + %1 już istnieje + + + Portable user folder created + Utworzono przenośny folder użytkownika + + + %1 successfully created. + %1 prawidłowo utworzony. + + + 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. + 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. @@ -1810,5 +2199,25 @@ Trophy Viewer Menedżer trofeów + + Select Game: + Wybierz grę: + + + Progress + Postęp + + + Show Earned Trophies + Pokaż zdobyte trofea + + + Show Not Earned Trophies + Pokaż niezdobyte trofea + + + Show Hidden Trophies + Pokaż ukryte trofea + diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 3ff4e106a..d44efce5d 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -11,26 +11,26 @@ shadPS4 is an experimental open-source emulator for the PlayStation 4. - shadPS4 é um emulador experimental de código-fonte aberto para o PlayStation 4. + O shadPS4 é um emulador experimental de código-fonte aberto para o PlayStation 4. This software should not be used to play games you have not legally obtained. - Este programa não deve ser usado para jogar jogos que tenham sido obtidos ilegalmente. + Este programa não deve ser usado para executar jogos que tenham sido obtidos ilegalmente. CheatsPatches Cheats / Patches for - Cheats / Patches para + Trapaças / Modificações para Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n - Cheats/Patches são experimentais.\nUse com cautela.\n\nBaixe os cheats individualmente selecionando o repositório e clicando no botão de download.\nNa aba Patches, você pode baixar todos os Patches de uma vez, escolha qual deseja usar e salve a opção.\n\nComo não desenvolvemos os Cheats/Patches,\npor favor, reporte os problemas relacionados ao autor do cheat.\n\nCriou um novo cheat? Visite:\n + As Trapaças/Modificações são experimentais.\nUse com cautela.\n\nBaixe as trapaças individualmente selecionando o repositório e clicando no botão de baixar.\nNa aba Modificações, você pode baixar todas as modificações de uma vez, escolha qual deseja usar e salve a opção.\n\nComo não desenvolvemos as Trapaças/Modificações,\npor favor, reporte os problemas relacionados ao autor da trapaça.\n\nCriou uma nova trapaça? Visite:\n No Image Available - Imagem Não Disponível + Nenhuma Imagem Disponível Serial: @@ -46,7 +46,7 @@ Select Cheat File: - Selecione o Arquivo de Cheat: + Selecione o Arquivo de Trapaça: Repository: @@ -54,7 +54,7 @@ Download Cheats - Baixar Cheats + Baixar Trapaças Delete File @@ -66,7 +66,7 @@ You can delete the cheats you don't want after downloading them. - Você pode excluir os cheats que não deseja após baixá-los. + Você pode excluir as trapaças que não deseja após baixá-las. Do you want to delete the selected file?\n%1 @@ -78,7 +78,7 @@ Download Patches - Baixar Patches + Baixar Modificações Save @@ -86,11 +86,11 @@ Cheats - Cheats + Trapaças Patches - Patches + Modificações Error @@ -118,7 +118,7 @@ Failed to parse XML: - Falha ao analisar XML: + Falha ao analisar o XML: Success @@ -154,19 +154,19 @@ Cheats Not Found - Cheats Não Encontrados + Trapaças Não Encontradas No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game. - Nenhum cheat encontrado para este jogo nesta versão do repositório selecionado, tente outro repositório ou uma versão diferente do jogo. + Nenhuma trapaça encontrada para este jogo nesta versão do repositório selecionado, tente outro repositório ou uma versão diferente do jogo. Cheats Downloaded Successfully - Cheats Baixados com Sucesso + Trapaças Baixadas com Sucesso 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. - Você baixou os cheats para esta versão do jogo do repositório selecionado com sucesso. É possível tentar baixar de outro repositório, se estiver disponível, também será possível utilizá-lo selecionando o arquivo da lista. + Você baixou as trapaças para esta versão do jogo do repositório selecionado com sucesso. É possível tentar baixar de outro repositório, se estiver disponível, também será possível utilizá-lo selecionando o arquivo da lista. Failed to save: @@ -182,11 +182,11 @@ Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game. - Patches Baixados com Sucesso! Todos os patches disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece em Cheats. Se o patch não aparecer, pode ser que ele não exista para o serial e versão específicas do jogo. + Modificações Baixados com Sucesso! Todos as modificações disponíveis para todos os jogos foram baixados, não é necessário baixá-los individualmente para cada jogo como acontece em trapaças. Se a modificação não aparecer, pode ser que ela não exista para o serial e versão específicas do jogo. Failed to parse JSON data from HTML. - Falha ao analisar dados JSON do HTML. + Falha ao analisar os dados JSON do HTML. Failed to retrieve HTML page. @@ -206,7 +206,7 @@ Incompatibility Notice - Aviso de incompatibilidade + Aviso de Incompatibilidade Failed to open file: @@ -214,7 +214,7 @@ XML ERROR: - ERRO de XML: + ERRO DE XML: Failed to open files.json for writing @@ -238,7 +238,7 @@ Can't apply cheats before the game is started - Não é possível aplicar cheats antes que o jogo comece. + Não é possível aplicar trapaças antes de começar o jogo. Close @@ -285,7 +285,7 @@ Update Available - Atualização disponível + Atualização Disponível Update Channel @@ -309,7 +309,7 @@ Check for Updates at Startup - Verificar Atualizações ao Iniciar + Verificar por Atualizações ao Iniciar Update @@ -376,7 +376,7 @@ Unable to open compatibility_data.json for writing. - Não foi possível abrir o compatibility_data.json para escrita. + Não foi possível abrir o compatibility_data.json para gravação. Unknown @@ -388,7 +388,7 @@ Boots - Boot + Inicia Menus @@ -487,7 +487,7 @@ Face Buttons - Botões de Face + Botões de Ação Triangle / Y @@ -535,12 +535,83 @@ Override Lightbar Color - Substituir cor da Lightbar + Substituir Cor da Barra de Luz Override Color Substituir a Cor + + Unable to Save + Não foi possível salvar + + + Cannot bind axis values more than once + Não é possível vincular os valores do eixo mais de uma vez + + + Save + Salvar + + + Apply + Aplicar + + + Restore Defaults + Restaurar Padrões + + + Cancel + Cancelar + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Editar atalhos de entrada do Teclado + Mouse e do Controle + + + Use Per-Game configs + Usar configurações por jogo + + + Error + Erro + + + Could not open the file for reading + Não foi possível abrir o arquivo para leitura + + + Could not open the file for writing + Não foi possível abrir o arquivo para gravação + + + Save Changes + Salvar Alterações + + + Do you want to save changes? + Gostaria de salvar as alterações? + + + Help + Ajuda + + + Do you want to reset your custom default config to the original default config? + Você gostaria de redefinir sua configuração padrão personalizada de volta para a configuração padrão original? + + + Do you want to reset this config to your custom default config? + Você gostaria de redefinir esta configuração para a sua configuração padrão personalizada? + + + Reset to Default + Redefinir ao Padrão + ElfViewer @@ -627,11 +698,11 @@ Play Time - Tempo Jogado + Tempo de Jogo Never Played - Nunca jogado + Nunca Jogado h @@ -651,7 +722,7 @@ Game does not initialize properly / crashes the emulator - Jogo não inicializa corretamente / trava o emulador + O jogo não inicializa corretamente ou trava o emulador Game boots, but only displays a blank screen @@ -659,15 +730,15 @@ Game displays an image but does not go past the menu - Jogo exibe imagem mas não passa do menu + O jogo exibe imagem mas não passa do menu Game has game-breaking glitches or unplayable performance - O jogo tem falhas que interrompem o jogo ou desempenho injogável + O jogo tem defeitos que interrompem o jogo ou desempenho injogável Game can be completed with playable performance and no major glitches - O jogo pode ser concluído com desempenho jogável e sem grandes falhas + O jogo pode ser concluído com desempenho jogável e sem grandes defeitos Click to see details on github @@ -709,7 +780,7 @@ Cheats / Patches - Cheats / Patches + Trapaças / Modificações SFO Viewer @@ -761,19 +832,23 @@ Delete... - Deletar... + Excluir... Delete Game - Deletar Jogo + Excluir Jogo Delete Update - Deletar Atualização + Excluir Atualização Delete DLC - Deletar DLC + Excluir DLC + + + Delete Trophy + Excluir Troféu Compatibility... @@ -825,7 +900,7 @@ This game has no DLC to delete! - Este jogo não tem DLC para deletar! + Este jogo não tem DLC para excluir! DLC @@ -833,11 +908,11 @@ Delete %1 - Deletar %1 + Excluir %1 Are you sure you want to delete %1's %2 directory? - Tem certeza de que deseja excluir o diretório %2 de %1 ? + Tem certeza de que deseja excluir o diretório %2 de %1? Open Update Folder @@ -849,7 +924,11 @@ This game has no update folder to open! - Este jogo não tem atualização para deletar! + Este jogo não possui pasta de atualização para abrir! + + + No log file found for this game! + Nenhum arquivo de registro foi encontrado para este jogo! Failed to convert icon. @@ -857,17 +936,48 @@ This game has no save data to delete! - Este jogo não tem dados salvos para deletar! + Este jogo não tem dados salvos para excluir! + + + This game has no saved trophies to delete! + Este jogo não tem troféus salvos para excluir! Save Data Dados Salvos + + Trophy + Troféus + SFO Viewer for Visualizador de SFO para + + HelpDialog + + Quickstart + Introdução + + + FAQ + Perguntas frequentes + + + Syntax + Sintaxe + + + Special Bindings + Atalhos Especiais + + + Keybindings + Teclas de atalho + + InstallDirSelect @@ -880,11 +990,222 @@ Install All Queued to Selected Folder - Instalar Todas da Fila para a Pasta Selecionada + Instalar Tudo da Fila para a Pasta Selecionada Delete PKG File on Install - Deletar PKG após instalação + Excluir o PKG após a Instalação + + + + KBMSettings + + Configure Controls + Configurar Controles + + + D-Pad + Direcional + + + Up + Cima + + + unmapped + não mapeado + + + Left + Esquerda + + + Right + Direita + + + Down + Baixo + + + Left Analog Halfmode + Meio Analógico Esquerdo + + + hold to move left stick at half-speed + Segure para mover o analógico esquerdo pela metade da velocidade + + + Left Stick + Analógico Esquerdo + + + Config Selection + Seleção de Configuração + + + Common Config + Configuração Comum + + + Use per-game configs + Usar configurações por jogo + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Editor de Texto + + + Help + Ajuda + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Clique do Touchpad + + + Mouse to Joystick + Mouse para Analógico + + + *press F7 ingame to activate + *Pressione F7 no jogo para ativar + + + R3 + R3 + + + Options + Opções + + + Mouse Movement Parameters + Parâmetros de Movimento do Mouse + + + note: click Help Button/Special Keybindings for more information + Nota: clique no botão de Ajuda e Atalhos Especiais para obter mais informações + + + Face Buttons + Botões de Ação + + + Triangle + Triângulo + + + Square + Quadrado + + + Circle + Círculo + + + Cross + Cruz + + + Right Analog Halfmode + Meio Analógico Direito + + + hold to move right stick at half-speed + Segure para mover o analógico direito pela metade da velocidade + + + Right Stick + Analógico Direito + + + Speed Offset (def 0.125): + Deslocamento de Velocidade (Pad 0,125): + + + Copy from Common Config + Copiar da Configuração Comum + + + Deadzone Offset (def 0.50): + Deslocamento da Zona Morta (Pad 0,50): + + + Speed Multiplier (def 1.0): + Multiplicador de Velocidade (Pad 1,0): + + + Common Config Selected + Configuração Comum Selecionada + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Este botão copia os mapeamentos da Configuração Comum para o perfil atualmente selecionado, e não pode ser usado quando o perfil atualmente selecionado é a Configuração Comum. + + + Copy values from Common Config + Copiar valores da Configuração Comum + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Você deseja substituir os mapeamentos existentes com os mapeamentos da Configuração Comum? + + + Unable to Save + Não foi possível salvar + + + Cannot bind any unique input more than once + Não é possível vincular qualquer entrada única mais de uma vez + + + Press a key + Aperte uma tecla + + + Cannot set mapping + Não é possível definir o mapeamento + + + Mousewheel cannot be mapped to stick outputs + A rolagem do mouse não pode ser mapeada para saídas do analógico + + + Save + Salvar + + + Apply + Aplicar + + + Restore Defaults + Restaurar Padrões + + + Cancel + Cancelar @@ -915,7 +1236,7 @@ Install application from a .pkg file - Instalar aplicação de um arquivo .pkg + Instalar aplicativo de um arquivo .pkg Recent Games @@ -923,7 +1244,7 @@ Open shadPS4 Folder - Abrir pasta shadPS4 + Abrir Pasta do shadPS4 Exit @@ -935,7 +1256,7 @@ Exit the application. - Sair da aplicação. + Sair do aplicativo. Show Game List @@ -963,11 +1284,11 @@ List View - Visualizar em Lista + Visualização em Lista Grid View - Visualizar em Grade + Visualização em Grade Elf Viewer @@ -979,11 +1300,19 @@ Download Cheats/Patches - Baixar Cheats/Patches + Baixar Trapaças/Modificações Dump Game List - Dumpar Lista de Jogos + Exportar Lista de Jogos + + + Trophy Viewer + Visualizador de Troféus + + + No games found. Please add your games to your library first. + Nenhum jogo encontrado. Adicione seus jogos à sua biblioteca primeiro. PKG Viewer @@ -1059,11 +1388,11 @@ Download Cheats For All Installed Games - Baixar Cheats para Todos os Jogos Instalados + Baixar Trapaças para Todos os Jogos Instalados Download Patches For All Games - Baixar Patches para Todos os Jogos + Baixar Modificações para Todos os Jogos Download Complete @@ -1071,15 +1400,15 @@ You have downloaded cheats for all the games you have installed. - Você baixou cheats para todos os jogos que instalou. + Você baixou trapaças para todos os jogos que instalou. Patches Downloaded Successfully! - Patches Baixados com Sucesso! + Modificações Baixadas com Sucesso! All Patches available for all games have been downloaded. - Todos os patches disponíveis para todos os jogos foram baixados. + Todos as modificações disponíveis para todos os jogos foram baixadas. Games: @@ -1107,15 +1436,15 @@ PKG and Game versions match: - As versões do PKG e do Jogo são igual: + As versões do PKG e do Jogo são iguais: Would you like to overwrite? - Gostaria de substituir? + Você gostaria de sobrescrever? PKG Version %1 is older than installed version: - Versão do PKG %1 é mais antiga do que a versão instalada: + A Versão do PKG %1 é mais antiga do que a versão instalada: Game is installed: @@ -1143,7 +1472,7 @@ PKG ERROR - ERRO de PKG + ERRO DE PKG Extracting PKG %1/%2 @@ -1194,7 +1523,7 @@ PKG ERROR - ERRO de PKG + ERRO DE PKG Name @@ -1222,7 +1551,7 @@ App Ver - App Ver + Versão do App FW @@ -1238,7 +1567,7 @@ Path - Diretório + Caminho File @@ -1309,11 +1638,15 @@ Trophy - Troféus + Troféu + + + Open the custom trophy images/sounds folder + Abrir a pasta de imagens e sons de troféus personalizados Logger - Registros de Log + Log/Registro Log Type @@ -1381,7 +1714,7 @@ Enable Shaders Dumping - Ativar Dumping de Shaders + Ativar Exportação de Shaders Enable NULL GPU @@ -1413,7 +1746,7 @@ Enable Debug Dumping - Ativar Depuração de Dumping + Ativar Exportação de Depuração Enable Vulkan Validation Layers @@ -1453,7 +1786,7 @@ Check for Updates at Startup - Verificar Atualizações ao Iniciar + Verificar por Atualizações ao Iniciar Always Show Changelog @@ -1476,8 +1809,8 @@ Música no Menu - Disable Trophy Pop-ups - Desabilitar Pop-ups dos Troféus + Disable Trophy Notification + Desativar Notificações de Troféu Background Image @@ -1497,7 +1830,7 @@ Update Compatibility Database On Startup - Atualizar Compatibilidade ao Inicializar + Atualizar Banco de Dados de Compatibilidade ao Inicializar Game Compatibility @@ -1537,15 +1870,15 @@ Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. - Idioma do console:\nDefine o idioma usado pelo jogo do PS4.\nRecomenda-se configurá-lo para um idioma que o jogo suporte, o que pode variar conforme a região. + Idioma do Console:\nDefine o idioma usado pelo jogo do PS4.\nRecomenda-se configurá-lo para um idioma que o jogo suporte, o que pode variar conforme a região. Emulator Language:\nSets the language of the emulator's user interface. - Idioma do emulador:\nDefine o idioma da interface do emulador. + Idioma do Emulador:\nDefine o idioma da interface do emulador. Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Ativar pasta de atualização separada:\nPermite instalar atualizações de jogos em uma pasta separada para fácil gerenciamento.\nIsso pode ser manualmente criado adicionando a atualização extraída à pasta do jogo com o nome "CUSA00000-UPDATE" onde o ID do CUSA corresponde ao ID do jogo. + Ativar Pasta de Atualização Separada:\nPermite instalar atualizações de jogos em uma pasta separada para fácil gerenciamento.\nIsso pode ser manualmente criado adicionando a atualização extraída à pasta do jogo com o nome "CUSA00000-UPDATE" onde o ID do CUSA corresponde ao ID do jogo. Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. @@ -1557,7 +1890,7 @@ Username:\nSets the PS4's account username, which may be displayed by some games. - Nome de usuário:\nDefine o nome de usuário da conta PS4 que pode ser exibido por alguns jogos. + Nome de usuário:\nDefine o nome de usuário da conta do PS4, que pode ser exibido por alguns jogos. Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. @@ -1565,11 +1898,11 @@ Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. - Tipo de Registro:\nDetermina se a saída da janela de log deve ser sincronizada por motivos de desempenho. Pode impactar negativamente a emulação. + Tipo de Registro:\nDetermina se a saída da janela de registro deve ser sincronizada por motivos de desempenho. Pode impactar negativamente na emulação. 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. - Filtro de Registro:\nFiltra o registro para exibir apenas informações específicas.\nExemplos: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNíveis: Trace, Debug, Info, Warning, Error, Critical - nesta ordem, um nível específico silencia todos os níveis anteriores na lista e registra todos os níveis após ele. + Filtro do Registro:\nFiltra o registro para exibir apenas informações específicas.\nExemplos: "Core:Trace" "Lib.Pad:Debug Common.Filesystem:Error" "*:Critical"\nNíveis: Trace, Debug, Info, Warning, Error, Critical - nesta ordem, um nível específico silencia todos os níveis anteriores na lista e registra todos os níveis após este. 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. @@ -1601,7 +1934,7 @@ Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - Exibir Dados de Compatibilidade:\nExibe informações de compatibilidade dos jogos na visualização de tabela.\nAtivar "Atualizar Compatibilidade ao Inicializar" para obter informações atualizadas. + Exibir Dados de Compatibilidade:\nExibe informações de compatibilidade dos jogos na visualização de tabela.\nAtive "Atualizar Compatibilidade ao Inicializar" para obter informações atualizadas. Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. @@ -1609,7 +1942,7 @@ Update Compatibility Database:\nImmediately update the compatibility database. - Atualizar Lista de Compatibilidade:\nAtualizar imediatamente o banco de dados de compatibilidade. + Atualizar Lista de Compatibilidade:\nAtualiza imediatamente o banco de dados de compatibilidade. Never @@ -1633,7 +1966,7 @@ Touchpad Center - Touchpad Centro + Centro do Touchpad None @@ -1653,7 +1986,7 @@ Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. - Ativar Dumping de Shaders:\nArmazena os shaders do jogo em uma pasta durante a renderização para fins de depuração técnica. + Ativar Exportação de Shaders:\nArmazena os shaders do jogo em uma pasta durante a renderização para fins de depuração técnica. Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. @@ -1677,7 +2010,7 @@ Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. - Ativar Depuração de Dumping:\nArmazena os símbolos de importação, exportação e informações do cabeçalho do arquivo do programa PS4 atual em um diretório. + Ativar Exportação de Depuração:\nArmazena os símbolos de importação, exportação e informações do cabeçalho do arquivo do programa PS4 atual em um diretório. Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation. @@ -1705,11 +2038,11 @@ Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Marcadores de Depuração de Host:\nInsere informações vindo do emulador como marcadores para comandos AMDGPU específicos em torno de comandos Vulkan, além de fornecer nomes de depuração aos recursos.\nSe isso estiver habilitado, ative os Diagnósticos de Falha.\nÚtil para programas como o RenderDoc. + Marcadores de Depuração do Host:\nInsere informações vindo do emulador como marcadores para comandos AMDGPU específicos em torno de comandos Vulkan, além de fornecer nomes de depuração aos recursos.\nSe isso estiver habilitado, ative os Diagnósticos de Falhas.\nÚtil para programas como o RenderDoc. Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Marcadores de Depuração de Convidado:\nInsere quaisquer marcadores de depuração que o próprio jogo adicionou ao buffer de comando.\nSe isso estiver habilitado, ative os Diagnósticos de Falha.\nÚtil para programas como o RenderDoc. + Marcadores de Depuração do Convidado:\nInsere quaisquer marcadores de depuração que o próprio jogo adicionou ao buffer de comando.\nSe isso estiver habilitado, ative os Diagnósticos de Falhas.\nÚtil para programas como o RenderDoc. Save Data Path:\nThe folder where game save data will be saved. @@ -1797,11 +2130,67 @@ Separate Log Files - Separar Arquivos de Log + Separar Arquivos de Registro Separate Log Files:\nWrites a separate logfile for each game. - Separar Arquivos de Log:\nGrava um arquivo de log para cada jogo. + Separar Arquivos de Registro:\nGrava um arquivo de registro para cada jogo. + + + Trophy Notification Position + Posição da Notificação do Troféu + + + Left + Esquerda + + + Right + Direita + + + Top + Acima + + + Bottom + Abaixo + + + Notification Duration + Duração da Notificação + + + Portable User Folder + Pasta Portátil do Usuário + + + Create Portable User Folder from Common User Folder + Criar Pasta Portátil do Usuário a partir da Pasta Comum do Usuário + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Pasta Portátil do Usuário:\nArmazena as configurações e dados do shadPS4 que serão aplicados apenas à compilação do shadPS4 localizada na pasta atual. Reinicie o aplicativo após criar a pasta portátil do usuário para começar a usá-la. + + + Cannot create portable user folder + Não é possível criar a pasta portátil do usuário + + + %1 already exists + %1 já existe + + + Portable user folder created + Pasta portátil do usuário criada + + + %1 successfully created. + %1 criado com sucesso. + + + 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. + 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. @@ -1810,5 +2199,25 @@ Trophy Viewer Visualizador de Troféus + + Select Game: + Selecionar Jogo: + + + Progress + Progresso + + + Show Earned Trophies + Mostrar Troféus Conquistados + + + Show Not Earned Trophies + Mostrar Troféus Não Conquistados + + + Show Hidden Trophies + Mostrar Troféus Ocultos + diff --git a/src/qt_gui/translations/pt_PT.ts b/src/qt_gui/translations/pt_PT.ts index e5cbedb96..455955fad 100644 --- a/src/qt_gui/translations/pt_PT.ts +++ b/src/qt_gui/translations/pt_PT.ts @@ -541,6 +541,77 @@ Override Color Substituir Cor + + Unable to Save + Não é possível salvar + + + Cannot bind axis values more than once + Não foi possível atribuir os valores do eixo X ou Y mais de uma vez + + + Save + Salvar + + + Apply + Aplicar + + + Restore Defaults + Restaurar o Padrão + + + Cancel + Cancelar + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Editar comandos do Teclado + Mouse e do Controle + + + Use Per-Game configs + Use uma configuração para cada jogo + + + Error + Erro + + + Could not open the file for reading + Não foi possível abrir o arquivo para ler + + + Could not open the file for writing + Não foi possível abrir o arquivo para escrever + + + Save Changes + Salvar mudanças + + + Do you want to save changes? + Salvar as mudanças? + + + Help + Ajuda + + + Do you want to reset your custom default config to the original default config? + Restaurar a configuração customizada padrão para a configuração original padrão? + + + Do you want to reset this config to your custom default config? + Deseja redefinir esta configuração para a configuração padrão personalizada? + + + Reset to Default + Resetar ao Padrão + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Eliminar DLC + + Delete Trophy + Eliminar Troféu + Compatibility... Compatibilidade... @@ -851,6 +926,10 @@ This game has no update folder to open! Este jogo não tem nenhuma pasta de atualização para abrir! + + No log file found for this game! + Não foi encontrado nenhum ficheiro de registo para este jogo! + Failed to convert icon. Falha ao converter ícone. @@ -859,15 +938,46 @@ This game has no save data to delete! Este jogo não tem dados guardados para eliminar! + + This game has no saved trophies to delete! + Este jogo não tem troféus guardados para eliminar! + Save Data Dados Guardados + + Trophy + Troféus + SFO Viewer for Visualizador SFO para + + HelpDialog + + Quickstart + Início Rápido + + + FAQ + Perguntas Frequentes + + + Syntax + Sintaxe + + + Special Bindings + Atalhos Especiais + + + Keybindings + Combinações de Teclas + + InstallDirSelect @@ -887,6 +997,217 @@ Eliminar Ficheiro PKG após Instalação + + KBMSettings + + Configure Controls + Configurar Comandos + + + D-Pad + Botões de Direção + + + Up + Cima + + + unmapped + não mapeado + + + Left + Esquerda + + + Right + Direita + + + Down + Baixo + + + Left Analog Halfmode + Meio Modo do Manípulo Esquerdo + + + hold to move left stick at half-speed + mantenha pressionado para mover o manípulo esquerdo à metade da velocidade + + + Left Stick + Manípulo Esquerdo + + + Config Selection + Seleção de Configuração + + + Common Config + Configuração Comum + + + Use per-game configs + Utilizar configurações por jogo + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Editor de Texto + + + Help + Ajuda + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Clique do Touchpad + + + Mouse to Joystick + Rato para Manípulo + + + *press F7 ingame to activate + *pressione F7 em jogo para ativar + + + R3 + R3 + + + Options + Opções + + + Mouse Movement Parameters + Parâmetros de Movimento do Rato + + + note: click Help Button/Special Keybindings for more information + nota: clique no Botão de Ajuda/Special Keybindings para obter mais informações + + + Face Buttons + Botões Frontais + + + Triangle + Triângulo + + + Square + Quadrado + + + Circle + Círculo + + + Cross + Cruz + + + Right Analog Halfmode + Meio Modo do Manípulo Direito + + + hold to move right stick at half-speed + mantenha pressionado para mover o manípulo direito à metade da velocidade + + + Right Stick + Manípulo Direito + + + Speed Offset (def 0.125): + Deslocamento de Velocidade (def 0,125): + + + Copy from Common Config + Copiar da Configuração Comum + + + Deadzone Offset (def 0.50): + Deslocamento da Zona Morta (def 0,50): + + + Speed Multiplier (def 1.0): + Multiplicador de Velocidade (def 1,0): + + + Common Config Selected + Configuração Comum Selecionada + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Este botão copia mapeamentos da Configuração Comum para o perfil atualmente selecionado, e não pode ser usado quando o perfil atualmente selecionado é a Configuração Comum. + + + Copy values from Common Config + Copiar valores da Configuração Comum + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Substituir mapeamentos existentes com os mapeamentos da Configuração Comum? + + + Unable to Save + Não é possível salvar + + + Cannot bind any unique input more than once + Não é possível vincular qualquer entrada única mais de uma vez + + + Press a key + Pressione uma tecla + + + Cannot set mapping + Não é possível definir o mapeamento + + + Mousewheel cannot be mapped to stick outputs + Roda do rato não pode ser mapeada para saídas empates + + + Save + Salvar + + + Apply + Aplicar + + + Restore Defaults + Restaurar Definições + + + Cancel + Cancelar + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Exportar Lista de Jogos + + Trophy Viewer + Visualizador de Troféus + + + No games found. Please add your games to your library first. + Nenhum jogo encontrado. Por favor, adicione os seus jogos à sua biblioteca primeiro. + PKG Viewer Visualizador PKG @@ -1311,6 +1640,10 @@ Trophy Troféus + + Open the custom trophy images/sounds folder + Abrir a pasta de imagens/sons de troféus personalizados + Logger Registos @@ -1476,8 +1809,8 @@ Música de Título - Disable Trophy Pop-ups - Desativar Pop-ups dos Troféus + Disable Trophy Notification + Desativar Notificações de Troféus Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separar Ficheiros de Registo:\nEscreve um ficheiro de registo para cada jogo. + + Trophy Notification Position + Posição da Notificação do Troféu + + + Left + Esquerda + + + Right + Direita + + + Top + Acima + + + Bottom + Abaixo + + + Notification Duration + Duração da Notificação + + + Portable User Folder + Pasta de Utilizador Portátil + + + Create Portable User Folder from Common User Folder + Criar Pasta de Utilizador Portátil a partir da Pasta de Utilizador Comum + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Pasta de utilizador portátil:\nArmazena as definições e dados do shadPS4 que serão aplicados apenas na compilação do shadPS4 localizada na pasta atual. Reinicie a aplicação após criar a pasta de utilizador portátil para começar a usá-la. + + + Cannot create portable user folder + Não é possível criar pasta de utilizador portátil + + + %1 already exists + %1 já existe + + + Portable user folder created + Pasta de utilizador portátil criada + + + %1 successfully created. + %1 criado com sucesso. + + + 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. + 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. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Visualizador de Troféus + + Select Game: + Escolha o Jogo: + + + Progress + Progresso + + + Show Earned Trophies + Mostrar Troféus Conquistados + + + Show Not Earned Trophies + Mostrar Troféus Não Conquistados + + + Show Hidden Trophies + Mostrar Troféus Ocultos + diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 4d9052261..9c7720e17 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -7,7 +7,7 @@ AboutDialog About shadPS4 - About shadPS4 + Despre shadPS4 shadPS4 is an experimental open-source emulator for the PlayStation 4. @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -584,7 +655,7 @@ Directory to install DLC - Directory to install DLC + Director pentru a instala DLC @@ -775,6 +846,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -845,11 +920,15 @@ Delete Save Data - Delete Save Data + Șterge Salvare Date This game has no update folder to open! - This game has no update folder to open! + Acest joc nu are folderul de actualizări pentru a fi deschis! + + + No log file found for this game! + No log file found for this game! Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -1190,7 +1519,7 @@ PKGViewer Open Folder - Open Folder + Deschide Folder PKG ERROR @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index d9e90d1a2..68eaabc34 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -541,6 +541,77 @@ Override Color Заменить цвет + + Unable to Save + Не удаётся сохранить + + + Cannot bind axis values more than once + Невозможно привязать значения оси более одного раза + + + Save + Сохранить + + + Apply + Применить + + + Restore Defaults + По умолчанию + + + Cancel + Отмена + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Редактировать бинды клавиатуры + мыши и контроллера + + + Use Per-Game configs + Использовать настройки для каждой игры + + + Error + Ошибка + + + Could not open the file for reading + Не удалось открыть файл для чтения + + + Could not open the file for writing + Не удалось открыть файл для записи + + + Save Changes + Сохранить изменения + + + Do you want to save changes? + Хотите сохранить изменения? + + + Help + Помощь + + + Do you want to reset your custom default config to the original default config? + Хотите ли вы сбросить ваш пользовательский конфиг по умолчанию к первоначальному конфигу по умолчанию? + + + Do you want to reset this config to your custom default config? + Хотите ли вы сбросить этот конфиг к вашему пользовательскому конфигу по умолчанию? + + + Reset to Default + Сбросить по умолчанию + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Удалить DLC + + Delete Trophy + Удалить трофей + Compatibility... Совместимость... @@ -851,6 +926,10 @@ This game has no update folder to open! У этой игры нет папки обновлений, которую можно открыть! + + No log file found for this game! + Не найден файл логов для этой игры! + Failed to convert icon. Не удалось преобразовать иконку. @@ -859,15 +938,46 @@ This game has no save data to delete! У этой игры нет сохранений, которые можно удалить! + + This game has no saved trophies to delete! + У этой игры нет сохраненных трофеев для удаления! + Save Data Сохранения + + Trophy + Трофей + SFO Viewer for Просмотр SFO для + + HelpDialog + + Quickstart + Быстрый старт + + + FAQ + ЧАВО + + + Syntax + Синтаксис + + + Special Bindings + Специальные бинды + + + Keybindings + Бинды клавиш + + InstallDirSelect @@ -887,6 +997,217 @@ Удалить файл PKG при установке + + KBMSettings + + Configure Controls + Настроить управление + + + D-Pad + Крестовина + + + Up + Вверх + + + unmapped + не назначено + + + Left + Влево + + + Right + Вправо + + + Down + Вниз + + + Left Analog Halfmode + Левый стик вполовину + + + hold to move left stick at half-speed + удерживайте для перемещения левого стика вполовину меньше + + + Left Stick + Левый стик + + + Config Selection + Выбор конфига + + + Common Config + Общий конфиг + + + Use per-game configs + Использовать настройки для каждой игры + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Текстовый редактор + + + Help + Помощь + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Нажатие на тачпад + + + Mouse to Joystick + Мышь в джойстик + + + *press F7 ingame to activate + *нажмите F7 в игре для активации + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Параметры движения мыши + + + note: click Help Button/Special Keybindings for more information + примечание: нажмите кнопку Помощь/Специальные бинды для получения дополнительной информации + + + Face Buttons + Кнопки действий + + + Triangle + Треугольник + + + Square + Квадрат + + + Circle + Круг + + + Cross + Крест + + + Right Analog Halfmode + Правый стик вполовину + + + hold to move right stick at half-speed + удерживайте для перемещения правого стика вполовину меньше + + + Right Stick + Правый стик + + + Speed Offset (def 0.125): + Смещение скорости (по умолч 0.125): + + + Copy from Common Config + Копировать из общего конфига + + + Deadzone Offset (def 0.50): + Смещение мёртвой зоны (по умолч 0.50) + + + Speed Multiplier (def 1.0): + Множитель скорости (по умолч 1.0) + + + Common Config Selected + Выбран общий конфиг + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Эта кнопка копирует настройки из общего конфига в текущий выбранный профиль, и не может быть использован, когда выбранный профиль это общий конфиг. + + + Copy values from Common Config + Копировать значения из общего конфига + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Вы хотите перезаписать существующие настройки настройками из общего конфига? + + + Unable to Save + Не удаётся сохранить + + + Cannot bind any unique input more than once + Невозможно привязать уникальный ввод более одного раза + + + Press a key + Нажмите кнопку + + + Cannot set mapping + Не удаётся задать настройки + + + Mousewheel cannot be mapped to stick outputs + Колесо не может быть назначено для вывода стиков + + + Save + Сохранить + + + Apply + Применить + + + Restore Defaults + По умолчанию + + + Cancel + Отмена + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Дамп списка игр + + Trophy Viewer + Просмотр трофеев + + + No games found. Please add your games to your library first. + Не найдено ни одной игры. Пожалуйста, сначала добавьте игры в библиотеку. + PKG Viewer Просмотр PKG @@ -1311,6 +1640,10 @@ Trophy Трофеи + + Open the custom trophy images/sounds folder + Открыть папку с пользовательскими изображениями/звуками трофеев + Logger Логирование @@ -1476,8 +1809,8 @@ Заглавная музыка - Disable Trophy Pop-ups - Отключить уведомления о трофеях + Disable Trophy Notification + Отключить уведомления о трофее Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Отдельные файлы логов:\nПишет отдельный файл логов для каждой игры. + + Trophy Notification Position + Местоположение уведомления о трофее + + + Left + Слева + + + Right + Справа + + + Top + Сверху + + + Bottom + Снизу + + + Notification Duration + Продолжительность уведомления + + + Portable User Folder + Портативная папка пользователя + + + Create Portable User Folder from Common User Folder + Создать портативную папку пользователя из общей папки пользователя + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Портативная папка пользователя:\nХранит настройки и данные shadPS4, которые будут применяться только к билду shadPS4, расположенному в этой папке. Перезагрузите приложение после создания портативной папки пользователя чтобы начать использовать её. + + + Cannot create portable user folder + Невозможно создать папку для портативной папки пользователя + + + %1 already exists + %1 уже существует + + + Portable user folder created + Портативная папка пользователя создана + + + %1 successfully created. + %1 успешно создано. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Открыть папку с пользовательскими изображениями/звуками трофеев:\nВы можете добавить пользовательские изображения к трофеям и аудио.\nДобавьте файлы в custom_trophy со следующими именами:\ntrophy.wav ИЛИ trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nПримечание: звук будет работать только в QT-версии. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Просмотр трофеев + + Select Game: + Выберите игру: + + + Progress + Прогресс + + + Show Earned Trophies + Показать заработанные трофеи + + + Show Not Earned Trophies + Показать не заработанные трофеи + + + Show Hidden Trophies + Показать скрытые трофеи + diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index ec07db041..657f78d0d 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -74,7 +74,7 @@ Select Patch File: - Përzgjidh Skedarin e Arnës: + Përzgjidh skedarin e arnës: Download Patches @@ -138,7 +138,7 @@ File Exists - Skedari Ekziston + Skedari ekziston File already exists. Do you want to replace it? @@ -411,7 +411,7 @@ D-Pad - D-Pad + Shigjetat Up @@ -451,7 +451,7 @@ Use per-game configs - Përdor konfigurime për secilën lojë + Përdor konfigurime të veçanta për secilën lojë L1 / LB @@ -541,6 +541,77 @@ Override Color Zëvendëso Ngjyrën + + Unable to Save + Ruajtja Dështoi + + + Cannot bind axis values more than once + Nuk mund të caktohen vlerat e boshtit më shumë se një herë + + + Save + Ruaj + + + Apply + Zbato + + + Restore Defaults + Rikthe Paracaktimet + + + Cancel + Anulo + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Redakto caktimet e hyrjeve për Tastierën + Miun dhe Dorezën + + + Use Per-Game configs + Përdor konfigurime për secilën lojë + + + Error + Gabim + + + Could not open the file for reading + Skedari nuk mund të hapet për lexim + + + Could not open the file for writing + Skedari nuk mund të hapet për shkrim + + + Save Changes + Ruaj Ndryshimet + + + Do you want to save changes? + Do të ruash ndryshimet? + + + Help + Ndihmë + + + Do you want to reset your custom default config to the original default config? + A do ta rivendosësh konfigurimin tënd të paracaktuar të personalizuar te konfigurimi i paracaktuar origjinal? + + + Do you want to reset this config to your custom default config? + A do ta rivendosësh këtë konfigurim në konfigurimin tënd të paracaktuar të personalizuar? + + + Reset to Default + Rivendos në të Paracaktuarit + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Fshi DLC-në + + Delete Trophy + Fshi Trofeun + Compatibility... Përputhshmëria... @@ -851,6 +926,10 @@ This game has no update folder to open! Kjo lojë nuk ka dosje përditësimi për të hapur! + + No log file found for this game! + Nuk u gjet asnjë skedar ditari për këtë lojë! + Failed to convert icon. Konvertimi i ikonës dështoi. @@ -859,15 +938,46 @@ This game has no save data to delete! Kjo lojë nuk ka të dhëna ruajtje për të fshirë! + + This game has no saved trophies to delete! + Kjo lojë nuk ka trofe të ruajtur për të fshirë! + Save Data Të dhënat e ruajtjes + + Trophy + Trofeu + SFO Viewer for Shikuesi SFO për + + HelpDialog + + Quickstart + Nisje e shpejtë + + + FAQ + Pyetje të Shpeshta + + + Syntax + Sintaksa + + + Special Bindings + Caktimet e Veçantë + + + Keybindings + Caktimet e Tasteve + + InstallDirSelect @@ -887,6 +997,217 @@ Fshi skedarin PKG pas instalimit + + KBMSettings + + Configure Controls + Konfiguro Kontrollet + + + D-Pad + Shigjetat + + + Up + Lartë + + + unmapped + pacaktuar + + + Left + Majtas + + + Right + Djathtas + + + Down + Poshtë + + + Left Analog Halfmode + Mënyra e gjysmuar për levën e majtë + + + hold to move left stick at half-speed + mbaj shtypur për të lëvizur levën e majtë me gjysmën e shpejtësisë + + + Left Stick + Leva e Majtë + + + Config Selection + Zgjedhja e Konfigurimit + + + Common Config + Konfigurim i Përbashkët + + + Use per-game configs + Përdor konfigurime për secilën lojë + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Redaktuesi i Tekstit + + + Help + Ndihmë + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Klikim i Panelit me Prekje + + + Mouse to Joystick + Miu në Levë + + + *press F7 ingame to activate + *shtyp F7 gjatë lojës për ta aktivizuar + + + R3 + R3 + + + Options + Rregullime + + + Mouse Movement Parameters + Parametrat e Lëvizjes së Miut + + + note: click Help Button/Special Keybindings for more information + shënim: kliko Butonin e Ndihmës/Caktimet e Tasteve të Veçantë për më shumë informacion + + + Face Buttons + Butonat Kryesore + + + Triangle + Trekëndësh + + + Square + Katror + + + Circle + Rreth + + + Cross + Kryq + + + Right Analog Halfmode + Mënyra e gjysmuar për levën e djathtë + + + hold to move right stick at half-speed + mbaj shtypur për të lëvizur levën e djathtë me gjysmën e shpejtësisë + + + Right Stick + Leva e Djathtë + + + Speed Offset (def 0.125): + Ofset i Shpejtësisë (paracaktuar 0.125): + + + Copy from Common Config + Kopjo nga Konfigurimi i Përbashkët + + + Deadzone Offset (def 0.50): + Ofseti i Zonës së Vdekur (paracaktuar 0.50): + + + Speed Multiplier (def 1.0): + Shumëzuesi i Shpejtësisë (paracaktuar 1.0): + + + Common Config Selected + Konfigurimi i Përbashkët i Zgjedhur + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Ky buton kopjon caktimet nga Konfigurimi i Përbashkët në profilin e zgjedhur aktualisht, dhe nuk mund të përdoret kur profili i zgjedhur aktualisht është Konfigurimi i Përbashkët. + + + Copy values from Common Config + Kopjo vlerat nga Konfigurimi i Përbashkët + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + A dëshiron të mbishkruash caktimet ekzistuese me ato nga Konfigurimi i Përbashkët? + + + Unable to Save + Ruajtja Dështoi + + + Cannot bind any unique input more than once + Asnjë hyrje unike nuk mund të caktohet më shumë se një herë + + + Press a key + Shtyp një tast + + + Cannot set mapping + Caktimi nuk u vendos dot + + + Mousewheel cannot be mapped to stick outputs + Rrota e miut nuk mund të caktohet për daljet e levës + + + Save + Ruaj + + + Apply + Zbato + + + Restore Defaults + Rikthe Paracaktimet + + + Cancel + Anulo + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Zbraz Listën e Lojërave + + Trophy Viewer + Shikuesi i Trofeve + + + No games found. Please add your games to your library first. + Nuk u gjetën lojëra. Shto lojërat në librarinë tënde fillimisht. + PKG Viewer Shikuesi i PKG @@ -1285,7 +1614,7 @@ Default tab when opening settings - Skeda e parazgjedhur kur hapen cilësimet + Skeda e paracaktuar kur hapen cilësimet Show Game Size In List @@ -1311,6 +1640,10 @@ Trophy Trofeu + + Open the custom trophy images/sounds folder + Hap dosjen e imazheve/tingujve të trofeve të personalizuar + Logger Regjistruesi i ditarit @@ -1381,7 +1714,7 @@ Enable Shaders Dumping - Aktivizo Zbrazjen e Shaders-ave + Aktivizo Zbrazjen e Shader-ave Enable NULL GPU @@ -1457,7 +1790,7 @@ Always Show Changelog - Shfaq gjithmonë regjistrin e ndryshimeve + Shfaq gjithmonë ditarin e ndryshimeve Update Channel @@ -1476,8 +1809,8 @@ Muzika e titullit - Disable Trophy Pop-ups - Çaktivizo njoftimet për Trofetë + Disable Trophy Notification + Çaktivizo Njoftimin e Trofeut Background Image @@ -1525,7 +1858,7 @@ Restore Defaults - Rikthe paracaktimet + Rikthe Paracaktimet Close @@ -1597,7 +1930,7 @@ Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. - Sjellja e butonit mbrapa:\nLejon të përcaktohet se në cilën pjesë të tastierës prekëse do të imitojë një prekje butoni mbrapa. + Sjellja e butonit mbrapa:\nLejon të përcaktohet se në cilën pjesë të panelit me prekje të dorezës do të imitojë një prekje butoni mbrapa. Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. @@ -1625,15 +1958,15 @@ Touchpad Left - Tastiera prekëse majtas + Paneli me Prekje Majtas Touchpad Right - Tastiera prekëse djathtas + Paneli me Prekje Djathtas Touchpad Center - Tastiera prekëse në qendër + Paneli me Prekje në Qendër None @@ -1765,43 +2098,99 @@ Video - Video + Video Display Mode - Display Mode + Mënyra e Shfaqjes Windowed - Windowed + Dritare Fullscreen - Fullscreen + Ekran të plotë Fullscreen (Borderless) - Fullscreen (Borderless) + Ekran të plotë (Pa kufij) Window Size - Window Size + Masa e Dritares W: - W: + Gjer: H: - H: + Lart: Separate Log Files - Separate Log Files + Skedarë të Ditarit të Ndarë Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Skedarë të Ditarit të Ndarë:\nShkruan një skedar të ditarit të veçuar për secilën lojë. + + + Trophy Notification Position + Pozicioni i Njoftimit të Trofeve + + + Left + Majtas + + + Right + Djathtas + + + Top + Sipër + + + Bottom + Poshtë + + + Notification Duration + Kohëzgjatja e Njoftimit + + + Portable User Folder + Dosja Portative e Përdoruesit + + + Create Portable User Folder from Common User Folder + Krijo Dosje Portative të Përdoruesit nga Dosja e Përbashkët e Përdoruesit + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Dosja portative e përdoruesit:\nRuan cilësimet dhe të dhënat të shadPS4 që do të zbatohen vetëm për konstruktin e shadPS4 të vendosur në dosjen aktuale. Rinis aplikacionin pasi të krijosh dosjen portative te përdoruesit për ta përdorur. + + + Cannot create portable user folder + Dosja portative e përdoruesit nuk u krijua dot + + + %1 already exists + %1 tashmë ekziston + + + Portable user folder created + Dosja portative e përdoruesit u krijua + + + %1 successfully created. + %1 u krijua me sukses. + + + 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. + 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. @@ -1810,5 +2199,25 @@ Trophy Viewer Shikuesi i Trofeve + + Select Game: + Zgjidh Lojën: + + + Progress + Ecuria + + + Show Earned Trophies + Shfaq Trofetë që janë fituar + + + Show Not Earned Trophies + Shfaq Trofetë që nuk janë fituar + + + Show Hidden Trophies + Shfaq Trofetë e Fshehur + diff --git a/src/qt_gui/translations/sv_SE.ts b/src/qt_gui/translations/sv_SE.ts index bdd2d2aa0..a002b150a 100644 --- a/src/qt_gui/translations/sv_SE.ts +++ b/src/qt_gui/translations/sv_SE.ts @@ -541,6 +541,77 @@ Override Color Åsidosätt färg + + Unable to Save + Kunde inte spara + + + Cannot bind axis values more than once + Kan inte binda axelvärden fler än en gång + + + Save + Spara + + + Apply + Tillämpa + + + Restore Defaults + Återställ till standard + + + Cancel + Avbryt + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Redigera inmatningsbindningar för tangentbord + mus och kontroller + + + Use Per-Game configs + Använd konfigurationer per-spel + + + Error + Fel + + + Could not open the file for reading + Kunde inte öppna filen för läsning + + + Could not open the file for writing + Kunde inte öppna filen för skrivning + + + Save Changes + Spara ändringar + + + Do you want to save changes? + Vill du spara ändringarna? + + + Help + Hjälp + + + Do you want to reset your custom default config to the original default config? + Vill du återställa din anpassade standardkonfiguration till ursprungliga standardkonfigurationen? + + + Do you want to reset this config to your custom default config? + Vill du återställa denna konfiguration till din anpassade standardkonfiguration? + + + Reset to Default + Återställ till standard + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Ta bort DLC + + Delete Trophy + Ta bort trofé + Compatibility... Kompatibilitet... @@ -851,6 +926,10 @@ This game has no update folder to open! Detta spel har ingen uppdateringsmapp att öppna! + + No log file found for this game! + Ingen loggfil hittades för detta spel! + Failed to convert icon. Misslyckades med att konvertera ikon. @@ -859,15 +938,46 @@ This game has no save data to delete! Detta spel har inget sparat data att ta bort! + + This game has no saved trophies to delete! + Detta spel har inga sparade troféer att ta bort! + Save Data Sparat data + + Trophy + Trofé + SFO Viewer for SFO-visare för + + HelpDialog + + Quickstart + Snabbstart + + + FAQ + Frågor och svar + + + Syntax + Syntax + + + Special Bindings + Speciella bindningar + + + Keybindings + Tangentbindningar + + InstallDirSelect @@ -887,6 +997,217 @@ Ta bort PKG-fil efter installation + + KBMSettings + + Configure Controls + Konfigurera kontroller + + + D-Pad + Riktningsknappar + + + Up + Upp + + + unmapped + inte mappad + + + Left + Vänster + + + Right + Höger + + + Down + Ner + + + Left Analog Halfmode + Halvläge för vänster analog + + + hold to move left stick at half-speed + håll ner för att flytta vänster spak i halvfart + + + Left Stick + Vänster spak + + + Config Selection + Konfigurationsval + + + Common Config + Gemensam konfiguration + + + Use per-game configs + Använd konfiguration per-spel + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Textredigerare + + + Help + Hjälp + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Klick på styrplatta + + + Mouse to Joystick + Mus till styrspak + + + *press F7 ingame to activate + *tryck F7 i spelet för att aktivera + + + R3 + R3 + + + Options + Alternativ + + + Mouse Movement Parameters + Parametrar för musrörelse + + + note: click Help Button/Special Keybindings for more information + observera: klicka på Hjälp-knapp/Speciella tangentbindningar för mer information + + + Face Buttons + Handlingsknappar + + + Triangle + Triangel + + + Square + Fyrkant + + + Circle + Cirkel + + + Cross + Kryss + + + Right Analog Halfmode + Halvläge för höger analog + + + hold to move right stick at half-speed + håll ner för att flytta höger spak i halvfart + + + Right Stick + Höger spak + + + Speed Offset (def 0.125): + Offset för hastighet (standard 0.125): + + + Copy from Common Config + Kopiera från gemensam konfiguration + + + Deadzone Offset (def 0.50): + Offset för dödläge (standard 0.50): + + + Speed Multiplier (def 1.0): + Hastighetsmultiplikator (standard 1.0): + + + Common Config Selected + Gemensam konfiguration valdes + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Den här knappen kopierar mappningar från gemensam konfiguration till den aktuella valda profilen och kan inte användas när den aktuella valda profilen är gemensam konfiguration. + + + Copy values from Common Config + Kopiera värden från gemensam konfiguration + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Vill du skriva över befintliga mappningar med mappningarna från gemensam konfiguration? + + + Unable to Save + Kunde inte spara + + + Cannot bind any unique input more than once + Kan inte binda någon unik inmatning fler än en gång + + + Press a key + Tryck på en tangent + + + Cannot set mapping + Kan inte ställa in mappning + + + Mousewheel cannot be mapped to stick outputs + Mushjulet kan inte mappas till spakutmatningar + + + Save + Spara + + + Apply + Tillämpa + + + Restore Defaults + Återställ till standard + + + Cancel + Avbryt + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dumpa spellista + + Trophy Viewer + Trofévisare + + + No games found. Please add your games to your library first. + Inga spel hittades. Lägg till dina spel till biblioteket först. + PKG Viewer PKG-visare @@ -1311,6 +1640,10 @@ Trophy Troféer + + Open the custom trophy images/sounds folder + Öppna mappen för anpassade trofébilder/ljud + Logger Loggning @@ -1476,8 +1809,8 @@ Titelmusik - Disable Trophy Pop-ups - Inaktivera popup för troféer + Disable Trophy Notification + Inaktivera troféaviseringar Background Image @@ -1765,43 +2098,99 @@ Video - Video + Video Display Mode - Display Mode + Visningsläge Windowed - Windowed + Fönster Fullscreen - Fullscreen + Helskärm Fullscreen (Borderless) - Fullscreen (Borderless) + Helskärm (kantlöst) Window Size - Window Size + Fönsterstorlek W: - W: + B: H: - H: + H: Separate Log Files - Separate Log Files + Separata loggfiler Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Separata loggfiler:\nSkriver en separat loggfil för varje spel. + + + Trophy Notification Position + Aviseringsposition för trofé + + + Left + Vänster + + + Right + Höger + + + Top + Överst + + + Bottom + Nederst + + + Notification Duration + Varaktighet för avisering + + + Portable User Folder + Portabel användarmapp + + + Create Portable User Folder from Common User Folder + Skapa portabel användarmapp från gemensam användarmapp + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portabel användarmapp:\nLagrar shadPS4-inställningar och data som endast tillämpas på den shadPS4-version som finns i den aktuella mappen. Starta om appen efter att du har skapat den portabla användarmappen för att börja använda den. + + + Cannot create portable user folder + Kan inte skapa portabel användarmapp + + + %1 already exists + %1 finns redan + + + Portable user folder created + Portabel användarmapp skapad + + + %1 successfully created. + %1 skapades. + + + 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. + Ö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. @@ -1810,5 +2199,25 @@ Trophy Viewer Trofé-visare + + Select Game: + Välj spel: + + + Progress + Förlopp + + + Show Earned Trophies + Visa förtjänade troféer + + + Show Not Earned Trophies + Visa icke-förtjänade troféer + + + Show Hidden Trophies + Visa dolda troféer + diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index ac7dee9a4..48ce4254d 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -26,11 +26,11 @@ Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n - Cheats/Patches deneysel niteliktedir.\nDikkatli kullanın.\n\nCheat'leri ayrı ayrı indirerek, depo seçerek ve indirme düğmesine tıklayarak indirin.\nPatches sekmesinde tüm patch'leri bir kerede indirebilir, hangi patch'leri kullanmak istediğinizi seçebilir ve seçiminizi kaydedebilirsiniz.\n\nCheats/Patches'i geliştirmediğimiz için,\nproblemleri cheat yazarına bildirin.\n\nYeni bir cheat mi oluşturduğunuz? Şu adresi ziyaret edin:\n + Hileler/Yamalar deneysel özelliklerdir.\nDikkatli kullanın.\n\nHileleri depo seçerek ve indirme düğmesine tıklayarak ayrı ayrı indirin.\nYamalar sekmesinde tüm yamaları tek seferde indirebilir, hangi yamaları kullanmak istediğinizi seçebilir ve seçiminizi kaydedebilirsiniz.\n\nHileleri ve yamaları biz geliştirmediğimiz için\nsorunlarınızı hile geliştiricisine bildirin.\n\nYeni bir hile oluşturduysanız şu adresi ziyaret edin:\n No Image Available - Görüntü Mevcut Değil + Kaynak Mevcut Değil Serial: @@ -70,7 +70,7 @@ Do you want to delete the selected file?\n%1 - Seçilen dosyayı silmek istiyor musunuz?\n%1 + Seçili dosyayı silmek istiyor musunuz?\n%1 Select Patch File: @@ -122,7 +122,7 @@ Success - Başarı + Başarılı Options saved successfully. @@ -138,11 +138,11 @@ File Exists - Dosya Var + Dosya mevcut File already exists. Do you want to replace it? - Dosya zaten var. Üzerine yazmak ister misiniz? + Dosya zaten mevcut. Var olan dosyayı değiştirmek istiyor musunuz? Failed to save file: @@ -431,7 +431,7 @@ Left Stick Deadzone (def:2 max:127) - Sol Analog Ölü Bölgesi (şu an:2, en çok:127) + Sol Analog Ölü Bölgesi (varsayılan: 2, en çok: 127) Left Deadzone @@ -447,11 +447,11 @@ Common Config - Genel Yapılandırma + Ortak Yapılandırma Use per-game configs - Oyuna özel yapılandırmaları kullan + Oyuna özel yapılandırma kullan L1 / LB @@ -507,7 +507,7 @@ Right Stick Deadzone (def:2, max:127) - Sağ Analog Ölü Bölgesi (şu an:2, en çok:127) + Sağ Analog Ölü Bölgesi (varsayılan: 2, en çok: 127) Right Deadzone @@ -541,6 +541,77 @@ Override Color Rengi Geçersiz Kıl + + Unable to Save + Kaydedilemedi + + + Cannot bind axis values more than once + Eksen değerleri birden fazla kez bağlanamaz + + + Save + Kaydet + + + Apply + Uygula + + + Restore Defaults + Varsayılanlara Sıfırla + + + Cancel + İptal + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Klavye + Fare ve Kontrolcü tuş atamalarını düzenle + + + Use Per-Game configs + Oyuna özel yapılandırma kullan + + + Error + Hata + + + Could not open the file for reading + Dosya okumak için açılamadı + + + Could not open the file for writing + Dosya yazmak için açılamadı + + + Save Changes + Değişiklikleri Kaydet + + + Do you want to save changes? + Değişiklikleri kaydetmek istiyor musunuz? + + + Help + Yardım + + + Do you want to reset your custom default config to the original default config? + Özel varsayılan yapılandırmanızı, orijinal varsayılan yapılandırmaya sıfırlamak istiyor musunuz? + + + Do you want to reset this config to your custom default config? + Bu yapılandırmayı özel varsayılan yapılandırmanıza sıfırlamak istiyor musunuz? + + + Reset to Default + Varsayılanlara Sıfırla + ElfViewer @@ -584,7 +655,7 @@ Directory to install DLC - İndirilebilir içeriğin yükleneceği dizin + DLC'lerin yükleneceği dizin @@ -773,7 +844,11 @@ Delete DLC - İndirilebilir İçeriği Sil + DLC'yi Sil + + + Delete Trophy + Kupayı Sil Compatibility... @@ -825,11 +900,11 @@ This game has no DLC to delete! - Bu oyunun silinecek indirilebilir içeriği yok! + Bu oyunun silinecek DLC'si yok! DLC - İndirilebilir İçerik + DLC Delete %1 @@ -851,6 +926,10 @@ This game has no update folder to open! Bu oyunun açılacak güncelleme klasörü yok! + + No log file found for this game! + Bu oyun için günlük dosyası bulunamadı! + Failed to convert icon. Simge dönüştürülemedi. @@ -859,15 +938,46 @@ This game has no save data to delete! Bu oyunun silinecek kayıt verisi yok! + + This game has no saved trophies to delete! + Bu oyunun silinecek kupası yok! + Save Data Kayıt Verisi + + Trophy + Kupa + SFO Viewer for SFO Görüntüleyici: + + HelpDialog + + Quickstart + Hızlı Başlangıç + + + FAQ + SSS + + + Syntax + Sözdizimi + + + Special Bindings + Özel Atamalar + + + Keybindings + Tuş Atamaları + + InstallDirSelect @@ -887,11 +997,222 @@ Yüklemede PKG Dosyasını Sil + + KBMSettings + + Configure Controls + Kontrolleri Yapılandır + + + D-Pad + Yön Düğmeleri + + + Up + Yukarı + + + unmapped + atanmamış + + + Left + Sol + + + Right + Sağ + + + Down + Aşağı + + + Left Analog Halfmode + Sol Analog Yarı Modu + + + hold to move left stick at half-speed + sol analogu yarı hızda hareket ettirmek için basılı tutun + + + Left Stick + Sol Analog + + + Config Selection + Yapılandırma Seçimi + + + Common Config + Ortak Yapılandırma + + + Use per-game configs + Oyuna özel yapılandırma kullan + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Metin Düzenleyici + + + Help + Yardım + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Dokunmatik Yüzey Tıklaması + + + Mouse to Joystick + Mouse'dan Kontrolcü + + + *press F7 ingame to activate + *Etkinleştirmek için oyundayken F7'ye basın + + + R3 + R3 + + + Options + Seçenekler + + + Mouse Movement Parameters + Mouse Hızı Değişkenleri + + + note: click Help Button/Special Keybindings for more information + Not: Daha fazla bilgi için Yardım ya da Özel Atamalar'a tıklayın + + + Face Buttons + Eylem Düğmeleri + + + Triangle + Üçgen + + + Square + Kare + + + Circle + Daire + + + Cross + Çarpı + + + Right Analog Halfmode + Sağ Analog Yarı Modu + + + hold to move right stick at half-speed + sağ analogu yarı hızda hareket ettirmek için basılı tutun + + + Right Stick + Sağ Analog + + + Speed Offset (def 0.125): + Hız Sapması (varsayılan 0.125): + + + Copy from Common Config + Ortak Yapılandırmadan Kopyala + + + Deadzone Offset (def 0.50): + Ölü Bölge Sapması (varsayılan 0.50): + + + Speed Multiplier (def 1.0): + Hız Çarpanı (varsayılan 1.0): + + + Common Config Selected + Ortak Yapılandırma Seçildi + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Bu tuş, Ortak Yapılandırma'daki atamaları seçili profile kopyalar ve seçili profil Ortak Yapılandırma ise kullanılamaz. + + + Copy values from Common Config + Ortak Yapılandırmadan Değerleri Kopyala + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Mevcut atamaların üzerine ortak yapılandırmadaki atamaları yazmak istiyor musunuz? + + + Unable to Save + Kaydedilemedi + + + Cannot bind any unique input more than once + Herhangi bir benzersiz girdi birden fazla kez bağlanamaz + + + Press a key + Bir tuşa basın + + + Cannot set mapping + Atama ayarlanamıyor + + + Mousewheel cannot be mapped to stick outputs + Mouse tekerleği analog çıkışlarına atanamaz + + + Save + Kaydet + + + Apply + Uygula + + + Restore Defaults + Varsayılanlara Sıfırla + + + Cancel + İptal + + MainWindow Open/Add Elf Folder - Elf Klasörünü Aç/Ekle + Elf Klasörü Aç/Ekle Install Packages (PKG) @@ -947,11 +1268,11 @@ Tiny - Küçük + Minik Small - Ufak + Küçük Medium @@ -985,6 +1306,14 @@ Dump Game List Oyun Listesini Kaydet + + Trophy Viewer + Kupa Görüntüleyici + + + No games found. Please add your games to your library first. + Oyun bulunamadı. Oyunlarınızı lütfen önce kütüphanenize ekleyin. + PKG Viewer PKG Görüntüleyici @@ -1175,7 +1504,7 @@ PKG is a patch or DLC, please install the game first! - PKG bir yama ya da indirilebilir içerik, lütfen önce oyunu yükleyin! + PKG bir yama ya da DLC, lütfen önce oyunu yükleyin! Game is already running! @@ -1311,6 +1640,10 @@ Trophy Kupa + + Open the custom trophy images/sounds folder + Özel kupa görüntüleri/sesleri klasörünü aç + Logger Kayıt Tutucu @@ -1385,11 +1718,11 @@ Enable NULL GPU - NULL GPU'yu Etkinleştir + NULL GPU'yu Etkinleştir Enable HDR - HDR'yi Etkinleştir + HDR Paths @@ -1476,8 +1809,8 @@ Oyun Müziği - Disable Trophy Pop-ups - Kupa Açılır Pencerelerini Devre Dışı Bırak + Disable Trophy Notification + Kupa Bildirimini Devre Dışı Bırak Background Image @@ -1533,7 +1866,7 @@ Point your mouse at an option to display its description. - Seçenek üzerinde farenizi tutarak açıklamasını görüntüleyin. + Açıklamasını görüntülemek için mouse'unuzu bir seçeneğin üzerine getirin. Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. @@ -1593,7 +1926,7 @@ Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. - Hareket etmeden sonra imlecin kaybolacağı süreyi ayarlayın. + İmleç İçin Hareketsizlik Zaman Aşımı:\nBoşta kalan imlecin kendini kaç saniye sonra gizleyeceğidir. Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. @@ -1661,7 +1994,7 @@ Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + HDR'yi Etkinleştir:\nDestekleyen oyunlarda HDR'yi etkinleştirir.\nMonitörünüz, BT2020 PQ renk alanını ve RGB10A2 takas zinciri biçimini desteklemelidir. Game Folders:\nThe list of folders to check for installed games. @@ -1697,7 +2030,7 @@ Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + Çökme Tanılamaları:\nÇökme anındaki Vulkan durumu hakkında bilgi içeren bir .yaml dosyası oluşturur.\n'Cihaz kayıp' hatalarını ayıklamak için kullanışlıdır. Bunu etkinleştirdiyseniz, Ana Bilgisayar ve Konuk Hata Ayıklama İşaretleyicileri'ni etkinleştirmelisiniz.\nIntel GPU'lar üzerinde çalışmaz.\nÇalışabilmesi için Vulkan Doğrulama Katmanları'nın etkinleştirilmesine ve Vulkan SDK'sine ihtiyacınız vardır. Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. @@ -1705,7 +2038,7 @@ Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Ana Bilgisayar Hata Ayıklama Göstergeleri:\nVulkan komutlarının etrafına belirli AMDGPU komutları için göstergeler gibi emülatör tarafı bilgileri ekler ve kaynaklara hata ayıklama adları verir.\nBunu etkinleştirdiyseniz, Çökme Tanılamaları'nı etkinleştirmelisiniz.\nRenderDoc gibi programlar için faydalıdır. Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. @@ -1765,15 +2098,15 @@ Video - Video + Görüntü Display Mode - Display Mode + Görüntü Modu Windowed - Windowed + Pencereli Fullscreen @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Ayrı Günlük Dosyaları:\nHer oyun için ayrı bir günlük dosyası yazar. + + Trophy Notification Position + Kupa Bildirim Konumu + + + Left + Sol + + + Right + Sağ + + + Top + Üst + + + Bottom + Alt + + + Notification Duration + Bildirim Süresi + + + Portable User Folder + Taşınabilir Kullanıcı Klasörü + + + Create Portable User Folder from Common User Folder + Ortak Kullanıcı Klasöründen Taşınabilir Kullanıcı Klasörü Oluştur + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Taşınabilir kullanıcı klasörü:\nYalnızca geçerli klasörde bulunan shadPS4 derlemesine uygulanacak shadPS4 ayarlarını ve verilerini depolar. Kullanmaya başlamak için taşınabilir kullanıcı klasörünü oluşturduktan sonra uygulamayı yeniden başlatın. + + + Cannot create portable user folder + Taşınabilir kullanıcı klasörü oluşturulamıyor + + + %1 already exists + %1 zaten mevcut + + + Portable user folder created + Taşınabilir kullanıcı klasörü oluşturuldu + + + %1 successfully created. + %1 başarıyla oluşturuldu. + + + 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. + Ö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. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Kupa Görüntüleyici + + Select Game: + Oyun Seç: + + + Progress + İlerleme + + + Show Earned Trophies + Kazanılmış Kupaları Göster + + + Show Not Earned Trophies + Kazanılmamış Kupaları Göster + + + Show Hidden Trophies + Gizli Kupaları Göster + diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 0a3865d49..06c88428b 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -407,139 +407,210 @@ ControlSettings Configure Controls - Configure Controls + Налаштування елементів керування D-Pad - D-Pad + Хрестовина Up - Up + Вгору Left - Left + Ліворуч Right - Right + Праворуч Down - Down + Вниз Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + Мертва зона лівого стику (за замов.: 2, максимум: 127) Left Deadzone - Left Deadzone + Ліва мертва зона Left Stick - Left Stick + Лівий стік Config Selection - Config Selection + Вибір конфігурації Common Config - Common Config + Загальна конфігурація Use per-game configs - Use per-game configs + Використовувати ігрові конфігурації L1 / LB - L1 / LB + L1 / Лівий Бампер L2 / LT - L2 / LT + L2 / Лівий Тригер Back - Back + Назад R1 / RB - R1 / RB + R1 / Правий Бампер R2 / RT - R2 / RT + R2 / Правий Тригер L3 - L3 + Кнопка лівого стику Options / Start - Options / Start + Опції / Старт R3 - R3 + Кнопка правого стику Face Buttons - Face Buttons + Лицьові кнопки Triangle / Y - Triangle / Y + Трикутник / Y Square / X - Square / X + Квадрат / X Circle / B - Circle / B + Коло / B Cross / A - Cross / A + Хрест / A Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + Мертва зона правого стику (за замов.: 2, максимум: 127) Right Deadzone - Right Deadzone + Права мертва зона Right Stick - Right Stick + Правий стик Color Adjustment - Color Adjustment + Налаштування Кольору R: - R: + R: G: - G: + G: B: - B: + B: Override Lightbar Color - Override Lightbar Color + Змінити колір підсвітки Override Color - Override Color + Змінити колір + + + Unable to Save + Не вдалося зберегти + + + Cannot bind axis values more than once + Неможливо пере назначити кнопку більше одного разу + + + Save + Зберегти + + + Apply + Застосувати + + + Restore Defaults + За замовчуванням + + + Cancel + Відмінити + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Налаштування клавіатури, миші та перевизначення кнопок контролеру + + + Use Per-Game configs + Використовувати ігрові конфігурації + + + Error + Помилка + + + Could not open the file for reading + Не вдалося відкрити файл для читання + + + Could not open the file for writing + Не вдалось відкрити файл для запису + + + Save Changes + Зберегти зміни + + + Do you want to save changes? + Бажаєте зберегти зміни? + + + Help + Допомога + + + Do you want to reset your custom default config to the original default config? + Ви бажаєте скинути ваші налаштування за замовчуванням до початкової конфігурації? + + + Do you want to reset this config to your custom default config? + Ви бажаєте скинути ці налаштування до вашої конфігурації за замовчуванням? + + + Reset to Default + Відновити налаштування за замовчанням @@ -584,7 +655,7 @@ Directory to install DLC - Directory to install DLC + Папка для встановлення DLC @@ -775,6 +846,10 @@ Delete DLC Видалити DLC + + Delete Trophy + Видалити трофей + Compatibility... Сумісність... @@ -829,7 +904,7 @@ DLC - DLC + DLC Delete %1 @@ -851,21 +926,56 @@ This game has no update folder to open! Ця гра не має папки оновленнь, щоб відкрити її! + + No log file found for this game! + Файл звіту для цієї гри не знайдено! + Failed to convert icon. - Failed to convert icon. + Не вдалося конвертувати значок. This game has no save data to delete! Ця гра не містить збережень, які можна видалити! + + This game has no saved trophies to delete! + Ця гра немає збережених трофеїв для видалення! + Save Data Збереження + + Trophy + Трофеї + SFO Viewer for - SFO Viewer for + Перегляд SFO + + + + HelpDialog + + Quickstart + Швидкий старт + + + FAQ + ЧаПи + + + Syntax + Синтаксис + + + Special Bindings + Спеціальні прив'язки + + + Keybindings + Призначення клавіш @@ -887,6 +997,217 @@ Видалити файл PKG під час встановлення + + KBMSettings + + Configure Controls + Налаштування елементів керування + + + D-Pad + Хрестовина + + + Up + Вгору + + + unmapped + не назначено + + + Left + Ліворуч + + + Right + Праворуч + + + Down + Вниз + + + Left Analog Halfmode + Напіврежим лівого аналогового стику + + + hold to move left stick at half-speed + утримуйте щоб рухати лівий стик в половину швидкості + + + Left Stick + Лівий стик + + + Config Selection + Вибір конфігурації + + + Common Config + Загальна конфігурація + + + Use per-game configs + Використовувати ігрові конфігурації + + + L1 + L1 / Лівий Бампер + + + L2 + L2 / Лівий Тригер + + + Text Editor + Текстовий редактор + + + Help + Довідка + + + R1 + R1 / Правий Бампер + + + R2 + R2 / Правий Тригер + + + L3 + Кнопка лівого стику + + + Touchpad Click + Натискання на сенсорну панель + + + Mouse to Joystick + Миша в джойстик + + + *press F7 ingame to activate + *натисніть F7 у грі для увімкнення + + + R3 + Кнопка правого стику + + + Options + Опції + + + Mouse Movement Parameters + Параметри руху миші + + + note: click Help Button/Special Keybindings for more information + замітка: натисніть кнопку Довідки/Спеціального Призначення клавіш для отримання додаткової інформації + + + Face Buttons + Лицьові кнопки + + + Triangle + Трикутник + + + Square + Квадрат + + + Circle + Коло + + + Cross + Хрест + + + Right Analog Halfmode + Напіврежим правого аналогового стику + + + hold to move right stick at half-speed + утримуйте щоб рухати правий стик в половину швидкості + + + Right Stick + Правий стик + + + Speed Offset (def 0.125): + Зміщення швидкості (замовч 0,125): + + + Copy from Common Config + Копіювати з Загальної конфігурації + + + Deadzone Offset (def 0.50): + Зміщення мертвої зони (замовч 0,50): + + + Speed Multiplier (def 1.0): + Модифікатор швидкості (замовч 1,0): + + + Common Config Selected + Вибрані Загальні налаштування + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Ця кнопка копіює перепризначення кнопок із Загальної конфігурації до поточного вибраного профілю, і не може бути використана, якщо поточний вибраний профіль є загальною конфігурацією. + + + Copy values from Common Config + Копіювати значення з Загальної конфігурації + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Ви бажаєте перезаписати наявні перепризначення кнопок з загальної конфігурації? + + + Unable to Save + Не вдалося зберегти + + + Cannot bind any unique input more than once + Не можна прив'язати кнопку вводу більш ніж один раз + + + Press a key + Натисніть клавішу + + + Cannot set mapping + Не вдалося встановити прив'язку + + + Mousewheel cannot be mapped to stick outputs + Коліщатко миші не можна прив'язати зі значенням стиків + + + Save + Зберегти + + + Apply + Застосувати + + + Restore Defaults + За замовчуванням + + + Cancel + Відмінити + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Дамп списку ігор + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer Перегляд PKG @@ -1163,27 +1492,27 @@ Run Game - Run Game + Запустити гру Eboot.bin file not found - Eboot.bin file not found + Файл Boot.bin не знайдено PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) + Файл PKG (*.PKG *.pkg) PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! + PKG - це патч або DLC, будь ласка, спочатку встановіть гру! Game is already running! - Game is already running! + Гра вже запущена! shadPS4 - shadPS4 + shadPS4 @@ -1206,7 +1535,7 @@ Installed - Installed + Встановлені Size @@ -1214,19 +1543,19 @@ Category - Category + Категорія Type - Type + Тип App Ver - App Ver + Версія додатку FW - FW + ПЗ Region @@ -1234,7 +1563,7 @@ Flags - Flags + Мітки Path @@ -1250,7 +1579,7 @@ Package - Package + Пакет @@ -1311,6 +1640,10 @@ Trophy Трофеї + + Open the custom trophy images/sounds folder + Відкрити папку користувацьких зображень трофеїв/звуків + Logger Логування @@ -1389,7 +1722,7 @@ Enable HDR - Enable HDR + Увімкнути HDR Paths @@ -1476,8 +1809,8 @@ Титульна музика - Disable Trophy Pop-ups - Вимкнути спливаючі вікна трофеїв + Disable Trophy Notification + Вимкнути сповіщення про отримання трофею Background Image @@ -1661,7 +1994,7 @@ Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + Увімкнути HDR:\nУвімкнути HDR в іграх, які його підтримують.\nВаш монітор повинен мати колірний простір BT2020 PQ та формат swapchain RGB10A2. Game Folders:\nThe list of folders to check for installed games. @@ -1729,7 +2062,7 @@ Set the volume of the background music. - Set the volume of the background music. + Налаштування гучності фонової музики. Enable Motion Controls @@ -1761,47 +2094,103 @@ Directory to save data - Directory to save data + Папка для збереження даних Video - Video + Відео Display Mode - Display Mode + Режим відображення Windowed - Windowed + У вікні Fullscreen - Fullscreen + Повноекранний Fullscreen (Borderless) - Fullscreen (Borderless) + Повний екран (без рамок) Window Size - Window Size + Розмір вікна W: - W: + Висота: H: - H: + Ширина: Separate Log Files - Separate Log Files + Окремі файли журналу Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Окремі файли журналу:\nЗаписує окремий файл журналу для кожної гри. + + + Trophy Notification Position + Розміщення сповіщень про трофеї + + + Left + Ліворуч + + + Right + Праворуч + + + Top + Вгорі + + + Bottom + Внизу + + + Notification Duration + Тривалість сповіщення + + + Portable User Folder + Портативна папка користувача + + + Create Portable User Folder from Common User Folder + Створити портативну папку користувача з загальної папки + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Портативна папка користувача:\nЗберігає налаштування та дані shadPS4, які будуть застосовані лише до збірки shadPS4, розташованої у поточній теці. Перезапустіть програму після створення портативної теки користувача, щоб почати користуватися нею. + + + Cannot create portable user folder + Не вдалося створити портативну папку користувача + + + %1 already exists + %1 вже існує + + + Portable user folder created + Портативна папка користувача створена + + + %1 successfully created. + %1 успішно створено. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Відкрити папку користувацьких зображень трофеїв/звуків:\nВи можете додати користувацькі зображення до трофеїв та звук.\nДодайте файли до теки custom_trophy з такими назвами:\ntrophy.wav АБО trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nПримітка: Звук буде працювати лише у версіях ShadPS4 з графічним інтерфейсом. @@ -1810,5 +2199,25 @@ Trophy Viewer Трофеї + + Select Game: + Select Game: + + + Progress + Прогрес + + + Show Earned Trophies + Показати отримані трофеї + + + Show Not Earned Trophies + Показати не отримані трофеї + + + Show Hidden Trophies + Показати приховані трофеї + diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 50a34ad7a..c16604b85 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + Unable to Save + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + Error + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + Save Changes + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trình xem chiến tích + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index b855f2dcd..6364ae1d6 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -411,7 +411,7 @@ D-Pad - D-Pad + 十字键 Up @@ -487,23 +487,23 @@ Face Buttons - 正面按钮 + 功能键(动作键) Triangle / Y - Triangle / Y + 三角 / Y Square / X - Square / X + 方框 / X Circle / B - Circle / B + 圈 / B Cross / A - Cross / A + 叉 / A Right Stick Deadzone (def:2, max:127) @@ -541,6 +541,77 @@ Override Color 覆盖颜色 + + Unable to Save + 无法保存 + + + Cannot bind axis values more than once + 摇杆 X/Y 轴的操作绑定不在同一直线 + + + Save + 保存 + + + Apply + 应用 + + + Restore Defaults + 恢复默认设置 + + + Cancel + 取消 + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + 编辑键盘鼠标和手柄按键绑定 + + + Use Per-Game configs + 每个游戏使用单独的配置 + + + Error + 错误 + + + Could not open the file for reading + 无法打开文件进行读取 + + + Could not open the file for writing + 无法打开文件进行读取 + + + Save Changes + 保存更改 + + + Do you want to save changes? + 您要保存更改吗? + + + Help + 帮助 + + + Do you want to reset your custom default config to the original default config? + 您要将自定义默认配置重置为默认配置吗? + + + Do you want to reset this config to your custom default config? + 您想要将此配置重置为自定义默认配置吗? + + + Reset to Default + 重置为默认 + ElfViewer @@ -775,6 +846,10 @@ Delete DLC 删除 DLC + + Delete Trophy + 删除奖杯 + Compatibility... 兼容性... @@ -851,6 +926,10 @@ This game has no update folder to open! 这个游戏没有可打开的更新文件夹! + + No log file found for this game! + 没有找到这个游戏的日志文件! + Failed to convert icon. 转换图标失败。 @@ -859,15 +938,46 @@ This game has no save data to delete! 这个游戏没有更新可以删除! + + This game has no saved trophies to delete! + 这个游戏没有保存的奖杯可删除! + Save Data 存档数据 + + Trophy + 奖杯 + SFO Viewer for SFO 查看器 - + + HelpDialog + + Quickstart + 快速入门 + + + FAQ + 常见问题 + + + Syntax + 语法 + + + Special Bindings + 特殊绑定 + + + Keybindings + 按键绑定 + + InstallDirSelect @@ -887,6 +997,217 @@ 安装后删除 PKG 文件 + + KBMSettings + + Configure Controls + 配置键鼠 + + + D-Pad + 十字键 + + + Up + + + + unmapped + 未映射 + + + Left + + + + Right + + + + Down + + + + Left Analog Halfmode + 左摇杆半速模式 + + + hold to move left stick at half-speed + 按住以半速移动左摇杆 + + + Left Stick + 左摇杆 + + + Config Selection + 配置选择 + + + Common Config + 通用配置 + + + Use per-game configs + 每个游戏使用单独的配置 + + + L1 + L1 + + + L2 + L2 + + + Text Editor + 文本编辑器 + + + Help + 帮助 + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + 触摸板点击 + + + Mouse to Joystick + 鼠标控制摇杆 + + + *press F7 ingame to activate + * 按 F7 键激活 + + + R3 + R3 + + + Options + 选项设置 + + + Mouse Movement Parameters + 鼠标移动参数 + + + note: click Help Button/Special Keybindings for more information + 注意:点击帮助按钮 -> 获取更多关于映射特殊键位的信息 + + + Face Buttons + 功能键(动作键) + + + Triangle + 三角 + + + Square + 方框 + + + Circle + + + + Cross + + + + Right Analog Halfmode + 右摇杆半速模式 + + + hold to move right stick at half-speed + 按住以半速移动右摇杆 + + + Right Stick + 右摇杆 + + + Speed Offset (def 0.125): + 速度偏移量(默认 0.125): + + + Copy from Common Config + 从通用配置中复制 + + + Deadzone Offset (def 0.50): + 死区偏移量(默认 0.50): + + + Speed Multiplier (def 1.0): + 速度系数(默认 1.0): + + + Common Config Selected + 已选中通用配置 + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + 此按钮用于将通用配置中的映射复制到当前选定的配置文件,当前选定的配置文件为通用配置时无法使用。 + + + Copy values from Common Config + 从通用配置中复制配置 + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + 您想要用通用配置的映射覆盖现有映射吗? + + + Unable to Save + 无法保存 + + + Cannot bind any unique input more than once + 不能绑定重复的按键 + + + Press a key + 按下按键 + + + Cannot set mapping + 无法设置映射 + + + Mousewheel cannot be mapped to stick outputs + 鼠标滚轮无法映射到摇杆 + + + Save + 保存 + + + Apply + 应用 + + + Restore Defaults + 恢复默认设置 + + + Cancel + 取消 + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List 导出游戏列表 + + Trophy Viewer + 奖杯查看器 + + + No games found. Please add your games to your library first. + 未找到游戏。请先将您的游戏添加到您的资料库。 + PKG Viewer PKG 查看器 @@ -1311,6 +1640,10 @@ Trophy 奖杯 + + Open the custom trophy images/sounds folder + 打开自定义奖杯图像/声音文件夹 + Logger 日志 @@ -1476,8 +1809,8 @@ 标题音乐 - Disable Trophy Pop-ups - 禁止弹出奖杯 + Disable Trophy Notification + 禁用奖杯通知 Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. 独立日志文件:\n每个游戏使用单独的日志文件。 + + Trophy Notification Position + 奖杯通知位置 + + + Left + 左边 + + + Right + 右边 + + + Top + 顶部 + + + Bottom + 底部 + + + Notification Duration + 通知显示持续时间 + + + Portable User Folder + 本地用户文件夹 + + + Create Portable User Folder from Common User Folder + 从公共用户文件夹创建本地用户文件夹 + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + 本地用户文件夹:\n存储 shadPS4 设置和数据,这些设置和数据仅应用于当前运行的 shadPS4。创建本地用户文件夹后,重启应用即可开始使用。 + + + Cannot create portable user folder + 无法创建本地用户文件夹 + + + %1 already exists + %1 已存在 + + + Portable user folder created + 本地用户文件夹已创建 + + + %1 successfully created. + %1 创建成功。 + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + 打开自定义奖杯图像/声音文件夹:\n您可以自定义奖杯图像和声音。\n将文件添加到 custom_trophy 文件夹中,文件名如下:\ntrophy.wav 或 trophy.mp3、bronze.png、gold.png、platinum.png、silver.png。\n注意:自定义声音只能在 QT 版本中生效。 + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer 奖杯查看器 + + Select Game: + 选择游戏: + + + Progress + 进度 + + + Show Earned Trophies + 显示获得的奖杯 + + + Show Not Earned Trophies + 显示未获得的奖杯 + + + Show Hidden Trophies + 显示隐藏奖杯 + diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 15c921d15..fb42a43b0 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -407,7 +407,7 @@ ControlSettings Configure Controls - Configure Controls + 操控設定 D-Pad @@ -519,7 +519,7 @@ Color Adjustment - Color Adjustment + 色彩調整 R: @@ -541,6 +541,77 @@ Override Color Override Color + + Unable to Save + 無法保存 + + + Cannot bind axis values more than once + Cannot bind axis values more than once + + + Save + 保存 + + + Apply + 套用 + + + Restore Defaults + Restore Defaults + + + Cancel + 取消 + + + + EditorDialog + + Edit Keyboard + Mouse and Controller input bindings + Edit Keyboard + Mouse and Controller input bindings + + + Use Per-Game configs + Use Per-Game configs + + + Error + 錯誤 + + + Could not open the file for reading + Could not open the file for reading + + + Could not open the file for writing + Could not open the file for writing + + + Save Changes + 儲存變更 + + + Do you want to save changes? + Do you want to save changes? + + + Help + Help + + + Do you want to reset your custom default config to the original default config? + Do you want to reset your custom default config to the original default config? + + + Do you want to reset this config to your custom default config? + Do you want to reset this config to your custom default config? + + + Reset to Default + Reset to Default + ElfViewer @@ -775,6 +846,10 @@ Delete DLC Delete DLC + + Delete Trophy + Delete Trophy + Compatibility... Compatibility... @@ -851,6 +926,10 @@ This game has no update folder to open! This game has no update folder to open! + + No log file found for this game! + No log file found for this game! + Failed to convert icon. Failed to convert icon. @@ -859,15 +938,46 @@ This game has no save data to delete! This game has no save data to delete! + + This game has no saved trophies to delete! + This game has no saved trophies to delete! + Save Data Save Data + + Trophy + Trophy + SFO Viewer for SFO Viewer for + + HelpDialog + + Quickstart + Quickstart + + + FAQ + FAQ + + + Syntax + Syntax + + + Special Bindings + Special Bindings + + + Keybindings + Keybindings + + InstallDirSelect @@ -887,6 +997,217 @@ Delete PKG File on Install + + KBMSettings + + Configure Controls + Configure Controls + + + D-Pad + D-Pad + + + Up + Up + + + unmapped + unmapped + + + Left + Left + + + Right + Right + + + Down + Down + + + Left Analog Halfmode + Left Analog Halfmode + + + hold to move left stick at half-speed + hold to move left stick at half-speed + + + Left Stick + Left Stick + + + Config Selection + Config Selection + + + Common Config + Common Config + + + Use per-game configs + Use per-game configs + + + L1 + L1 + + + L2 + L2 + + + Text Editor + Text Editor + + + Help + Help + + + R1 + R1 + + + R2 + R2 + + + L3 + L3 + + + Touchpad Click + Touchpad Click + + + Mouse to Joystick + Mouse to Joystick + + + *press F7 ingame to activate + *press F7 ingame to activate + + + R3 + R3 + + + Options + Options + + + Mouse Movement Parameters + Mouse Movement Parameters + + + note: click Help Button/Special Keybindings for more information + note: click Help Button/Special Keybindings for more information + + + Face Buttons + Face Buttons + + + Triangle + Triangle + + + Square + Square + + + Circle + Circle + + + Cross + Cross + + + Right Analog Halfmode + Right Analog Halfmode + + + hold to move right stick at half-speed + hold to move right stick at half-speed + + + Right Stick + Right Stick + + + Speed Offset (def 0.125): + Speed Offset (def 0.125): + + + Copy from Common Config + Copy from Common Config + + + Deadzone Offset (def 0.50): + Deadzone Offset (def 0.50): + + + Speed Multiplier (def 1.0): + Speed Multiplier (def 1.0): + + + Common Config Selected + Common Config Selected + + + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + + + Copy values from Common Config + Copy values from Common Config + + + Do you want to overwrite existing mappings with the mappings from the Common Config? + Do you want to overwrite existing mappings with the mappings from the Common Config? + + + Unable to Save + Unable to Save + + + Cannot bind any unique input more than once + Cannot bind any unique input more than once + + + Press a key + Press a key + + + Cannot set mapping + Cannot set mapping + + + Mousewheel cannot be mapped to stick outputs + Mousewheel cannot be mapped to stick outputs + + + Save + Save + + + Apply + Apply + + + Restore Defaults + Restore Defaults + + + Cancel + Cancel + + MainWindow @@ -985,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -1311,6 +1640,10 @@ Trophy Trophy + + Open the custom trophy images/sounds folder + Open the custom trophy images/sounds folder + Logger Logger @@ -1476,8 +1809,8 @@ Title Music - Disable Trophy Pop-ups - Disable Trophy Pop-ups + Disable Trophy Notification + Disable Trophy Notification Background Image @@ -1803,6 +2136,62 @@ Separate Log Files:\nWrites a separate logfile for each game. Separate Log Files:\nWrites a separate logfile for each game. + + Trophy Notification Position + Trophy Notification Position + + + Left + Left + + + Right + Right + + + Top + Top + + + Bottom + Bottom + + + Notification Duration + Notification Duration + + + Portable User Folder + Portable User Folder + + + Create Portable User Folder from Common User Folder + Create Portable User Folder from Common User Folder + + + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + + + Cannot create portable user folder + Cannot create portable user folder + + + %1 already exists + %1 already exists + + + Portable user folder created + Portable user folder created + + + %1 successfully created. + %1 successfully created. + + + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + TrophyViewer @@ -1810,5 +2199,25 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + + + Progress + Progress + + + Show Earned Trophies + Show Earned Trophies + + + Show Not Earned Trophies + Show Not Earned Trophies + + + Show Hidden Trophies + Show Hidden Trophies + diff --git a/src/qt_gui/trophy_viewer.cpp b/src/qt_gui/trophy_viewer.cpp index 63e9f04dd..bed487605 100644 --- a/src/qt_gui/trophy_viewer.cpp +++ b/src/qt_gui/trophy_viewer.cpp @@ -1,27 +1,267 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include #include +#include #include +#include #include "common/path_util.h" +#include "main_window_themes.h" #include "trophy_viewer.h" +namespace fs = std::filesystem; + CMRC_DECLARE(res); -TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindow() { - this->setWindowTitle(tr("Trophy Viewer")); +// true: European format; false: American format +bool useEuropeanDateFormat = true; + +void TrophyViewer::updateTrophyInfo() { + int total = 0; + int unlocked = 0; + + // Cycles through each tab (table) of the QTabWidget + for (int i = 0; i < tabWidget->count(); i++) { + QTableWidget* table = qobject_cast(tabWidget->widget(i)); + if (table) { + total += table->rowCount(); + for (int row = 0; row < table->rowCount(); ++row) { + QString cellText; + // The "Unlocked" column can be a widget or a simple item + QWidget* widget = table->cellWidget(row, 0); + if (widget) { + // Looks for the QLabel inside the widget (as defined in SetTableItem) + QLabel* label = widget->findChild(); + if (label) { + cellText = label->text(); + } + } else { + QTableWidgetItem* item = table->item(row, 0); + if (item) { + cellText = item->text(); + } + } + if (cellText == "unlocked") + unlocked++; + } + } + } + int progress = (total > 0) ? (unlocked * 100 / total) : 0; + trophyInfoLabel->setText( + QString(tr("Progress") + ": %1% (%2/%3)").arg(progress).arg(unlocked).arg(total)); +} + +void TrophyViewer::updateTableFilters() { + bool showEarned = showEarnedCheck->isChecked(); + bool showNotEarned = showNotEarnedCheck->isChecked(); + bool showHidden = showHiddenCheck->isChecked(); + + // Cycles through each tab of the QTabWidget + for (int i = 0; i < tabWidget->count(); ++i) { + QTableWidget* table = qobject_cast(tabWidget->widget(i)); + if (!table) + continue; + for (int row = 0; row < table->rowCount(); ++row) { + QString unlockedText; + // Gets the text of the "Unlocked" column (index 0) + QWidget* widget = table->cellWidget(row, 0); + if (widget) { + QLabel* label = widget->findChild(); + if (label) + unlockedText = label->text(); + } else { + QTableWidgetItem* item = table->item(row, 0); + if (item) + unlockedText = item->text(); + } + + QString hiddenText; + // Gets the text of the "Hidden" column (index 7) + QWidget* hiddenWidget = table->cellWidget(row, 7); + if (hiddenWidget) { + QLabel* label = hiddenWidget->findChild(); + if (label) + hiddenText = label->text(); + } else { + QTableWidgetItem* item = table->item(row, 7); + if (item) + hiddenText = item->text(); + } + + bool visible = true; + if (unlockedText == "unlocked" && !showEarned) + visible = false; + if (unlockedText == "locked" && !showNotEarned) + visible = false; + if (hiddenText.toLower() == "yes" && !showHidden) + visible = false; + + table->setRowHidden(row, !visible); + } + } +} + +TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath, QString gameName, + const QVector& allTrophyGames) + : QMainWindow(), allTrophyGames_(allTrophyGames), currentGameName_(gameName) { + this->setWindowTitle(tr("Trophy Viewer") + " - " + currentGameName_); this->setAttribute(Qt::WA_DeleteOnClose); tabWidget = new QTabWidget(this); + + auto lan = Config::getEmulatorLanguage(); + if (lan == "en_US" || lan == "zh_CN" || lan == "zh_TW" || lan == "ja_JP" || lan == "ko_KR" || + lan == "lt_LT" || lan == "nb_NO" || lan == "nl_NL") { + useEuropeanDateFormat = false; + } + gameTrpPath_ = gameTrpPath; headers << "Unlocked" << "Trophy" << "Name" << "Description" + << "Time Unlocked" + << "Type" << "ID" << "Hidden" - << "Type" << "PID"; PopulateTrophyWidget(trophyPath); + + trophyInfoDock = new QDockWidget("", this); + QWidget* dockWidget = new QWidget(trophyInfoDock); + QVBoxLayout* dockLayout = new QVBoxLayout(dockWidget); + dockLayout->setAlignment(Qt::AlignTop); + + // ComboBox for game selection + if (!allTrophyGames_.isEmpty()) { + QLabel* gameSelectionLabel = new QLabel(tr("Select Game:"), dockWidget); + dockLayout->addWidget(gameSelectionLabel); + + gameSelectionComboBox = new QComboBox(dockWidget); + for (const auto& game : allTrophyGames_) { + gameSelectionComboBox->addItem(game.name); + } + + // Select current game in ComboBox + if (!currentGameName_.isEmpty()) { + int index = gameSelectionComboBox->findText(currentGameName_); + if (index >= 0) { + gameSelectionComboBox->setCurrentIndex(index); + } + } + + dockLayout->addWidget(gameSelectionComboBox); + + connect(gameSelectionComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + &TrophyViewer::onGameSelectionChanged); + + QFrame* line = new QFrame(dockWidget); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + dockLayout->addWidget(line); + } + + trophyInfoLabel = new QLabel(tr("Progress") + ": 0% (0/0)", dockWidget); + trophyInfoLabel->setStyleSheet( + "font-weight: bold; font-size: 16px; color: white; background: #333; padding: 5px;"); + dockLayout->addWidget(trophyInfoLabel); + + // Creates QCheckBox to filter trophies + showEarnedCheck = new QCheckBox(tr("Show Earned Trophies"), dockWidget); + showNotEarnedCheck = new QCheckBox(tr("Show Not Earned Trophies"), dockWidget); + showHiddenCheck = new QCheckBox(tr("Show Hidden Trophies"), dockWidget); + + // Defines the initial states (all checked) + showEarnedCheck->setChecked(true); + showNotEarnedCheck->setChecked(true); + showHiddenCheck->setChecked(false); + + // Adds checkboxes to the layout + dockLayout->addWidget(showEarnedCheck); + dockLayout->addWidget(showNotEarnedCheck); + dockLayout->addWidget(showHiddenCheck); + + dockWidget->setLayout(dockLayout); + trophyInfoDock->setWidget(dockWidget); + + // Adds the dock to the left area + this->addDockWidget(Qt::LeftDockWidgetArea, trophyInfoDock); + + expandButton = new QPushButton(">>", this); + expandButton->setGeometry(80, 0, 27, 27); + expandButton->hide(); + + connect(expandButton, &QPushButton::clicked, this, [this] { + trophyInfoDock->setVisible(true); + expandButton->hide(); + }); + + // Connects checkbox signals to update trophy display +#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) + connect(showEarnedCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters); + connect(showNotEarnedCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters); + connect(showHiddenCheck, &QCheckBox::stateChanged, this, &TrophyViewer::updateTableFilters); +#else + connect(showEarnedCheck, &QCheckBox::checkStateChanged, this, + &TrophyViewer::updateTableFilters); + connect(showNotEarnedCheck, &QCheckBox::checkStateChanged, this, + &TrophyViewer::updateTableFilters); + connect(showHiddenCheck, &QCheckBox::checkStateChanged, this, + &TrophyViewer::updateTableFilters); +#endif + + updateTrophyInfo(); + updateTableFilters(); + + connect(trophyInfoDock, &QDockWidget::topLevelChanged, this, [this] { + if (!trophyInfoDock->isVisible()) { + expandButton->show(); + } + }); + + connect(trophyInfoDock, &QDockWidget::visibilityChanged, this, [this] { + if (!trophyInfoDock->isVisible()) { + expandButton->show(); + } else { + expandButton->hide(); + } + }); +} + +void TrophyViewer::onGameSelectionChanged(int index) { + if (index < 0 || index >= allTrophyGames_.size()) { + return; + } + + while (tabWidget->count() > 0) { + QWidget* widget = tabWidget->widget(0); + tabWidget->removeTab(0); + delete widget; + } + + const TrophyGameInfo& selectedGame = allTrophyGames_[index]; + currentGameName_ = selectedGame.name; + gameTrpPath_ = selectedGame.gameTrpPath; + + this->setWindowTitle(tr("Trophy Viewer") + " - " + currentGameName_); + + PopulateTrophyWidget(selectedGame.trophyPath); + + updateTrophyInfo(); + updateTableFilters(); +} + +void TrophyViewer::onDockClosed() { + if (!trophyInfoDock->isVisible()) { + reopenButton->setVisible(true); + } +} + +void TrophyViewer::reopenLeftDock() { + trophyInfoDock->show(); + reopenButton->setVisible(false); } void TrophyViewer::PopulateTrophyWidget(QString title) { @@ -68,6 +308,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { QStringList trpPid; QStringList trophyNames; QStringList trophyDetails; + QStringList trpTimeUnlocked; QString xmlPath = trpDir + "/Xml/TROP.XML"; QFile file(xmlPath); @@ -84,14 +325,35 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { trpHidden.append(reader.attributes().value("hidden").toString()); trpType.append(reader.attributes().value("ttype").toString()); trpPid.append(reader.attributes().value("pid").toString()); + if (reader.attributes().hasAttribute("unlockstate")) { if (reader.attributes().value("unlockstate").toString() == "true") { trpUnlocked.append("unlocked"); } else { trpUnlocked.append("locked"); } + if (reader.attributes().hasAttribute("timestamp")) { + QString ts = reader.attributes().value("timestamp").toString(); + if (ts.length() > 10) + trpTimeUnlocked.append("unknown"); + else { + bool ok; + qint64 timestampInt = ts.toLongLong(&ok); + if (ok) { + QDateTime dt = QDateTime::fromSecsSinceEpoch(timestampInt); + QString format = useEuropeanDateFormat ? "dd/MM/yyyy HH:mm:ss" + : "MM/dd/yyyy HH:mm:ss"; + trpTimeUnlocked.append(dt.toString(format)); + } else { + trpTimeUnlocked.append("unknown"); + } + } + } else { + trpTimeUnlocked.append(""); + } } else { trpUnlocked.append("locked"); + trpTimeUnlocked.append(""); } } @@ -105,7 +367,7 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { } QTableWidget* tableWidget = new QTableWidget(this); tableWidget->setShowGrid(false); - tableWidget->setColumnCount(8); + tableWidget->setColumnCount(9); tableWidget->setHorizontalHeaderLabels(headers); tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); @@ -113,6 +375,8 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { tableWidget->horizontalHeader()->setStretchLastSection(true); tableWidget->verticalHeader()->setVisible(false); tableWidget->setRowCount(icons.size()); + tableWidget->setSortingEnabled(true); + for (int row = 0; auto& icon : icons) { QTableWidgetItem* item = new QTableWidgetItem(); item->setData(Qt::DecorationRole, icon); @@ -122,15 +386,34 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { const std::string filename = GetTrpType(trpType[row].at(0)); QTableWidgetItem* typeitem = new QTableWidgetItem(); - auto resource = cmrc::res::get_filesystem(); - std::string resourceString = "src/images/" + filename; - auto file = resource.open(resourceString); - std::vector imgdata(file.begin(), file.end()); - QImage type_icon = QImage::fromData(imgdata).scaled(QSize(64, 64), Qt::KeepAspectRatio, - Qt::SmoothTransformation); + const auto CustomTrophy_Dir = + Common::FS::GetUserPath(Common::FS::PathType::CustomTrophy); + std::string customPath; + + if (fs::exists(CustomTrophy_Dir / filename)) { + customPath = (CustomTrophy_Dir / filename).string(); + } + + std::vector imgdata; + + if (!customPath.empty()) { + std::ifstream file(customPath, std::ios::binary); + if (file) { + imgdata = std::vector(std::istreambuf_iterator(file), + std::istreambuf_iterator()); + } + } else { + auto resource = cmrc::res::get_filesystem(); + std::string resourceString = "src/images/" + filename; + auto file = resource.open(resourceString); + imgdata = std::vector(file.begin(), file.end()); + } + + QImage type_icon = QImage::fromData(imgdata).scaled( + QSize(100, 100), Qt::KeepAspectRatio, Qt::SmoothTransformation); typeitem->setData(Qt::DecorationRole, type_icon); typeitem->setFlags(typeitem->flags() & ~Qt::ItemIsEditable); - tableWidget->setItem(row, 6, typeitem); + tableWidget->setItem(row, 5, typeitem); std::string detailString = trophyDetails[row].toStdString(); std::size_t newline_pos = 0; @@ -143,46 +426,50 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { SetTableItem(tableWidget, row, 0, trpUnlocked[row]); SetTableItem(tableWidget, row, 2, trophyNames[row]); SetTableItem(tableWidget, row, 3, QString::fromStdString(detailString)); - SetTableItem(tableWidget, row, 4, trpId[row]); - SetTableItem(tableWidget, row, 5, trpHidden[row]); - SetTableItem(tableWidget, row, 7, trpPid[row]); + SetTableItem(tableWidget, row, 4, trpTimeUnlocked[row]); + SetTableItem(tableWidget, row, 6, trpId[row]); + SetTableItem(tableWidget, row, 7, trpHidden[row]); + SetTableItem(tableWidget, row, 8, trpPid[row]); } tableWidget->verticalHeader()->resizeSection(row, icon.height()); row++; } tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); int width = 16; - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 9; i++) { width += tableWidget->horizontalHeader()->sectionSize(i); } tableWidget->resize(width, 720); tabWidget->addTab(tableWidget, tabName.insert(6, " ").replace(0, 1, tabName.at(0).toUpper())); - this->resize(width + 20, 720); + + if (!this->isMaximized()) { + this->resize(width + 400, 720); + QSize mainWindowSize = QApplication::activeWindow()->size(); + this->resize(mainWindowSize.width() * 0.8, mainWindowSize.height() * 0.8); + } + this->show(); + + tableWidget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed); + tableWidget->setColumnWidth(3, 500); } this->setCentralWidget(tabWidget); } void TrophyViewer::SetTableItem(QTableWidget* parent, int row, int column, QString str) { - QWidget* widget = new QWidget(); - QVBoxLayout* layout = new QVBoxLayout(); - QLabel* label = new QLabel(str); - QTableWidgetItem* item = new QTableWidgetItem(); - label->setWordWrap(true); - label->setStyleSheet("color: white; font-size: 15px; font-weight: bold;"); + QTableWidgetItem* item = new QTableWidgetItem(str); - // Create shadow effect - QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect(); - shadowEffect->setBlurRadius(5); // Set the blur radius of the shadow - shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow - shadowEffect->setOffset(2, 2); // Set the offset of the shadow - - label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel - - layout->addWidget(label); if (column != 1 && column != 2 && column != 3) - layout->setAlignment(Qt::AlignCenter); - widget->setLayout(layout); + item->setTextAlignment(Qt::AlignCenter); + item->setFont(QFont("Arial", 12, QFont::Bold)); + + Theme theme = static_cast(Config::getMainWindowTheme()); + + if (theme == Theme::Light) { + item->setForeground(QBrush(Qt::black)); + } else { + item->setForeground(QBrush(Qt::white)); + } + parent->setItem(row, column, item); - parent->setCellWidget(row, column, widget); } diff --git a/src/qt_gui/trophy_viewer.h b/src/qt_gui/trophy_viewer.h index 089de433e..c63171774 100644 --- a/src/qt_gui/trophy_viewer.h +++ b/src/qt_gui/trophy_viewer.h @@ -4,24 +4,46 @@ #pragma once #include +#include +#include #include +#include #include #include #include #include #include +#include +#include #include #include #include +#include #include #include "common/types.h" #include "core/file_format/trp.h" +struct TrophyGameInfo { + QString name; + QString trophyPath; + QString gameTrpPath; +}; + class TrophyViewer : public QMainWindow { Q_OBJECT public: - explicit TrophyViewer(QString trophyPath, QString gameTrpPath); + explicit TrophyViewer( + QString trophyPath, QString gameTrpPath, QString gameName = "", + const QVector& allTrophyGames = QVector()); + + void updateTrophyInfo(); + void updateTableFilters(); + void onDockClosed(); + void reopenLeftDock(); + +private slots: + void onGameSelectionChanged(int index); private: void PopulateTrophyWidget(QString title); @@ -30,7 +52,17 @@ private: QTabWidget* tabWidget = nullptr; QStringList headers; QString gameTrpPath_; + QString currentGameName_; TRP trp; + QLabel* trophyInfoLabel; + QCheckBox* showEarnedCheck; + QCheckBox* showNotEarnedCheck; + QCheckBox* showHiddenCheck; + QComboBox* gameSelectionComboBox; + QPushButton* expandButton; + QDockWidget* trophyInfoDock; + QPushButton* reopenButton; + QVector allTrophyGames_; std::string GetTrpType(const QChar trp_) { switch (trp_.toLatin1()) { diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 80d196147..fcdde7240 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -11,6 +11,7 @@ #include "common/config.h" #include "common/elf_info.h" #include "common/version.h" +#include "core/debug_state.h" #include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" #include "imgui/renderer/imgui_core.h" @@ -396,6 +397,25 @@ void WindowSDL::WaitEvent() { case SDL_EVENT_QUIT: is_open = false; break; + case SDL_EVENT_TOGGLE_FULLSCREEN: { + if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { + SDL_SetWindowFullscreen(window, 0); + } else { + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); + } + break; + } + case SDL_EVENT_TOGGLE_PAUSE: + SDL_Log("Received SDL_EVENT_TOGGLE_PAUSE"); + + if (DebugState.IsGuestThreadsPaused()) { + SDL_Log("Game Resumed"); + DebugState.ResumeGuestThreads(); + } else { + SDL_Log("Game Paused"); + DebugState.PauseGuestThreads(); + } + break; default: break; } diff --git a/src/sdl_window.h b/src/sdl_window.h index 03ba0797b..48a9be58c 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -7,6 +7,8 @@ #include "core/libraries/pad/pad.h" #include "input/controller.h" #include "string" +#define SDL_EVENT_TOGGLE_FULLSCREEN (SDL_EVENT_USER + 1) +#define SDL_EVENT_TOGGLE_PAUSE (SDL_EVENT_USER + 2) struct SDL_Window; struct SDL_Gamepad; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 6b0d7228b..036df24d8 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -32,6 +32,8 @@ static constexpr spv::ExecutionMode GetInputPrimitiveType(AmdGpu::PrimitiveType return spv::ExecutionMode::Triangles; case AmdGpu::PrimitiveType::AdjTriangleList: return spv::ExecutionMode::InputTrianglesAdjacency; + case AmdGpu::PrimitiveType::AdjLineList: + return spv::ExecutionMode::InputLinesAdjacency; default: UNREACHABLE_MSG("Unknown input primitive type {}", u32(type)); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index e2d702389..9f8784797 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp @@ -236,7 +236,7 @@ Id EmitFindILsb64(EmitContext& ctx, Id value) { const Id hi{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 1U)}; const Id lo_lsb{ctx.OpFindILsb(ctx.U32[1], lo)}; const Id hi_lsb{ctx.OpFindILsb(ctx.U32[1], hi)}; - const Id found_lo{ctx.OpINotEqual(ctx.U32[1], lo_lsb, ctx.ConstU32(u32(-1)))}; + const Id found_lo{ctx.OpINotEqual(ctx.U1[1], lo_lsb, ctx.ConstU32(u32(-1)))}; return ctx.OpSelect(ctx.U32[1], found_lo, lo_lsb, hi_lsb); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 7c25d1477..e20cfeae2 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -50,6 +50,8 @@ static constexpr u32 NumVertices(AmdGpu::PrimitiveType type) { return 3u; case AmdGpu::PrimitiveType::AdjTriangleList: return 6u; + case AmdGpu::PrimitiveType::AdjLineList: + return 4u; default: UNREACHABLE(); } diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 126cb4eb6..cf1882b8c 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/assert.h" #include "shader_recompiler/frontend/control_flow_graph.h" @@ -39,9 +40,6 @@ static IR::Condition MakeCondition(const GcnInst& inst) { return IR::Condition::Execz; case Opcode::S_CBRANCH_EXECNZ: return IR::Condition::Execnz; - case Opcode::S_AND_SAVEEXEC_B64: - case Opcode::S_ANDN2_B64: - return IR::Condition::Execnz; default: return IR::Condition::True; } @@ -76,9 +74,9 @@ CFG::CFG(Common::ObjectPool& block_pool_, std::span inst_l index_to_pc.resize(inst_list.size() + 1); labels.reserve(LabelReserveSize); EmitLabels(); - EmitDivergenceLabels(); EmitBlocks(); LinkBlocks(); + SplitDivergenceScopes(); } void CFG::EmitLabels() { @@ -112,7 +110,7 @@ void CFG::EmitLabels() { std::ranges::sort(labels); } -void CFG::EmitDivergenceLabels() { +void CFG::SplitDivergenceScopes() { const auto is_open_scope = [](const GcnInst& inst) { // An open scope instruction is an instruction that modifies EXEC // but also saves the previous value to restore later. This indicates @@ -136,64 +134,97 @@ void CFG::EmitDivergenceLabels() { (inst.opcode == Opcode::S_ANDN2_B64 && inst.dst[0].field == OperandField::ExecLo); }; - // Since we will be adding new labels, avoid iterating those as well. - const size_t end_size = labels.size(); - for (u32 l = 0; l < end_size; l++) { - const Label start = labels[l]; - // Stop if we reached end of existing labels. - if (l == end_size - 1) { - break; - } - const Label end = labels[l + 1]; - const size_t end_index = GetIndex(end); - + for (auto blk = blocks.begin(); blk != blocks.end(); blk++) { + auto next_blk = std::next(blk); s32 curr_begin = -1; - s32 last_exec_idx = -1; - for (size_t index = GetIndex(start); index < end_index; index++) { + for (size_t index = blk->begin_index; index <= blk->end_index; index++) { const auto& inst = inst_list[index]; - if (curr_begin != -1) { - // Keep note of the last instruction that does not ignore exec, so we know where - // to end the divergence block without impacting trailing instructions that do. - if (!IgnoresExecMask(inst)) { - last_exec_idx = index; - } - // Consider a close scope on certain instruction types or at the last instruction - // before the next label. - if (is_close_scope(inst) || index == end_index - 1) { - // Only insert a scope if, since the open-scope instruction, there is at least - // one instruction that does not ignore exec. - if (index - curr_begin > 1 && last_exec_idx != -1) { - // Add a label to the instruction right after the open scope call. - // It is the start of a new basic block. - const auto& save_inst = inst_list[curr_begin]; - AddLabel(index_to_pc[curr_begin] + save_inst.length); - // Add a label to the close scope instruction. - // There are 3 cases where we need to close a scope. - // * Close scope instruction inside the block - // * Close scope instruction at the end of the block (cbranch or endpgm) - // * Normal instruction at the end of the block - // If the instruction we want to close the scope at is at the end of the - // block, we do not need to insert a new label. - if (last_exec_idx != end_index - 1) { - // Add the label after the last instruction affected by exec. - const auto& last_exec_inst = inst_list[last_exec_idx]; - AddLabel(index_to_pc[last_exec_idx] + last_exec_inst.length); - } - } - // Reset scope begin. + const bool is_close = is_close_scope(inst); + if ((is_close || index == blk->end_index) && curr_begin != -1) { + // If there are no instructions inside scope don't do anything. + if (index - curr_begin == 1) { curr_begin = -1; + continue; } + // If all instructions in the scope ignore exec masking, we shouldn't insert a + // scope. + const auto start = inst_list.begin() + curr_begin + 1; + if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask)) { + // Determine the first instruction affected by the exec mask. + do { + ++curr_begin; + } while (IgnoresExecMask(inst_list[curr_begin])); + + // Determine the last instruction affected by the exec mask. + s32 curr_end = index; + while (IgnoresExecMask(inst_list[curr_end])) { + --curr_end; + } + + // Create a new block for the divergence scope. + Block* block = block_pool.Create(); + block->begin = index_to_pc[curr_begin]; + block->end = index_to_pc[curr_end]; + block->begin_index = curr_begin; + block->end_index = curr_end; + block->end_inst = inst_list[curr_end]; + blocks.insert_before(next_blk, *block); + + // If we are inside the parent block, make an epilogue block and jump to it. + if (curr_end != blk->end_index) { + Block* epi_block = block_pool.Create(); + epi_block->begin = index_to_pc[curr_end + 1]; + epi_block->end = blk->end; + epi_block->begin_index = curr_end + 1; + epi_block->end_index = blk->end_index; + epi_block->end_inst = blk->end_inst; + epi_block->cond = blk->cond; + epi_block->end_class = blk->end_class; + epi_block->branch_true = blk->branch_true; + epi_block->branch_false = blk->branch_false; + blocks.insert_before(next_blk, *epi_block); + + // Have divergence block always jump to epilogue block. + block->cond = IR::Condition::True; + block->branch_true = epi_block; + block->branch_false = nullptr; + + // If the parent block fails to enter divergence block make it jump to + // epilogue too + blk->branch_false = epi_block; + } else { + // No epilogue block is needed since the divergence block + // also ends the parent block. Inherit the end condition. + auto& parent_blk = *blk; + ASSERT(blk->cond == IR::Condition::True && blk->branch_true); + block->cond = IR::Condition::True; + block->branch_true = blk->branch_true; + block->branch_false = nullptr; + + // If the parent block didn't enter the divergence scope + // have it jump directly to the next one + blk->branch_false = blk->branch_true; + } + + // Shrink parent block to end right before curr_begin + // and make it jump to divergence block + --curr_begin; + blk->end = index_to_pc[curr_begin]; + blk->end_index = curr_begin; + blk->end_inst = inst_list[curr_begin]; + blk->cond = IR::Condition::Execnz; + blk->end_class = EndClass::Branch; + blk->branch_true = block; + } + // Reset scope begin. + curr_begin = -1; } // Mark a potential start of an exec scope. if (is_open_scope(inst)) { curr_begin = index; - last_exec_idx = -1; } } } - - // Sort labels to make sure block insertion is correct. - std::ranges::sort(labels); } void CFG::EmitBlocks() { @@ -234,22 +265,6 @@ void CFG::LinkBlocks() { for (auto it = blocks.begin(); it != blocks.end(); it++) { auto& block = *it; const auto end_inst{block.end_inst}; - // Handle divergence block inserted here. - if (end_inst.opcode == Opcode::S_AND_SAVEEXEC_B64 || - end_inst.opcode == Opcode::S_ANDN2_B64 || end_inst.IsCmpx()) { - // Blocks are stored ordered by address in the set - auto next_it = std::next(it); - auto* target_block = &(*next_it); - ++target_block->num_predecessors; - block.branch_true = target_block; - - auto merge_it = std::next(next_it); - auto* merge_block = &(*merge_it); - ++merge_block->num_predecessors; - block.branch_false = merge_block; - block.end_class = EndClass::Branch; - continue; - } // If the block doesn't end with a branch we simply // need to link with the next block. diff --git a/src/shader_recompiler/frontend/control_flow_graph.h b/src/shader_recompiler/frontend/control_flow_graph.h index d98d4b05d..0acce3306 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.h +++ b/src/shader_recompiler/frontend/control_flow_graph.h @@ -57,9 +57,9 @@ public: private: void EmitLabels(); - void EmitDivergenceLabels(); void EmitBlocks(); void LinkBlocks(); + void SplitDivergenceScopes(); void AddLabel(Label address) { const auto it = std::ranges::find(labels, address); diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index 460f8913c..22f5b8644 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -61,6 +61,8 @@ void Translator::EmitDataShare(const GcnInst& inst) { return DS_WRITE(64, false, false, false, inst); case Opcode::DS_WRITE2_B64: return DS_WRITE(64, false, true, false, inst); + case Opcode::DS_WRITE2ST64_B64: + return DS_WRITE(64, false, true, true, inst); case Opcode::DS_READ_B64: return DS_READ(64, false, false, false, inst); case Opcode::DS_READ2_B64: diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index c8a4b13cb..5c66b1115 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -251,54 +251,6 @@ void FoldCmpClass(IR::Block& block, IR::Inst& inst) { } } -void FoldReadLane(IR::Block& block, IR::Inst& inst) { - const u32 lane = inst.Arg(1).U32(); - IR::Inst* prod = inst.Arg(0).InstRecursive(); - - const auto search_chain = [lane](const IR::Inst* prod) -> IR::Value { - while (prod->GetOpcode() == IR::Opcode::WriteLane) { - if (prod->Arg(2).U32() == lane) { - return prod->Arg(1); - } - prod = prod->Arg(0).InstRecursive(); - } - return {}; - }; - - if (prod->GetOpcode() == IR::Opcode::WriteLane) { - if (const IR::Value value = search_chain(prod); !value.IsEmpty()) { - inst.ReplaceUsesWith(value); - } - return; - } - - if (prod->GetOpcode() == IR::Opcode::Phi) { - boost::container::small_vector phi_args; - for (size_t arg_index = 0; arg_index < prod->NumArgs(); ++arg_index) { - const IR::Inst* arg{prod->Arg(arg_index).InstRecursive()}; - if (arg->GetOpcode() != IR::Opcode::WriteLane) { - return; - } - const IR::Value value = search_chain(arg); - if (value.IsEmpty()) { - continue; - } - phi_args.emplace_back(value); - } - if (std::ranges::all_of(phi_args, [&](IR::Value value) { return value == phi_args[0]; })) { - inst.ReplaceUsesWith(phi_args[0]); - return; - } - const auto insert_point = IR::Block::InstructionList::s_iterator_to(*prod); - IR::Inst* const new_phi{&*block.PrependNewInst(insert_point, IR::Opcode::Phi)}; - new_phi->SetFlags(IR::Type::U32); - for (size_t arg_index = 0; arg_index < phi_args.size(); arg_index++) { - new_phi->AddPhiOperand(prod->PhiBlock(arg_index), phi_args[arg_index]); - } - inst.ReplaceUsesWith(IR::Value{new_phi}); - } -} - void ConstantPropagation(IR::Block& block, IR::Inst& inst) { switch (inst.GetOpcode()) { case IR::Opcode::IAdd32: @@ -408,8 +360,6 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { case IR::Opcode::SelectF32: case IR::Opcode::SelectF64: return FoldSelect(inst); - case IR::Opcode::ReadLane: - return FoldReadLane(block, inst); case IR::Opcode::FPNeg32: FoldWhenAllImmediates(inst, [](f32 a) { return -a; }); return; diff --git a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp index fced4b362..5cf8a1525 100644 --- a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp +++ b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp @@ -1,11 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later + #include "common/assert.h" #include "shader_recompiler/info.h" #include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/breadth_first_search.h" #include "shader_recompiler/ir/ir_emitter.h" #include "shader_recompiler/ir/opcodes.h" +#include "shader_recompiler/ir/passes/ir_passes.h" #include "shader_recompiler/ir/pattern_matching.h" #include "shader_recompiler/ir/program.h" #include "shader_recompiler/runtime_info.h" @@ -75,10 +77,12 @@ namespace Shader::Optimization { * * output_patch_stride and output_cp_stride are usually compile time constants in the gcn * - * Hull shaders can probably also read output control points corresponding to other threads, like - * shared memory (but we havent seen this yet). - * ^ This is an UNREACHABLE for now. We may need to insert additional barriers if this happens. - * They should also be able to read PatchConst values, + * Hull shaders can also read output control points corresponding to other threads. + * In HLSL style, this should only be possible in the Patch Constant function. + * TODO we may need to insert additional barriers if sync is free/more permissive + * on AMD LDS HW + + * They should also be able to read output PatchConst values, * although not sure if this happens in practice. * * To determine which type of attribute (input, output, patchconst) we the check the users of @@ -101,22 +105,22 @@ namespace Shader::Optimization { * layout (location = 0) in vec4 in_attrs[][NUM_INPUT_ATTRIBUTES]; * * Here the NUM_INPUT_ATTRIBUTES is derived from the ls_stride member of the TessConstants V#. - * We divide ls_stride (in bytes) by 16 to get the number of vec4 attributes. - * For TES, the number of attributes comes from hs_cp_stride / 16. + * We take ALIGN_UP(ls_stride, 16) / 16 to get the number of vec4 attributes. + * For TES, NUM_INPUT_ATTRIBUTES is ALIGN_UP(hs_cp_stride, 16) / 16. * The first (outer) dimension is unsized but corresponds to the number of vertices in the hs input * patch (for Hull) or the hs output patch (for Domain). * * For input reads in TCS or TES, we emit SPIR-V like: - * float value = in_attrs[addr / ls_stride][(addr % ls_stride) >> 4][(addr & 0xF) >> 2]; + * float value = in_attrs[addr / ls_stride][(addr % ls_stride) >> 4][(addr % ls_stride) >> 2]; * * For output writes, we assume the control point index is InvocationId, since high level languages * impose that restriction (although maybe it's technically possible on hardware). So SPIR-V looks * like this: * layout (location = 0) in vec4 in_attrs[][NUM_OUTPUT_ATTRIBUTES]; - * out_attrs[InvocationId][(addr % hs_cp_stride) >> 4][(addr & 0xF) >> 2] = value; + * out_attrs[InvocationId][(addr % hs_cp_stride) >> 4][(addr % hs_cp_stride) >> 2] = value; * - * NUM_OUTPUT_ATTRIBUTES is derived by hs_cp_stride / 16, so it can link with the TES in_attrs - * variable. + * NUM_OUTPUT_ATTRIBUTES is derived by ALIGN_UP(hs_cp_stride, 16) / 16, so it matches + * NUM_INPUT_ATTRIBUTES of the TES. * * Another challenge is the fact that the GCN shader needs to address attributes from LDS as a whole * which contains the attributes from many patches. On the other hand, higher level shading @@ -235,12 +239,11 @@ private: case IR::Opcode::Phi: { struct PhiCounter { u16 seq_num; - u8 unique_edge; - u8 counter; + u16 unique_edge; }; PhiCounter count = inst->Flags(); - ASSERT_MSG(count.counter == 0 || count.unique_edge == use.operand); + ASSERT_MSG(count.seq_num == 0 || count.unique_edge == use.operand); // the point of seq_num is to tell us if we've already traversed this // phi on the current walk. Alternatively we could keep a set of phi's // seen on the current walk. This is to handle phi cycles @@ -249,13 +252,11 @@ private: count.seq_num = seq_num; // Mark the phi as having been traversed originally through this edge count.unique_edge = use.operand; - count.counter = inc; } else if (count.seq_num < seq_num) { count.seq_num = seq_num; // For now, assume we are visiting this phi via the same edge // as on other walks. If not, some dataflow analysis might be necessary ASSERT(count.unique_edge == use.operand); - count.counter += inc; } else { // count.seq_num == seq_num // there's a cycle, and we've already been here on this walk @@ -735,6 +736,8 @@ void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info) { } } } + + ConstantPropagationPass(program.post_order_blocks); } } // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir/passes/ir_passes.h b/src/shader_recompiler/ir/passes/ir_passes.h index 69628dbfd..760dbb112 100644 --- a/src/shader_recompiler/ir/passes/ir_passes.h +++ b/src/shader_recompiler/ir/passes/ir_passes.h @@ -17,11 +17,11 @@ void IdentityRemovalPass(IR::BlockList& program); void DeadCodeEliminationPass(IR::Program& program); void ConstantPropagationPass(IR::BlockList& program); void FlattenExtendedUserdataPass(IR::Program& program); +void ReadLaneEliminationPass(IR::Program& program); void ResourceTrackingPass(IR::Program& program); void CollectShaderInfoPass(IR::Program& program); void LowerBufferFormatToRaw(IR::Program& program); -void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info, - Stage stage); +void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info); void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info); void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); void DomainShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); diff --git a/src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp b/src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp new file mode 100644 index 000000000..fbe382d41 --- /dev/null +++ b/src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_recompiler/ir/program.h" + +namespace Shader::Optimization { + +static IR::Inst* SearchChain(IR::Inst* inst, u32 lane) { + while (inst->GetOpcode() == IR::Opcode::WriteLane) { + if (inst->Arg(2).U32() == lane) { + // We found a possible write lane source, return it. + return inst; + } + inst = inst->Arg(0).InstRecursive(); + } + return inst; +} + +static bool IsPossibleToEliminate(IR::Inst* inst, u32 lane) { + // Breadth-first search visiting the right most arguments first + boost::container::small_vector visited; + std::queue queue; + queue.push(inst); + + while (!queue.empty()) { + // Pop one instruction from the queue + IR::Inst* inst{queue.front()}; + queue.pop(); + + // If it's a WriteLane search for possible candidates + if (inst = SearchChain(inst, lane); inst->GetOpcode() == IR::Opcode::WriteLane) { + // We found a possible write lane source, stop looking here. + continue; + } + // If there are other instructions in-between that use the value we can't eliminate. + if (inst->GetOpcode() != IR::Opcode::ReadLane && inst->GetOpcode() != IR::Opcode::Phi) { + return false; + } + // Visit the right most arguments first + for (size_t arg = inst->NumArgs(); arg--;) { + auto arg_value{inst->Arg(arg)}; + if (arg_value.IsImmediate()) { + continue; + } + // Queue instruction if it hasn't been visited + IR::Inst* arg_inst{arg_value.InstRecursive()}; + if (std::ranges::find(visited, arg_inst) == visited.end()) { + visited.push_back(arg_inst); + queue.push(arg_inst); + } + } + } + return true; +} + +using PhiMap = std::unordered_map; + +static IR::Value GetRealValue(PhiMap& phi_map, IR::Inst* inst, u32 lane) { + // If this is a WriteLane op search the chain for a possible candidate. + if (inst = SearchChain(inst, lane); inst->GetOpcode() == IR::Opcode::WriteLane) { + return inst->Arg(1); + } + + // If this is a phi, duplicate it and populate its arguments with real values. + if (inst->GetOpcode() == IR::Opcode::Phi) { + // We are in a phi cycle, use the already duplicated phi. + const auto [it, is_new_phi] = phi_map.try_emplace(inst); + if (!is_new_phi) { + return IR::Value{it->second}; + } + + // Create new phi and insert it right before the old one. + const auto insert_point = IR::Block::InstructionList::s_iterator_to(*inst); + IR::Block* block = inst->GetParent(); + IR::Inst* new_phi{&*block->PrependNewInst(insert_point, IR::Opcode::Phi)}; + new_phi->SetFlags(IR::Type::U32); + it->second = new_phi; + + // Gather all arguments. + for (size_t arg_index = 0; arg_index < inst->NumArgs(); arg_index++) { + IR::Inst* arg_prod = inst->Arg(arg_index).InstRecursive(); + const IR::Value arg = GetRealValue(phi_map, arg_prod, lane); + new_phi->AddPhiOperand(inst->PhiBlock(arg_index), arg); + } + return IR::Value{new_phi}; + } + UNREACHABLE(); +} + +void ReadLaneEliminationPass(IR::Program& program) { + PhiMap phi_map; + for (IR::Block* const block : program.blocks) { + for (IR::Inst& inst : block->Instructions()) { + if (inst.GetOpcode() != IR::Opcode::ReadLane) { + continue; + } + const u32 lane = inst.Arg(1).U32(); + IR::Inst* prod = inst.Arg(0).InstRecursive(); + + // Check simple case of no control flow and phis + if (prod = SearchChain(prod, lane); prod->GetOpcode() == IR::Opcode::WriteLane) { + inst.ReplaceUsesWith(prod->Arg(1)); + continue; + } + + // Traverse the phi tree to see if it's possible to eliminate + if (prod->GetOpcode() == IR::Opcode::Phi && IsPossibleToEliminate(prod, lane)) { + inst.ReplaceUsesWith(GetRealValue(phi_map, prod, lane)); + phi_map.clear(); + } + } + } +} + +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp index d6f1efb12..071b94ac0 100644 --- a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp +++ b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp @@ -11,8 +11,7 @@ namespace Shader::Optimization { -void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info, - Stage stage) { +void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info) { auto& info = program.info; const auto& ForEachInstruction = [&](auto func) { @@ -24,7 +23,7 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim } }; - switch (stage) { + switch (program.info.stage) { case Stage::Local: { ForEachInstruction([=](IR::IREmitter& ir, IR::Inst& inst) { const auto opcode = inst.GetOpcode(); diff --git a/src/shader_recompiler/ir/srt_gvn_table.h b/src/shader_recompiler/ir/srt_gvn_table.h index 232ee6152..3baa1c7da 100644 --- a/src/shader_recompiler/ir/srt_gvn_table.h +++ b/src/shader_recompiler/ir/srt_gvn_table.h @@ -52,24 +52,16 @@ private: switch (inst->GetOpcode()) { case IR::Opcode::Phi: { - // hack to get to parity with main - // Need to fix ssa_rewrite pass to remove certain phis - std::optional source = TryRemoveTrivialPhi(inst); - if (!source) { - const auto pred = [](IR::Inst* inst) -> std::optional { - if (inst->GetOpcode() == IR::Opcode::GetUserData || - inst->GetOpcode() == IR::Opcode::CompositeConstructU32x2 || - inst->GetOpcode() == IR::Opcode::ReadConst) { - return inst; - } - return std::nullopt; - }; - source = IR::BreadthFirstSearch(inst, pred).transform([](auto inst) { - return IR::Value{inst}; - }); - ASSERT(source); - } - vn = GetValueNumber(source.value()); + const auto pred = [](IR::Inst* inst) -> std::optional { + if (inst->GetOpcode() == IR::Opcode::GetUserData || + inst->GetOpcode() == IR::Opcode::CompositeConstructU32x2 || + inst->GetOpcode() == IR::Opcode::ReadConst) { + return inst; + } + return std::nullopt; + }; + IR::Inst* source = IR::BreadthFirstSearch(inst, pred).value(); + vn = GetValueNumber(source); value_numbers[IR::Value(inst)] = vn; break; } @@ -117,30 +109,6 @@ private: return iv; } - // Temp workaround for something like this: - // [0000555558a5baf8] %297 = Phi [ %24, {Block $1} ], [ %297, {Block $5} ] (uses: 4) - // [0000555558a4e038] %305 = CompositeConstructU32x2 %297, %296 (uses: 4) - // [0000555558a4e0a8] %306 = ReadConst %305, #0 (uses: 2) - // Should probably be fixed in ssa_rewrite - std::optional TryRemoveTrivialPhi(IR::Inst* phi) { - IR::Value single_source{}; - - for (auto i = 0; i < phi->NumArgs(); i++) { - IR::Value v = phi->Arg(i).Resolve(); - if (v == IR::Value(phi)) { - continue; - } - if (!single_source.IsEmpty() && single_source != v) { - return std::nullopt; - } - single_source = v; - } - - ASSERT(!single_source.IsEmpty()); - phi->ReplaceUsesWith(single_source); - return single_source; - } - struct HashInstVector { size_t operator()(const InstVector& iv) const { u32 h = 0; diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index 1c132ebbb..5004e0beb 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -1,9 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/config.h" -#include "common/io_file.h" -#include "common/path_util.h" #include "shader_recompiler/frontend/control_flow_graph.h" #include "shader_recompiler/frontend/decode.h" #include "shader_recompiler/frontend/structured_control_flow.h" @@ -63,26 +60,18 @@ IR::Program TranslateProgram(std::span code, Pools& pools, Info& info program.post_order_blocks = Shader::IR::PostOrder(program.syntax_list.front()); // Run optimization passes - const auto stage = program.info.stage; - Shader::Optimization::SsaRewritePass(program.post_order_blocks); + Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::IdentityRemovalPass(program.blocks); if (info.l_stage == LogicalStage::TessellationControl) { - // Tess passes require previous const prop passes for now (for simplicity). TODO allow - // fine grained folding or opportunistic folding we set an operand to an immediate - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::TessellationPreprocess(program, runtime_info); - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::HullShaderTransform(program, runtime_info); } else if (info.l_stage == LogicalStage::TessellationEval) { - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::TessellationPreprocess(program, runtime_info); - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::DomainShaderTransform(program, runtime_info); } - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); - Shader::Optimization::RingAccessElimination(program, runtime_info, stage); - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); + Shader::Optimization::RingAccessElimination(program, runtime_info); + Shader::Optimization::ReadLaneEliminationPass(program); Shader::Optimization::FlattenExtendedUserdataPass(program); Shader::Optimization::ResourceTrackingPass(program); Shader::Optimization::LowerBufferFormatToRaw(program); diff --git a/src/shader_recompiler/specialization.h b/src/shader_recompiler/specialization.h index 1c3bfc60a..e40309aaf 100644 --- a/src/shader_recompiler/specialization.h +++ b/src/shader_recompiler/specialization.h @@ -13,7 +13,9 @@ namespace Shader { struct VsAttribSpecialization { + s32 num_components{}; AmdGpu::NumberClass num_class{}; + AmdGpu::CompMapping dst_select{}; auto operator<=>(const VsAttribSpecialization&) const = default; }; @@ -89,12 +91,17 @@ struct StageSpecialization { Backend::Bindings start_) : info{&info_}, runtime_info{runtime_info_}, start{start_} { fetch_shader_data = Gcn::ParseFetchShader(info_); - if (info_.stage == Stage::Vertex && fetch_shader_data && - !profile_.support_legacy_vertex_attributes) { + if (info_.stage == Stage::Vertex && fetch_shader_data) { // Specialize shader on VS input number types to follow spec. ForEachSharp(vs_attribs, fetch_shader_data->attributes, - [](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { - spec.num_class = AmdGpu::GetNumberClass(sharp.GetNumberFmt()); + [&profile_](auto& spec, const auto& desc, AmdGpu::Buffer sharp) { + spec.num_components = desc.UsesStepRates() + ? AmdGpu::NumComponents(sharp.GetDataFmt()) + : 0; + spec.num_class = profile_.support_legacy_vertex_attributes + ? AmdGpu::NumberClass{} + : AmdGpu::GetNumberClass(sharp.GetNumberFmt()); + spec.dst_select = sharp.DstSelect(); }); } u32 binding{}; diff --git a/src/shadps4.qrc b/src/shadps4.qrc index 14b50f7a5..83dea01c4 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -1,36 +1,40 @@ - - images/shadps4.ico - images/about_icon.png - images/dump_icon.png - images/play_icon.png - images/pause_icon.png - images/stop_icon.png - images/utils_icon.png - images/file_icon.png - images/folder_icon.png - images/themes_icon.png - images/iconsize_icon.png - images/list_icon.png - images/grid_icon.png - images/exit_icon.png - images/settings_icon.png - images/controller_icon.png - images/refresh_icon.png - images/update_icon.png - images/list_mode_icon.png - images/flag_jp.png - images/flag_eu.png - images/flag_unk.png - images/flag_us.png - images/flag_world.png - images/flag_china.png - images/github.png - images/discord.png - images/ko-fi.png - images/youtube.png - images/website.png - images/ps4_controller.png - images/keyboard_icon.png - + + images/shadps4.ico + images/about_icon.png + images/dump_icon.png + images/play_icon.png + images/pause_icon.png + images/stop_icon.png + images/utils_icon.png + images/file_icon.png + images/folder_icon.png + images/themes_icon.png + images/iconsize_icon.png + images/list_icon.png + images/grid_icon.png + images/exit_icon.png + images/settings_icon.png + images/controller_icon.png + images/restart_game_icon.png + images/update_icon.png + images/list_mode_icon.png + images/flag_jp.png + images/flag_eu.png + images/flag_unk.png + images/flag_us.png + images/flag_world.png + images/flag_china.png + images/github.png + images/discord.png + images/ko-fi.png + images/youtube.png + images/website.png + images/ps4_controller.png + images/keyboard_icon.png + images/KBM.png + images/fullscreen_icon.png + images/refreshlist_icon.png + images/trophy_icon.png + diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 246c8c947..967b952c6 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -602,20 +602,25 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spansrc_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Gds) { rasterizer->InlineData(dma_data->dst_addr_lo, &dma_data->data, sizeof(u32), true); - } else if (dma_data->src_sel == DmaDataSrc::Memory && + } else if ((dma_data->src_sel == DmaDataSrc::Memory || + dma_data->src_sel == DmaDataSrc::MemoryUsingL2) && dma_data->dst_sel == DmaDataDst::Gds) { rasterizer->InlineData(dma_data->dst_addr_lo, dma_data->SrcAddress(), dma_data->NumBytes(), true); } else if (dma_data->src_sel == DmaDataSrc::Data && - dma_data->dst_sel == DmaDataDst::Memory) { + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { rasterizer->InlineData(dma_data->DstAddress(), &dma_data->data, sizeof(u32), false); } else if (dma_data->src_sel == DmaDataSrc::Gds && - dma_data->dst_sel == DmaDataDst::Memory) { + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { // LOG_WARNING(Render_Vulkan, "GDS memory read"); - } else if (dma_data->src_sel == DmaDataSrc::Memory && - dma_data->dst_sel == DmaDataDst::Memory) { + } else if ((dma_data->src_sel == DmaDataSrc::Memory || + dma_data->src_sel == DmaDataSrc::MemoryUsingL2) && + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { rasterizer->InlineData(dma_data->DstAddress(), dma_data->SrcAddress(), dma_data->NumBytes(), false); @@ -726,20 +731,39 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span -Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { +Liverpool::Task Liverpool::ProcessCompute(const u32* acb, u32 acb_dwords, u32 vqid) { FIBER_ENTER(acb_task_name[vqid]); - const auto& queue = asc_queues[{vqid}]; + auto& queue = asc_queues[{vqid}]; - auto base_addr = reinterpret_cast(acb.data()); - while (!acb.empty()) { - const auto* header = reinterpret_cast(acb.data()); - const u32 type = header->type; - if (type != 3) { - // No other types of packets were spotted so far - UNREACHABLE_MSG("Invalid PM4 type {}", type); + auto base_addr = reinterpret_cast(acb); + while (acb_dwords > 0) { + auto* header = reinterpret_cast(acb); + u32 next_dw_off = header->type3.NumWords() + 1; + + // If we have a buffered packet, use it. + if (queue.tmp_dwords > 0) [[unlikely]] { + header = reinterpret_cast(queue.tmp_packet.data()); + next_dw_off = header->type3.NumWords() + 1 - queue.tmp_dwords; + std::memcpy(queue.tmp_packet.data() + queue.tmp_dwords, acb, next_dw_off * sizeof(u32)); + queue.tmp_dwords = 0; + } + + // If the packet is split across ring boundary, buffer until next submission + if (next_dw_off > acb_dwords) [[unlikely]] { + std::memcpy(queue.tmp_packet.data(), acb, acb_dwords * sizeof(u32)); + queue.tmp_dwords = acb_dwords; + if constexpr (!is_indirect) { + *queue.read_addr += acb_dwords; + *queue.read_addr %= queue.ring_size_dw; + } + break; + } + + if (header->type != 3) { + // No other types of packets were spotted so far + UNREACHABLE_MSG("Invalid PM4 type {}", header->type.Value()); } - const u32 count = header->type3.NumWords(); const PM4ItOpcode opcode = header->type3.opcode; const auto* it_body = reinterpret_cast(header) + 1; switch (opcode) { @@ -749,8 +773,8 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } case PM4ItOpcode::IndirectBuffer: { const auto* indirect_buffer = reinterpret_cast(header); - auto task = ProcessCompute( - {indirect_buffer->Address(), indirect_buffer->ib_size}, vqid); + auto task = ProcessCompute(indirect_buffer->Address(), + indirect_buffer->ib_size, vqid); RESUME_ASC(task, vqid); while (!task.handle.done()) { @@ -766,19 +790,24 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Gds) { rasterizer->InlineData(dma_data->dst_addr_lo, &dma_data->data, sizeof(u32), true); - } else if (dma_data->src_sel == DmaDataSrc::Memory && + } else if ((dma_data->src_sel == DmaDataSrc::Memory || + dma_data->src_sel == DmaDataSrc::MemoryUsingL2) && dma_data->dst_sel == DmaDataDst::Gds) { rasterizer->InlineData(dma_data->dst_addr_lo, dma_data->SrcAddress(), dma_data->NumBytes(), true); } else if (dma_data->src_sel == DmaDataSrc::Data && - dma_data->dst_sel == DmaDataDst::Memory) { + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { rasterizer->InlineData(dma_data->DstAddress(), &dma_data->data, sizeof(u32), false); } else if (dma_data->src_sel == DmaDataSrc::Gds && - dma_data->dst_sel == DmaDataDst::Memory) { + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { // LOG_WARNING(Render_Vulkan, "GDS memory read"); - } else if (dma_data->src_sel == DmaDataSrc::Memory && - dma_data->dst_sel == DmaDataDst::Memory) { + } else if ((dma_data->src_sel == DmaDataSrc::Memory || + dma_data->src_sel == DmaDataSrc::MemoryUsingL2) && + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { rasterizer->InlineData(dma_data->DstAddress(), dma_data->SrcAddress(), dma_data->NumBytes(), false); @@ -800,7 +829,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } case PM4ItOpcode::SetShReg: { const auto* set_data = reinterpret_cast(header); - const auto set_size = (count - 1) * sizeof(u32); + const auto set_size = (header->type3.NumWords() - 1) * sizeof(u32); if (set_data->reg_offset >= 0x200 && set_data->reg_offset <= (0x200 + sizeof(ComputeProgram) / 4)) { @@ -895,14 +924,14 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } default: UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}", - static_cast(opcode), count); + static_cast(opcode), header->type3.NumWords()); } - const auto packet_size_dw = header->type3.NumWords() + 1; - acb = NextPacket(acb, packet_size_dw); + acb += next_dw_off; + acb_dwords -= next_dw_off; if constexpr (!is_indirect) { - *queue.read_addr += packet_size_dw; + *queue.read_addr += next_dw_off; *queue.read_addr %= queue.ring_size_dw; } } @@ -969,7 +998,7 @@ void Liverpool::SubmitAsc(u32 gnm_vqid, std::span acb) { auto& queue = mapped_queues[gnm_vqid]; const auto vqid = gnm_vqid - 1; - const auto& task = ProcessCompute(acb, vqid); + const auto& task = ProcessCompute(acb.data(), acb.size(), vqid); { std::scoped_lock lock{queue.m_access}; queue.submits.emplace(task.handle); diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index c18bcd57b..474c04ec2 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -1496,10 +1496,13 @@ public: } struct AscQueueInfo { + static constexpr size_t Pm4BufferSize = 1024; VAddr map_addr; u32* read_addr; u32 ring_size_dw; u32 pipe_id; + std::array tmp_packet; + u32 tmp_dwords; }; Common::SlotVector asc_queues{}; @@ -1541,7 +1544,7 @@ private: Task ProcessGraphics(std::span dcb, std::span ccb); Task ProcessCeUpdate(std::span ccb); template - Task ProcessCompute(std::span acb, u32 vqid); + Task ProcessCompute(const u32* acb, u32 acb_dwords, u32 vqid); void Process(std::stop_token stoken); diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index e92ba17fa..ae1d32e00 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -377,12 +377,14 @@ struct PM4CmdAcquireMem { enum class DmaDataDst : u32 { Memory = 0, Gds = 1, + MemoryUsingL2 = 3, }; enum class DmaDataSrc : u32 { Memory = 0, Gds = 1, Data = 2, + MemoryUsingL2 = 3, }; struct PM4DmaData { diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index e60cca122..3001bf773 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -12,6 +12,7 @@ set(SHADER_FILES detilers/micro_64bpp.comp detilers/micro_8bpp.comp fs_tri.vert + fsr.comp post_process.frag ) diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake index 9f7525535..798b43e6c 100644 --- a/src/video_core/host_shaders/StringShaderHeader.cmake +++ b/src/video_core/host_shaders/StringShaderHeader.cmake @@ -9,28 +9,31 @@ get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME) string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME}) string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME) -FILE(READ ${SOURCE_FILE} line_contents) +# Function to recursively parse #include directives and replace them with file contents +function(parse_includes file_path output_content) + file(READ ${file_path} file_content) + # This regex includes \n at the begin to (hackish) avoid including comments + string(REGEX MATCHALL "\n#include +\"[^\"]+\"" includes "${file_content}") -# Replace double quotes with single quotes, -# as double quotes will be used to wrap the lines -STRING(REGEX REPLACE "\"" "'" line_contents "${line_contents}") + set(parsed_content "${file_content}") + foreach (include_match ${includes}) + string(REGEX MATCH "\"([^\"]+)\"" _ "${include_match}") + set(include_file ${CMAKE_MATCH_1}) + get_filename_component(include_full_path "${file_path}" DIRECTORY) + set(include_full_path "${include_full_path}/${include_file}") -# CMake separates list elements with semicolons, but semicolons -# are used extensively in the shader code. -# Replace with a temporary marker, to be reverted later. -STRING(REGEX REPLACE ";" "{{SEMICOLON}}" line_contents "${line_contents}") + if (NOT EXISTS "${include_full_path}") + message(FATAL_ERROR "Included file not found: ${include_full_path} from ${file_path}") + endif () -# Make every line an individual element in the CMake list. -STRING(REGEX REPLACE "\n" ";" line_contents "${line_contents}") + parse_includes("${include_full_path}" sub_content) + string(REPLACE "${include_match}" "\n${sub_content}" parsed_content "${parsed_content}") + endforeach () + set(${output_content} "${parsed_content}" PARENT_SCOPE) +endfunction() -# Build the shader string, wrapping each line in double quotes. -foreach(line IN LISTS line_contents) - string(CONCAT CONTENTS "${CONTENTS}" \"${line}\\n\"\n) -endforeach() - -# Revert the original semicolons in the source. -STRING(REGEX REPLACE "{{SEMICOLON}}" ";" CONTENTS "${CONTENTS}") +parse_includes("${SOURCE_FILE}" CONTENTS) get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY) -make_directory(${OUTPUT_DIR}) +file(MAKE_DIRECTORY ${OUTPUT_DIR}) configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY) diff --git a/src/video_core/host_shaders/fsr.comp b/src/video_core/host_shaders/fsr.comp new file mode 100644 index 000000000..105859e35 --- /dev/null +++ b/src/video_core/host_shaders/fsr.comp @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// SPDX-License-Identifier: MIT + +#version 450 +#extension GL_ARB_separate_shader_objects: enable +#extension GL_ARB_shading_language_420pack: enable + +// FidelityFX Super Resolution Sample +// +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +layout (push_constant) uniform const_buffer +{ + uvec4 Const0; + uvec4 Const1; + uvec4 Const2; + uvec4 Const3; + uvec4 Sample; +}; + +#define A_GPU 1 +#define A_GLSL 1 + +#define A_HALF +#include "fsr/ffx_a.h" + +layout (set = 0, binding = 0) uniform texture2D InputTexture; +layout (set = 0, binding = 1, rgba16f) uniform image2D OutputTexture; +layout (set = 0, binding = 2) uniform sampler InputSampler; + +#if SAMPLE_EASU +#define FSR_EASU_H 1 +AH4 FsrEasuRH(AF2 p) { AH4 res = AH4(textureGather(sampler2D(InputTexture, InputSampler), p, 0)); return res; } +AH4 FsrEasuGH(AF2 p) { AH4 res = AH4(textureGather(sampler2D(InputTexture, InputSampler), p, 1)); return res; } +AH4 FsrEasuBH(AF2 p) { AH4 res = AH4(textureGather(sampler2D(InputTexture, InputSampler), p, 2)); return res; } +#endif// SAMPLE_EASU + +#if SAMPLE_RCAS +#define FSR_RCAS_H +AH4 FsrRcasLoadH(ASW2 p) { return AH4(texelFetch(sampler2D(InputTexture, InputSampler), ASU2(p), 0)); } +void FsrRcasInputH(inout AH1 r, inout AH1 g, inout AH1 b) { } +#endif// SAMPLE_RCAS + +#include "fsr/ffx_fsr1.h" + +void CurrFilter(AU2 pos) +{ + #if SAMPLE_EASU + AH3 c; + FsrEasuH(c, pos, Const0, Const1, Const2, Const3); + if (Sample.x == 1) + c *= c; + imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); + #endif// SAMPLE_EASU +#if SAMPLE_RCAS + AH3 c; + FsrRcasH(c.r, c.g, c.b, pos, Const0); + if (Sample.x == 1) + c *= c; + imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); + #endif// SAMPLE_RCAS +} + +layout (local_size_x = 64) in; +void main() +{ + // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. + AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); + CurrFilter(gxy); + gxy.x += 8u; + CurrFilter(gxy); + gxy.y += 8u; + CurrFilter(gxy); + gxy.x -= 8u; + CurrFilter(gxy); +} \ No newline at end of file diff --git a/src/video_core/host_shaders/fsr/ffx_a.h b/src/video_core/host_shaders/fsr/ffx_a.h new file mode 100644 index 000000000..882b0381c --- /dev/null +++ b/src/video_core/host_shaders/fsr/ffx_a.h @@ -0,0 +1,2657 @@ +// clang-format off +//============================================================================================================================== +// +// [A] SHADER PORTABILITY 1.20210629 +// +//============================================================================================================================== +// FidelityFX Super Resolution Sample +// +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +// MIT LICENSE +// =========== +// Copyright (c) 2014 Michal Drobot (for concepts used in "FLOAT APPROXIMATIONS"). +// ----------- +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// ----------- +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +// Software. +// ----------- +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +// ABOUT +// ===== +// Common central point for high-level shading language and C portability for various shader headers. +//------------------------------------------------------------------------------------------------------------------------------ +// DEFINES +// ======= +// A_CPU ..... Include the CPU related code. +// A_GPU ..... Include the GPU related code. +// A_GLSL .... Using GLSL. +// A_HLSL .... Using HLSL. +// A_HLSL_6_2 Using HLSL 6.2 with new 'uint16_t' and related types (requires '-enable-16bit-types'). +// A_NO_16_BIT_CAST Don't use instructions that are not availabe in SPIR-V (needed for running A_HLSL_6_2 on Vulkan) +// A_GCC ..... Using a GCC compatible compiler (else assume MSVC compatible compiler by default). +// ======= +// A_BYTE .... Support 8-bit integer. +// A_HALF .... Support 16-bit integer and floating point. +// A_LONG .... Support 64-bit integer. +// A_DUBL .... Support 64-bit floating point. +// ======= +// A_WAVE .... Support wave-wide operations. +//------------------------------------------------------------------------------------------------------------------------------ +// To get #include "ffx_a.h" working in GLSL use '#extension GL_GOOGLE_include_directive:require'. +//------------------------------------------------------------------------------------------------------------------------------ +// SIMPLIFIED TYPE SYSTEM +// ====================== +// - All ints will be unsigned with exception of when signed is required. +// - Type naming simplified and shortened "A<#components>", +// - H = 16-bit float (half) +// - F = 32-bit float (float) +// - D = 64-bit float (double) +// - P = 1-bit integer (predicate, not using bool because 'B' is used for byte) +// - B = 8-bit integer (byte) +// - W = 16-bit integer (word) +// - U = 32-bit integer (unsigned) +// - L = 64-bit integer (long) +// - Using "AS<#components>" for signed when required. +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Make sure 'ALerp*(a,b,m)' does 'b*m+(-a*m+a)' (2 ops). +//------------------------------------------------------------------------------------------------------------------------------ +// CHANGE LOG +// ========== +// 20200914 - Expanded wave ops and prx code. +// 20200713 - Added [ZOL] section, fixed serious bugs in sRGB and Rec.709 color conversion code, etc. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// COMMON +//============================================================================================================================== +#define A_2PI 6.28318530718 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// CPU +// +// +//============================================================================================================================== +#ifdef A_CPU + // Supporting user defined overrides. + #ifndef A_RESTRICT + #define A_RESTRICT __restrict + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifndef A_STATIC + #define A_STATIC static + #endif +//------------------------------------------------------------------------------------------------------------------------------ + // Same types across CPU and GPU. + // Predicate uses 32-bit integer (C friendly bool). + typedef uint32_t AP1; + typedef float AF1; + typedef double AD1; + typedef uint8_t AB1; + typedef uint16_t AW1; + typedef uint32_t AU1; + typedef uint64_t AL1; + typedef int8_t ASB1; + typedef int16_t ASW1; + typedef int32_t ASU1; + typedef int64_t ASL1; +//------------------------------------------------------------------------------------------------------------------------------ + #define AD1_(a) ((AD1)(a)) + #define AF1_(a) ((AF1)(a)) + #define AL1_(a) ((AL1)(a)) + #define AU1_(a) ((AU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1_(a) ((ASL1)(a)) + #define ASU1_(a) ((ASU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AU1 AU1_AF1(AF1 a){union{AF1 f;AU1 u;}bits;bits.f=a;return bits.u;} +//------------------------------------------------------------------------------------------------------------------------------ + #define A_TRUE 1 + #define A_FALSE 0 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// CPU/GPU PORTING +// +//------------------------------------------------------------------------------------------------------------------------------ +// Get CPU and GPU to share all setup code, without duplicate code paths. +// This uses a lower-case prefix for special vector constructs. +// - In C restrict pointers are used. +// - In the shading language, in/inout/out arguments are used. +// This depends on the ability to access a vector value in both languages via array syntax (aka color[2]). +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD1 *A_RESTRICT + #define retAD3 AD1 *A_RESTRICT + #define retAD4 AD1 *A_RESTRICT + #define retAF2 AF1 *A_RESTRICT + #define retAF3 AF1 *A_RESTRICT + #define retAF4 AF1 *A_RESTRICT + #define retAL2 AL1 *A_RESTRICT + #define retAL3 AL1 *A_RESTRICT + #define retAL4 AL1 *A_RESTRICT + #define retAU2 AU1 *A_RESTRICT + #define retAU3 AU1 *A_RESTRICT + #define retAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 AD1 *A_RESTRICT + #define inAD3 AD1 *A_RESTRICT + #define inAD4 AD1 *A_RESTRICT + #define inAF2 AF1 *A_RESTRICT + #define inAF3 AF1 *A_RESTRICT + #define inAF4 AF1 *A_RESTRICT + #define inAL2 AL1 *A_RESTRICT + #define inAL3 AL1 *A_RESTRICT + #define inAL4 AL1 *A_RESTRICT + #define inAU2 AU1 *A_RESTRICT + #define inAU3 AU1 *A_RESTRICT + #define inAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 AD1 *A_RESTRICT + #define inoutAD3 AD1 *A_RESTRICT + #define inoutAD4 AD1 *A_RESTRICT + #define inoutAF2 AF1 *A_RESTRICT + #define inoutAF3 AF1 *A_RESTRICT + #define inoutAF4 AF1 *A_RESTRICT + #define inoutAL2 AL1 *A_RESTRICT + #define inoutAL3 AL1 *A_RESTRICT + #define inoutAL4 AL1 *A_RESTRICT + #define inoutAU2 AU1 *A_RESTRICT + #define inoutAU3 AU1 *A_RESTRICT + #define inoutAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 AD1 *A_RESTRICT + #define outAD3 AD1 *A_RESTRICT + #define outAD4 AD1 *A_RESTRICT + #define outAF2 AF1 *A_RESTRICT + #define outAF3 AF1 *A_RESTRICT + #define outAF4 AF1 *A_RESTRICT + #define outAL2 AL1 *A_RESTRICT + #define outAL3 AL1 *A_RESTRICT + #define outAL4 AL1 *A_RESTRICT + #define outAU2 AU1 *A_RESTRICT + #define outAU3 AU1 *A_RESTRICT + #define outAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD1 x[2] + #define varAD3(x) AD1 x[3] + #define varAD4(x) AD1 x[4] + #define varAF2(x) AF1 x[2] + #define varAF3(x) AF1 x[3] + #define varAF4(x) AF1 x[4] + #define varAL2(x) AL1 x[2] + #define varAL3(x) AL1 x[3] + #define varAL4(x) AL1 x[4] + #define varAU2(x) AU1 x[2] + #define varAU3(x) AU1 x[3] + #define varAU4(x) AU1 x[4] +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) {x,y} + #define initAD3(x,y,z) {x,y,z} + #define initAD4(x,y,z,w) {x,y,z,w} + #define initAF2(x,y) {x,y} + #define initAF3(x,y,z) {x,y,z} + #define initAF4(x,y,z,w) {x,y,z,w} + #define initAL2(x,y) {x,y} + #define initAL3(x,y,z) {x,y,z} + #define initAL4(x,y,z,w) {x,y,z,w} + #define initAU2(x,y) {x,y} + #define initAU3(x,y,z) {x,y,z} + #define initAU4(x,y,z,w) {x,y,z,w} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Replace transcendentals with manual versions. +//============================================================================================================================== + #ifdef A_GCC + A_STATIC AD1 AAbsD1(AD1 a){return __builtin_fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return __builtin_fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(__builtin_abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(__builtin_llabs(ASL1_(a)));} + #else + A_STATIC AD1 AAbsD1(AD1 a){return fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(labs((long)ASL1_(a)));} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ACosD1(AD1 a){return __builtin_cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return __builtin_cosf(a);} + #else + A_STATIC AD1 ACosD1(AD1 a){return cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return cosf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ADotD2(inAD2 a,inAD2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AD1 ADotD3(inAD3 a,inAD3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AD1 ADotD4(inAD4 a,inAD4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} + A_STATIC AF1 ADotF2(inAF2 a,inAF2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AF1 ADotF3(inAF3 a,inAF3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AF1 ADotF4(inAF4 a,inAF4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AExp2D1(AD1 a){return __builtin_exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return __builtin_exp2f(a);} + #else + A_STATIC AD1 AExp2D1(AD1 a){return exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return exp2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AFloorD1(AD1 a){return __builtin_floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return __builtin_floorf(a);} + #else + A_STATIC AD1 AFloorD1(AD1 a){return floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return floorf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ALerpD1(AD1 a,AD1 b,AD1 c){return b*c+(-a*c+a);} + A_STATIC AF1 ALerpF1(AF1 a,AF1 b,AF1 c){return b*c+(-a*c+a);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ALog2D1(AD1 a){return __builtin_log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return __builtin_log2f(a);} + #else + A_STATIC AD1 ALog2D1(AD1 a){return log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return log2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMaxD1(AD1 a,AD1 b){return a>b?a:b;} + A_STATIC AF1 AMaxF1(AF1 a,AF1 b){return a>b?a:b;} + A_STATIC AL1 AMaxL1(AL1 a,AL1 b){return a>b?a:b;} + A_STATIC AU1 AMaxU1(AU1 a,AU1 b){return a>b?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + // These follow the convention that A integer types don't have signage, until they are operated on. + A_STATIC AL1 AMaxSL1(AL1 a,AL1 b){return (ASL1_(a)>ASL1_(b))?a:b;} + A_STATIC AU1 AMaxSU1(AU1 a,AU1 b){return (ASU1_(a)>ASU1_(b))?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMinD1(AD1 a,AD1 b){return a>ASL1_(b));} + A_STATIC AU1 AShrSU1(AU1 a,AU1 b){return AU1_(ASU1_(a)>>ASU1_(b));} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASinD1(AD1 a){return __builtin_sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return __builtin_sinf(a);} + #else + A_STATIC AD1 ASinD1(AD1 a){return sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return sinf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASqrtD1(AD1 a){return __builtin_sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return __builtin_sqrtf(a);} + #else + A_STATIC AD1 ASqrtD1(AD1 a){return sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return sqrtf(a);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + A_STATIC AD1 AClampD1(AD1 x,AD1 n,AD1 m){return AMaxD1(n,AMinD1(x,m));} + A_STATIC AF1 AClampF1(AF1 x,AF1 n,AF1 m){return AMaxF1(n,AMinF1(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AFractD1(AD1 a){return a-AFloorD1(a);} + A_STATIC AF1 AFractF1(AF1 a){return a-AFloorF1(a);} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 APowD1(AD1 a,AD1 b){return AExp2D1(b*ALog2D1(a));} + A_STATIC AF1 APowF1(AF1 a,AF1 b){return AExp2F1(b*ALog2F1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ARsqD1(AD1 a){return ARcpD1(ASqrtD1(a));} + A_STATIC AF1 ARsqF1(AF1 a){return ARcpF1(ASqrtF1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ASatD1(AD1 a){return AMinD1(1.0,AMaxD1(0.0,a));} + A_STATIC AF1 ASatF1(AF1 a){return AMinF1(1.0f,AMaxF1(0.0f,a));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + A_STATIC retAD2 opAAbsD2(outAD2 d,inAD2 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);return d;} + A_STATIC retAD3 opAAbsD3(outAD3 d,inAD3 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);return d;} + A_STATIC retAD4 opAAbsD4(outAD4 d,inAD4 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);d[3]=AAbsD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAbsF2(outAF2 d,inAF2 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);return d;} + A_STATIC retAF3 opAAbsF3(outAF3 d,inAF3 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);return d;} + A_STATIC retAF4 opAAbsF4(outAF4 d,inAF4 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);d[3]=AAbsF1(a[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opAAddOneD2(outAD2 d,inAD2 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;return d;} + A_STATIC retAD3 opAAddOneD3(outAD3 d,inAD3 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;return d;} + A_STATIC retAD4 opAAddOneD4(outAD4 d,inAD4 a,AD1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;d[3]=a[3]+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAddOneF2(outAF2 d,inAF2 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;return d;} + A_STATIC retAF3 opAAddOneF3(outAF3 d,inAF3 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;return d;} + A_STATIC retAF4 opAAddOneF4(outAF4 d,inAF4 a,AF1 b){d[0]=a[0]+b;d[1]=a[1]+b;d[2]=a[2]+b;d[3]=a[3]+b;return d;} +//============================================================================================================================== + A_STATIC retAD2 opACpyD2(outAD2 d,inAD2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAD3 opACpyD3(outAD3 d,inAD3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAD4 opACpyD4(outAD4 d,inAD4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opACpyF2(outAF2 d,inAF2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAF3 opACpyF3(outAF3 d,inAF3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAF4 opACpyF4(outAF4 d,inAF4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);return d;} + A_STATIC retAD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);return d;} + A_STATIC retAD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);d[3]=ALerpD1(a[3],b[3],c[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);return d;} + A_STATIC retAF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);return d;} + A_STATIC retAF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);d[3]=ALerpF1(a[3],b[3],c[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);return d;} + A_STATIC retAD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);return d;} + A_STATIC retAD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);d[3]=ALerpD1(a[3],b[3],c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);return d;} + A_STATIC retAF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);return d;} + A_STATIC retAF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);d[3]=ALerpF1(a[3],b[3],c);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);d[3]=AMaxD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);d[3]=AMaxF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);d[3]=AMinD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);d[3]=AMinF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//============================================================================================================================== + A_STATIC retAD2 opANegD2(outAD2 d,inAD2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAD3 opANegD3(outAD3 d,inAD3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAD4 opANegD4(outAD4 d,inAD4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opANegF2(outAF2 d,inAF2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAF3 opANegF3(outAF3 d,inAF3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAF4 opANegF4(outAF4 d,inAF4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opARcpD2(outAD2 d,inAD2 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);return d;} + A_STATIC retAD3 opARcpD3(outAD3 d,inAD3 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);return d;} + A_STATIC retAD4 opARcpD4(outAD4 d,inAD4 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);d[3]=ARcpD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opARcpF2(outAF2 d,inAF2 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);return d;} + A_STATIC retAF3 opARcpF3(outAF3 d,inAF3 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);return d;} + A_STATIC retAF4 opARcpF4(outAF4 d,inAF4 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);d[3]=ARcpF1(a[3]);return d;} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF FLOAT PACKING +//============================================================================================================================== + // Convert float to half (in lower 16-bits of output). + // Same fast technique as documented here: ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf + // Supports denormals. + // Conversion rules are to make computations possibly "safer" on the GPU, + // -INF & -NaN -> -65504 + // +INF & +NaN -> +65504 + A_STATIC AU1 AU1_AH1_AF1(AF1 f){ + static AW1 base[512]={ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100, + 0x0200,0x0400,0x0800,0x0c00,0x1000,0x1400,0x1800,0x1c00,0x2000,0x2400,0x2800,0x2c00,0x3000,0x3400,0x3800,0x3c00, + 0x4000,0x4400,0x4800,0x4c00,0x5000,0x5400,0x5800,0x5c00,0x6000,0x6400,0x6800,0x6c00,0x7000,0x7400,0x7800,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8001,0x8002,0x8004,0x8008,0x8010,0x8020,0x8040,0x8080,0x8100, + 0x8200,0x8400,0x8800,0x8c00,0x9000,0x9400,0x9800,0x9c00,0xa000,0xa400,0xa800,0xac00,0xb000,0xb400,0xb800,0xbc00, + 0xc000,0xc400,0xc800,0xcc00,0xd000,0xd400,0xd800,0xdc00,0xe000,0xe400,0xe800,0xec00,0xf000,0xf400,0xf800,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff}; + static AB1 shift[512]={ + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}; + union{AF1 f;AU1 u;}bits;bits.f=f;AU1 u=bits.u;AU1 i=u>>23;return (AU1)(base[i])+((u&0x7fffff)>>shift[i]);} +//------------------------------------------------------------------------------------------------------------------------------ + // Used to output packed constant. + A_STATIC AU1 AU1_AH2_AF2(inAF2 a){return AU1_AH1_AF1(a[0])+(AU1_AH1_AF1(a[1])<<16);} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GLSL +// +// +//============================================================================================================================== +#if defined(A_GLSL) && defined(A_GPU) + #ifndef A_SKIP_EXT + #ifdef A_HALF + #extension GL_EXT_shader_16bit_storage:require + #extension GL_EXT_shader_explicit_arithmetic_types:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_LONG + #extension GL_ARB_gpu_shader_int64:require + #extension GL_NV_shader_atomic_int64:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_WAVE + #extension GL_KHR_shader_subgroup_arithmetic:require + #extension GL_KHR_shader_subgroup_ballot:require + #extension GL_KHR_shader_subgroup_quad:require + #extension GL_KHR_shader_subgroup_shuffle:require + #endif + #endif +//============================================================================================================================== + #define AP1 bool + #define AP2 bvec2 + #define AP3 bvec3 + #define AP4 bvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 vec2 + #define AF3 vec3 + #define AF4 vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uvec2 + #define AU3 uvec3 + #define AU4 uvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 ivec2 + #define ASU3 ivec3 + #define ASU4 ivec4 +//============================================================================================================================== + #define AF1_AU1(x) uintBitsToFloat(AU1(x)) + #define AF2_AU2(x) uintBitsToFloat(AU2(x)) + #define AF3_AU3(x) uintBitsToFloat(AU3(x)) + #define AF4_AU4(x) uintBitsToFloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) floatBitsToUint(AF1(x)) + #define AU2_AF2(x) floatBitsToUint(AF2(x)) + #define AU3_AF3(x) floatBitsToUint(AF3(x)) + #define AU4_AF4(x) floatBitsToUint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH1_AF1_x(AF1 a){return packHalf2x16(AF2(a,0.0));} + #define AU1_AH1_AF1(a) AU1_AH1_AF1_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2_AF2 packHalf2x16 + #define AU1_AW2Unorm_AF2 packUnorm2x16 + #define AU1_AB4Unorm_AF4 packUnorm4x8 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF2_AH2_AU1 unpackHalf2x16 + #define AF2_AW2Unorm_AU1 unpackUnorm2x16 + #define AF4_AB4Unorm_AU1 unpackUnorm4x8 +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){return bitfieldExtract(src,ASU1(off),ASU1(bits));} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + // Proxy for V_BFI_B32 where the 'mask' is set as 'bits', 'mask=(1<>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #define AB1 uint8_t + #define AB2 u8vec2 + #define AB3 u8vec3 + #define AB4 u8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASB1 int8_t + #define ASB2 i8vec2 + #define ASB3 i8vec3 + #define ASB4 i8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + AB1 AB1_x(AB1 a){return AB1(a);} + AB2 AB2_x(AB1 a){return AB2(a,a);} + AB3 AB3_x(AB1 a){return AB3(a,a,a);} + AB4 AB4_x(AB1 a){return AB4(a,a,a,a);} + #define AB1_(a) AB1_x(AB1(a)) + #define AB2_(a) AB2_x(AB1(a)) + #define AB3_(a) AB3_x(AB1(a)) + #define AB4_(a) AB4_x(AB1(a)) + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #define AH1 float16_t + #define AH2 f16vec2 + #define AH3 f16vec3 + #define AH4 f16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 uint16_t + #define AW2 u16vec2 + #define AW3 u16vec3 + #define AW4 u16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 int16_t + #define ASW2 i16vec2 + #define ASW3 i16vec3 + #define ASW4 i16vec4 +//============================================================================================================================== + #define AH2_AU1(x) unpackFloat2x16(AU1(x)) + AH4 AH4_AU2_x(AU2 x){return AH4(unpackFloat2x16(x.x),unpackFloat2x16(x.y));} + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) unpackUint2x16(AU1(x)) + #define AW4_AU2(x) unpackUint4x16(pack64(AU2(x))) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2(x) packFloat2x16(AH2(x)) + AU2 AU2_AH4_x(AH4 x){return AU2(packFloat2x16(x.xy),packFloat2x16(x.zw));} + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) packUint2x16(AW2(x)) + #define AU2_AW4(x) unpack32(packUint4x16(AW4(x))) +//============================================================================================================================== + #define AW1_AH1(x) halfBitsToUint16(AH1(x)) + #define AW2_AH2(x) halfBitsToUint16(AH2(x)) + #define AW3_AH3(x) halfBitsToUint16(AH3(x)) + #define AW4_AH4(x) halfBitsToUint16(AH4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AH1_AW1(x) uint16BitsToHalf(AW1(x)) + #define AH2_AW2(x) uint16BitsToHalf(AW2(x)) + #define AH3_AW3(x) uint16BitsToHalf(AW3(x)) + #define AH4_AW4(x) uint16BitsToHalf(AW4(x)) +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AClampH1(AH1 x,AH1 n,AH1 m){return clamp(x,n,m);} + AH2 AClampH2(AH2 x,AH2 n,AH2 m){return clamp(x,n,m);} + AH3 AClampH3(AH3 x,AH3 n,AH3 m){return clamp(x,n,m);} + AH4 AClampH4(AH4 x,AH4 n,AH4 m){return clamp(x,n,m);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFractH1(AH1 x){return fract(x);} + AH2 AFractH2(AH2 x){return fract(x);} + AH3 AFractH3(AH3 x){return fract(x);} + AH4 AFractH4(AH4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return mix(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return mix(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return mix(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of max3. + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of min3. + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return AH1_(1.0)/x;} + AH2 ARcpH2(AH2 x){return AH2_(1.0)/x;} + AH3 ARcpH3(AH3 x){return AH3_(1.0)/x;} + AH4 ARcpH4(AH4 x){return AH4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return AH1_(1.0)/sqrt(x);} + AH2 ARsqH2(AH2 x){return AH2_(1.0)/sqrt(x);} + AH3 ARsqH3(AH3 x){return AH3_(1.0)/sqrt(x);} + AH4 ARsqH4(AH4 x){return AH4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return clamp(x,AH1_(0.0),AH1_(1.0));} + AH2 ASatH2(AH2 x){return clamp(x,AH2_(0.0),AH2_(1.0));} + AH3 ASatH3(AH3 x){return clamp(x,AH3_(0.0),AH3_(1.0));} + AH4 ASatH4(AH4 x){return clamp(x,AH4_(0.0),AH4_(1.0));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #define AD1 double + #define AD2 dvec2 + #define AD3 dvec3 + #define AD4 dvec4 +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 x){return fract(x);} + AD2 AFractD2(AD2 x){return fract(x);} + AD3 AFractD3(AD3 x){return fract(x);} + AD4 AFractD4(AD4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return mix(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return mix(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return mix(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return AD1_(1.0)/x;} + AD2 ARcpD2(AD2 x){return AD2_(1.0)/x;} + AD3 ARcpD3(AD3 x){return AD3_(1.0)/x;} + AD4 ARcpD4(AD4 x){return AD4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return AD1_(1.0)/sqrt(x);} + AD2 ARsqD2(AD2 x){return AD2_(1.0)/sqrt(x);} + AD3 ARsqD3(AD3 x){return AD3_(1.0)/sqrt(x);} + AD4 ARsqD4(AD4 x){return AD4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return clamp(x,AD1_(0.0),AD1_(1.0));} + AD2 ASatD2(AD2 x){return clamp(x,AD2_(0.0),AD2_(1.0));} + AD3 ASatD3(AD3 x){return clamp(x,AD3_(0.0),AD3_(1.0));} + AD4 ASatD4(AD4 x){return clamp(x,AD4_(0.0),AD4_(1.0));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL LONG +//============================================================================================================================== + #ifdef A_LONG + #define AL1 uint64_t + #define AL2 u64vec2 + #define AL3 u64vec3 + #define AL4 u64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1 int64_t + #define ASL2 i64vec2 + #define ASL3 i64vec3 + #define ASL4 i64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AL1_AU2(x) packUint2x32(AU2(x)) + #define AU2_AL1(x) unpackUint2x32(AL1(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AL1_x(AL1 a){return AL1(a);} + AL2 AL2_x(AL1 a){return AL2(a,a);} + AL3 AL3_x(AL1 a){return AL3(a,a,a);} + AL4 AL4_x(AL1 a){return AL4(a,a,a,a);} + #define AL1_(a) AL1_x(AL1(a)) + #define AL2_(a) AL2_x(AL1(a)) + #define AL3_(a) AL3_x(AL1(a)) + #define AL4_(a) AL4_x(AL1(a)) +//============================================================================================================================== + AL1 AAbsSL1(AL1 a){return AL1(abs(ASL1(a)));} + AL2 AAbsSL2(AL2 a){return AL2(abs(ASL2(a)));} + AL3 AAbsSL3(AL3 a){return AL3(abs(ASL3(a)));} + AL4 AAbsSL4(AL4 a){return AL4(abs(ASL4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMaxSL1(AL1 a,AL1 b){return AL1(max(ASU1(a),ASU1(b)));} + AL2 AMaxSL2(AL2 a,AL2 b){return AL2(max(ASU2(a),ASU2(b)));} + AL3 AMaxSL3(AL3 a,AL3 b){return AL3(max(ASU3(a),ASU3(b)));} + AL4 AMaxSL4(AL4 a,AL4 b){return AL4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMinSL1(AL1 a,AL1 b){return AL1(min(ASU1(a),ASU1(b)));} + AL2 AMinSL2(AL2 a,AL2 b){return AL2(min(ASU2(a),ASU2(b)));} + AL3 AMinSL3(AL3 a,AL3 b){return AL3(min(ASU3(a),ASU3(b)));} + AL4 AMinSL4(AL4 a,AL4 b){return AL4(min(ASU4(a),ASU4(b)));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// WAVE OPERATIONS +//============================================================================================================================== + #ifdef A_WAVE + // Where 'x' must be a compile time literal. + AF1 AWaveXorF1(AF1 v,AU1 x){return subgroupShuffleXor(v,x);} + AF2 AWaveXorF2(AF2 v,AU1 x){return subgroupShuffleXor(v,x);} + AF3 AWaveXorF3(AF3 v,AU1 x){return subgroupShuffleXor(v,x);} + AF4 AWaveXorF4(AF4 v,AU1 x){return subgroupShuffleXor(v,x);} + AU1 AWaveXorU1(AU1 v,AU1 x){return subgroupShuffleXor(v,x);} + AU2 AWaveXorU2(AU2 v,AU1 x){return subgroupShuffleXor(v,x);} + AU3 AWaveXorU3(AU3 v,AU1 x){return subgroupShuffleXor(v,x);} + AU4 AWaveXorU4(AU4 v,AU1 x){return subgroupShuffleXor(v,x);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AH2 AWaveXorH2(AH2 v,AU1 x){return AH2_AU1(subgroupShuffleXor(AU1_AH2(v),x));} + AH4 AWaveXorH4(AH4 v,AU1 x){return AH4_AU2(subgroupShuffleXor(AU2_AH4(v),x));} + AW2 AWaveXorW2(AW2 v,AU1 x){return AW2_AU1(subgroupShuffleXor(AU1_AW2(v),x));} + AW4 AWaveXorW4(AW4 v,AU1 x){return AW4_AU2(subgroupShuffleXor(AU2_AW4(v),x));} + #endif + #endif +//============================================================================================================================== +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// HLSL +// +// +//============================================================================================================================== +#if defined(A_HLSL) && defined(A_GPU) + #ifdef A_HLSL_6_2 + #define AP1 bool + #define AP2 bool2 + #define AP3 bool3 + #define AP4 bool4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float32_t + #define AF2 float32_t2 + #define AF3 float32_t3 + #define AF4 float32_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint32_t + #define AU2 uint32_t2 + #define AU3 uint32_t3 + #define AU4 uint32_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int32_t + #define ASU2 int32_t2 + #define ASU3 int32_t3 + #define ASU4 int32_t4 + #else + #define AP1 bool + #define AP2 bool2 + #define AP3 bool3 + #define AP4 bool4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 float2 + #define AF3 float3 + #define AF4 float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uint2 + #define AU3 uint3 + #define AU4 uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 int2 + #define ASU3 int3 + #define ASU4 int4 + #endif +//============================================================================================================================== + #define AF1_AU1(x) asfloat(AU1(x)) + #define AF2_AU2(x) asfloat(AU2(x)) + #define AF3_AU3(x) asfloat(AU3(x)) + #define AF4_AU4(x) asfloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) asuint(AF1(x)) + #define AU2_AF2(x) asuint(AF2(x)) + #define AU3_AF3(x) asuint(AF3(x)) + #define AU4_AF4(x) asuint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH1_AF1_x(AF1 a){return f32tof16(a);} + #define AU1_AH1_AF1(a) AU1_AH1_AF1_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_AF2_x(AF2 a){return f32tof16(a.x)|(f32tof16(a.y)<<16);} + #define AU1_AH2_AF2(a) AU1_AH2_AF2_x(AF2(a)) + #define AU1_AB4Unorm_AF4(x) D3DCOLORtoUBYTE4(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AF2 AF2_AH2_AU1_x(AU1 x){return AF2(f16tof32(x&0xFFFF),f16tof32(x>>16));} + #define AF2_AH2_AU1(x) AF2_AH2_AU1_x(AU1(x)) +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){AU1 mask=(1u<>off)&mask;} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + AU1 ABfiM(AU1 src,AU1 ins,AU1 bits){AU1 mask=(1u<>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #ifdef A_HLSL_6_2 + #define AH1 float16_t + #define AH2 float16_t2 + #define AH3 float16_t3 + #define AH4 float16_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 uint16_t + #define AW2 uint16_t2 + #define AW3 uint16_t3 + #define AW4 uint16_t4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 int16_t + #define ASW2 int16_t2 + #define ASW3 int16_t3 + #define ASW4 int16_t4 + #else + #define AH1 min16float + #define AH2 min16float2 + #define AH3 min16float3 + #define AH4 min16float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 min16uint + #define AW2 min16uint2 + #define AW3 min16uint3 + #define AW4 min16uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 min16int + #define ASW2 min16int2 + #define ASW3 min16int3 + #define ASW4 min16int4 + #endif +//============================================================================================================================== + // Need to use manual unpack to get optimal execution (don't use packed types in buffers directly). + // Unpack requires this pattern: https://gpuopen.com/first-steps-implementing-fp16/ + AH2 AH2_AU1_x(AU1 x){AF2 t=f16tof32(AU2(x&0xFFFF,x>>16));return AH2(t);} + AH4 AH4_AU2_x(AU2 x){return AH4(AH2_AU1_x(x.x),AH2_AU1_x(x.y));} + AW2 AW2_AU1_x(AU1 x){AU2 t=AU2(x&0xFFFF,x>>16);return AW2(t);} + AW4 AW4_AU2_x(AU2 x){return AW4(AW2_AU1_x(x.x),AW2_AU1_x(x.y));} + #define AH2_AU1(x) AH2_AU1_x(AU1(x)) + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) AW2_AU1_x(AU1(x)) + #define AW4_AU2(x) AW4_AU2_x(AU2(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_x(AH2 x){return f32tof16(x.x)+(f32tof16(x.y)<<16);} + AU2 AU2_AH4_x(AH4 x){return AU2(AU1_AH2_x(x.xy),AU1_AH2_x(x.zw));} + AU1 AU1_AW2_x(AW2 x){return AU1(x.x)+(AU1(x.y)<<16);} + AU2 AU2_AW4_x(AW4 x){return AU2(AU1_AW2_x(x.xy),AU1_AW2_x(x.zw));} + #define AU1_AH2(x) AU1_AH2_x(AH2(x)) + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) AU1_AW2_x(AW2(x)) + #define AU2_AW4(x) AU2_AW4_x(AW4(x)) +//============================================================================================================================== + #if defined(A_HLSL_6_2) && !defined(A_NO_16_BIT_CAST) + #define AW1_AH1(x) asuint16(x) + #define AW2_AH2(x) asuint16(x) + #define AW3_AH3(x) asuint16(x) + #define AW4_AH4(x) asuint16(x) + #else + #define AW1_AH1(a) AW1(f32tof16(AF1(a))) + #define AW2_AH2(a) AW2(AW1_AH1((a).x),AW1_AH1((a).y)) + #define AW3_AH3(a) AW3(AW1_AH1((a).x),AW1_AH1((a).y),AW1_AH1((a).z)) + #define AW4_AH4(a) AW4(AW1_AH1((a).x),AW1_AH1((a).y),AW1_AH1((a).z),AW1_AH1((a).w)) + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #if defined(A_HLSL_6_2) && !defined(A_NO_16_BIT_CAST) + #define AH1_AW1(x) asfloat16(x) + #define AH2_AW2(x) asfloat16(x) + #define AH3_AW3(x) asfloat16(x) + #define AH4_AW4(x) asfloat16(x) + #else + #define AH1_AW1(a) AH1(f16tof32(AU1(a))) + #define AH2_AW2(a) AH2(AH1_AW1((a).x),AH1_AW1((a).y)) + #define AH3_AW3(a) AH3(AH1_AW1((a).x),AH1_AW1((a).y),AH1_AW1((a).z)) + #define AH4_AW4(a) AH4(AH1_AW1((a).x),AH1_AW1((a).y),AH1_AW1((a).z),AH1_AW1((a).w)) + #endif +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AClampH1(AH1 x,AH1 n,AH1 m){return max(n,min(x,m));} + AH2 AClampH2(AH2 x,AH2 n,AH2 m){return max(n,min(x,m));} + AH3 AClampH3(AH3 x,AH3 n,AH3 m){return max(n,min(x,m));} + AH4 AClampH4(AH4 x,AH4 n,AH4 m){return max(n,min(x,m));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_FRACT_F16 (note DX frac() is different). + AH1 AFractH1(AH1 x){return x-floor(x);} + AH2 AFractH2(AH2 x){return x-floor(x);} + AH3 AFractH3(AH3 x){return x-floor(x);} + AH4 AFractH4(AH4 x){return x-floor(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return lerp(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return lerp(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return lerp(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return rcp(x);} + AH2 ARcpH2(AH2 x){return rcp(x);} + AH3 ARcpH3(AH3 x){return rcp(x);} + AH4 ARcpH4(AH4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return rsqrt(x);} + AH2 ARsqH2(AH2 x){return rsqrt(x);} + AH3 ARsqH3(AH3 x){return rsqrt(x);} + AH4 ARsqH4(AH4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return saturate(x);} + AH2 ASatH2(AH2 x){return saturate(x);} + AH3 ASatH3(AH3 x){return saturate(x);} + AH4 ASatH4(AH4 x){return saturate(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #ifdef A_HLSL_6_2 + #define AD1 float64_t + #define AD2 float64_t2 + #define AD3 float64_t3 + #define AD4 float64_t4 + #else + #define AD1 double + #define AD2 double2 + #define AD3 double3 + #define AD4 double4 + #endif +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 a){return a-floor(a);} + AD2 AFractD2(AD2 a){return a-floor(a);} + AD3 AFractD3(AD3 a){return a-floor(a);} + AD4 AFractD4(AD4 a){return a-floor(a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return lerp(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return lerp(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return lerp(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return rcp(x);} + AD2 ARcpD2(AD2 x){return rcp(x);} + AD3 ARcpD3(AD3 x){return rcp(x);} + AD4 ARcpD4(AD4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return rsqrt(x);} + AD2 ARsqD2(AD2 x){return rsqrt(x);} + AD3 ARsqD3(AD3 x){return rsqrt(x);} + AD4 ARsqD4(AD4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return saturate(x);} + AD2 ASatD2(AD2 x){return saturate(x);} + AD3 ASatD3(AD3 x){return saturate(x);} + AD4 ASatD4(AD4 x){return saturate(x);} + #endif +//============================================================================================================================== +// HLSL WAVE +//============================================================================================================================== + #ifdef A_WAVE + // Where 'x' must be a compile time literal. + AF1 AWaveXorF1(AF1 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF2 AWaveXorF2(AF2 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF3 AWaveXorF3(AF3 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AF4 AWaveXorF4(AF4 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU1 AWaveXorU1(AU1 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU2 AWaveXorU1(AU2 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU3 AWaveXorU1(AU3 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} + AU4 AWaveXorU1(AU4 v,AU1 x){return WaveReadLaneAt(v,WaveGetLaneIndex()^x);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AH2 AWaveXorH2(AH2 v,AU1 x){return AH2_AU1(WaveReadLaneAt(AU1_AH2(v),WaveGetLaneIndex()^x));} + AH4 AWaveXorH4(AH4 v,AU1 x){return AH4_AU2(WaveReadLaneAt(AU2_AH4(v),WaveGetLaneIndex()^x));} + AW2 AWaveXorW2(AW2 v,AU1 x){return AW2_AU1(WaveReadLaneAt(AU1_AW2(v),WaveGetLaneIndex()^x));} + AW4 AWaveXorW4(AW4 v,AU1 x){return AW4_AU1(WaveReadLaneAt(AU1_AW4(v),WaveGetLaneIndex()^x));} + #endif + #endif +//============================================================================================================================== +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GPU COMMON +// +// +//============================================================================================================================== +#ifdef A_GPU + // Negative and positive infinity. + #define A_INFP_F AF1_AU1(0x7f800000u) + #define A_INFN_F AF1_AU1(0xff800000u) +//------------------------------------------------------------------------------------------------------------------------------ + // Copy sign from 's' to positive 'd'. + AF1 ACpySgnF1(AF1 d,AF1 s){return AF1_AU1(AU1_AF1(d)|(AU1_AF1(s)&AU1_(0x80000000u)));} + AF2 ACpySgnF2(AF2 d,AF2 s){return AF2_AU2(AU2_AF2(d)|(AU2_AF2(s)&AU2_(0x80000000u)));} + AF3 ACpySgnF3(AF3 d,AF3 s){return AF3_AU3(AU3_AF3(d)|(AU3_AF3(s)&AU3_(0x80000000u)));} + AF4 ACpySgnF4(AF4 d,AF4 s){return AF4_AU4(AU4_AF4(d)|(AU4_AF4(s)&AU4_(0x80000000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Single operation to return (useful to create a mask to use in lerp for branch free logic), + // m=NaN := 0 + // m>=0 := 0 + // m<0 := 1 + // Uses the following useful floating point logic, + // saturate(+a*(-INF)==-INF) := 0 + // saturate( 0*(-INF)== NaN) := 0 + // saturate(-a*(-INF)==+INF) := 1 + AF1 ASignedF1(AF1 m){return ASatF1(m*AF1_(A_INFN_F));} + AF2 ASignedF2(AF2 m){return ASatF2(m*AF2_(A_INFN_F));} + AF3 ASignedF3(AF3 m){return ASatF3(m*AF3_(A_INFN_F));} + AF4 ASignedF4(AF4 m){return ASatF4(m*AF4_(A_INFN_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AGtZeroF1(AF1 m){return ASatF1(m*AF1_(A_INFP_F));} + AF2 AGtZeroF2(AF2 m){return ASatF2(m*AF2_(A_INFP_F));} + AF3 AGtZeroF3(AF3 m){return ASatF3(m*AF3_(A_INFP_F));} + AF4 AGtZeroF4(AF4 m){return ASatF4(m*AF4_(A_INFP_F));} +//============================================================================================================================== + #ifdef A_HALF + #ifdef A_HLSL_6_2 + #define A_INFP_H AH1_AW1((uint16_t)0x7c00u) + #define A_INFN_H AH1_AW1((uint16_t)0xfc00u) + #else + #define A_INFP_H AH1_AW1(0x7c00u) + #define A_INFN_H AH1_AW1(0xfc00u) + #endif + +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ACpySgnH1(AH1 d,AH1 s){return AH1_AW1(AW1_AH1(d)|(AW1_AH1(s)&AW1_(0x8000u)));} + AH2 ACpySgnH2(AH2 d,AH2 s){return AH2_AW2(AW2_AH2(d)|(AW2_AH2(s)&AW2_(0x8000u)));} + AH3 ACpySgnH3(AH3 d,AH3 s){return AH3_AW3(AW3_AH3(d)|(AW3_AH3(s)&AW3_(0x8000u)));} + AH4 ACpySgnH4(AH4 d,AH4 s){return AH4_AW4(AW4_AH4(d)|(AW4_AH4(s)&AW4_(0x8000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASignedH1(AH1 m){return ASatH1(m*AH1_(A_INFN_H));} + AH2 ASignedH2(AH2 m){return ASatH2(m*AH2_(A_INFN_H));} + AH3 ASignedH3(AH3 m){return ASatH3(m*AH3_(A_INFN_H));} + AH4 ASignedH4(AH4 m){return ASatH4(m*AH4_(A_INFN_H));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AGtZeroH1(AH1 m){return ASatH1(m*AH1_(A_INFP_H));} + AH2 AGtZeroH2(AH2 m){return ASatH2(m*AH2_(A_INFP_H));} + AH3 AGtZeroH3(AH3 m){return ASatH3(m*AH3_(A_INFP_H));} + AH4 AGtZeroH4(AH4 m){return ASatH4(m*AH4_(A_INFP_H));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [FIS] FLOAT INTEGER SORTABLE +//------------------------------------------------------------------------------------------------------------------------------ +// Float to integer sortable. +// - If sign bit=0, flip the sign bit (positives). +// - If sign bit=1, flip all bits (negatives). +// Integer sortable to float. +// - If sign bit=1, flip the sign bit (positives). +// - If sign bit=0, flip all bits (negatives). +// Has nice side effects. +// - Larger integers are more positive values. +// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage). +// Burns 3 ops for conversion {shift,or,xor}. +//============================================================================================================================== + AU1 AFisToU1(AU1 x){return x^(( AShrSU1(x,AU1_(31)))|AU1_(0x80000000));} + AU1 AFisFromU1(AU1 x){return x^((~AShrSU1(x,AU1_(31)))|AU1_(0x80000000));} +//------------------------------------------------------------------------------------------------------------------------------ + // Just adjust high 16-bit value (useful when upper part of 32-bit word is a 16-bit float value). + AU1 AFisToHiU1(AU1 x){return x^(( AShrSU1(x,AU1_(15)))|AU1_(0x80000000));} + AU1 AFisFromHiU1(AU1 x){return x^((~AShrSU1(x,AU1_(15)))|AU1_(0x80000000));} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + AW1 AFisToW1(AW1 x){return x^(( AShrSW1(x,AW1_(15)))|AW1_(0x8000));} + AW1 AFisFromW1(AW1 x){return x^((~AShrSW1(x,AW1_(15)))|AW1_(0x8000));} +//------------------------------------------------------------------------------------------------------------------------------ + AW2 AFisToW2(AW2 x){return x^(( AShrSW2(x,AW2_(15)))|AW2_(0x8000));} + AW2 AFisFromW2(AW2 x){return x^((~AShrSW2(x,AW2_(15)))|AW2_(0x8000));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [PERM] V_PERM_B32 +//------------------------------------------------------------------------------------------------------------------------------ +// Support for V_PERM_B32 started in the 3rd generation of GCN. +//------------------------------------------------------------------------------------------------------------------------------ +// yyyyxxxx - The 'i' input. +// 76543210 +// ======== +// HGFEDCBA - Naming on permutation. +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Make sure compiler optimizes this. +//============================================================================================================================== + #ifdef A_HALF + AU1 APerm0E0A(AU2 i){return((i.x )&0xffu)|((i.y<<16)&0xff0000u);} + AU1 APerm0F0B(AU2 i){return((i.x>> 8)&0xffu)|((i.y<< 8)&0xff0000u);} + AU1 APerm0G0C(AU2 i){return((i.x>>16)&0xffu)|((i.y )&0xff0000u);} + AU1 APerm0H0D(AU2 i){return((i.x>>24)&0xffu)|((i.y>> 8)&0xff0000u);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 APermHGFA(AU2 i){return((i.x )&0x000000ffu)|(i.y&0xffffff00u);} + AU1 APermHGFC(AU2 i){return((i.x>>16)&0x000000ffu)|(i.y&0xffffff00u);} + AU1 APermHGAE(AU2 i){return((i.x<< 8)&0x0000ff00u)|(i.y&0xffff00ffu);} + AU1 APermHGCE(AU2 i){return((i.x>> 8)&0x0000ff00u)|(i.y&0xffff00ffu);} + AU1 APermHAFE(AU2 i){return((i.x<<16)&0x00ff0000u)|(i.y&0xff00ffffu);} + AU1 APermHCFE(AU2 i){return((i.x )&0x00ff0000u)|(i.y&0xff00ffffu);} + AU1 APermAGFE(AU2 i){return((i.x<<24)&0xff000000u)|(i.y&0x00ffffffu);} + AU1 APermCGFE(AU2 i){return((i.x<< 8)&0xff000000u)|(i.y&0x00ffffffu);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 APermGCEA(AU2 i){return((i.x)&0x00ff00ffu)|((i.y<<8)&0xff00ff00u);} + AU1 APermGECA(AU2 i){return(((i.x)&0xffu)|((i.x>>8)&0xff00u)|((i.y<<16)&0xff0000u)|((i.y<<8)&0xff000000u));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [BUC] BYTE UNSIGNED CONVERSION +//------------------------------------------------------------------------------------------------------------------------------ +// Designed to use the optimal conversion, enables the scaling to possibly be factored into other computation. +// Works on a range of {0 to A_BUC_<32,16>}, for <32-bit, and 16-bit> respectively. +//------------------------------------------------------------------------------------------------------------------------------ +// OPCODE NOTES +// ============ +// GCN does not do UNORM or SNORM for bytes in opcodes. +// - V_CVT_F32_UBYTE{0,1,2,3} - Unsigned byte to float. +// - V_CVT_PKACC_U8_F32 - Float to unsigned byte (does bit-field insert into 32-bit integer). +// V_PERM_B32 does byte packing with ability to zero fill bytes as well. +// - Can pull out byte values from two sources, and zero fill upper 8-bits of packed hi and lo. +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABuc{0,1,2,3}{To,From}U1() - Designed for V_CVT_F32_UBYTE* and V_CVT_PKACCUM_U8_F32 ops. +// ==== ===== +// 0 : 0 +// 1 : 1 +// ... +// 255 : 255 +// : 256 (just outside the encoding range) +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABuc{0,1,2,3}{To,From}U2() - Designed for 16-bit denormal tricks and V_PERM_B32. +// ==== ===== +// 0 : 0 +// 1 : 1/512 +// 2 : 1/256 +// ... +// 64 : 1/8 +// 128 : 1/4 +// 255 : 255/512 +// : 1/2 (just outside the encoding range) +//------------------------------------------------------------------------------------------------------------------------------ +// OPTIMAL IMPLEMENTATIONS ON AMD ARCHITECTURES +// ============================================ +// r=ABuc0FromU1(i) +// V_CVT_F32_UBYTE0 r,i +// -------------------------------------------- +// r=ABuc0ToU1(d,i) +// V_CVT_PKACCUM_U8_F32 r,i,0,d +// -------------------------------------------- +// d=ABuc0FromU2(i) +// Where 'k0' is an SGPR with 0x0E0A +// Where 'k1' is an SGPR with {32768.0} packed into the lower 16-bits +// V_PERM_B32 d,i.x,i.y,k0 +// V_PK_FMA_F16 d,d,k1.x,0 +// -------------------------------------------- +// r=ABuc0ToU2(d,i) +// Where 'k0' is an SGPR with {1.0/32768.0} packed into the lower 16-bits +// Where 'k1' is an SGPR with 0x???? +// Where 'k2' is an SGPR with 0x???? +// V_PK_FMA_F16 i,i,k0.x,0 +// V_PERM_B32 r.x,i,i,k1 +// V_PERM_B32 r.y,i,i,k2 +//============================================================================================================================== + // Peak range for 32-bit and 16-bit operations. + #define A_BUC_32 (255.0) + #define A_BUC_16 (255.0/512.0) +//============================================================================================================================== + #if 1 + // Designed to be one V_CVT_PKACCUM_U8_F32. + // The extra min is required to pattern match to V_CVT_PKACCUM_U8_F32. + AU1 ABuc0ToU1(AU1 d,AF1 i){return (d&0xffffff00u)|((min(AU1(i),255u) )&(0x000000ffu));} + AU1 ABuc1ToU1(AU1 d,AF1 i){return (d&0xffff00ffu)|((min(AU1(i),255u)<< 8)&(0x0000ff00u));} + AU1 ABuc2ToU1(AU1 d,AF1 i){return (d&0xff00ffffu)|((min(AU1(i),255u)<<16)&(0x00ff0000u));} + AU1 ABuc3ToU1(AU1 d,AF1 i){return (d&0x00ffffffu)|((min(AU1(i),255u)<<24)&(0xff000000u));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed to be one V_CVT_F32_UBYTE*. + AF1 ABuc0FromU1(AU1 i){return AF1((i )&255u);} + AF1 ABuc1FromU1(AU1 i){return AF1((i>> 8)&255u);} + AF1 ABuc2FromU1(AU1 i){return AF1((i>>16)&255u);} + AF1 ABuc3FromU1(AU1 i){return AF1((i>>24)&255u);} + #endif +//============================================================================================================================== + #ifdef A_HALF + // Takes {x0,x1} and {y0,y1} and builds {{x0,y0},{x1,y1}}. + AW2 ABuc01ToW2(AH2 x,AH2 y){x*=AH2_(1.0/32768.0);y*=AH2_(1.0/32768.0); + return AW2_AU1(APermGCEA(AU2(AU1_AW2(AW2_AH2(x)),AU1_AW2(AW2_AH2(y)))));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed for 3 ops to do SOA to AOS and conversion. + AU2 ABuc0ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABuc1ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABuc2ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABuc3ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0))); + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Designed for 2 ops to do both AOS to SOA, and conversion. + AH2 ABuc0FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)))*AH2_(32768.0);} + AH2 ABuc1FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)))*AH2_(32768.0);} + AH2 ABuc2FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)))*AH2_(32768.0);} + AH2 ABuc3FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)))*AH2_(32768.0);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [BSC] BYTE SIGNED CONVERSION +//------------------------------------------------------------------------------------------------------------------------------ +// Similar to [BUC]. +// Works on a range of {-/+ A_BSC_<32,16>}, for <32-bit, and 16-bit> respectively. +//------------------------------------------------------------------------------------------------------------------------------ +// ENCODING (without zero-based encoding) +// ======== +// 0 = unused (can be used to mean something else) +// 1 = lowest value +// 128 = exact zero center (zero based encoding +// 255 = highest value +//------------------------------------------------------------------------------------------------------------------------------ +// Zero-based [Zb] flips the MSB bit of the byte (making 128 "exact zero" actually zero). +// This is useful if there is a desire for cleared values to decode as zero. +//------------------------------------------------------------------------------------------------------------------------------ +// BYTE : FLOAT - ABsc{0,1,2,3}{To,From}U2() - Designed for 16-bit denormal tricks and V_PERM_B32. +// ==== ===== +// 0 : -127/512 (unused) +// 1 : -126/512 +// 2 : -125/512 +// ... +// 128 : 0 +// ... +// 255 : 127/512 +// : 1/4 (just outside the encoding range) +//============================================================================================================================== + // Peak range for 32-bit and 16-bit operations. + #define A_BSC_32 (127.0) + #define A_BSC_16 (127.0/512.0) +//============================================================================================================================== + #if 1 + AU1 ABsc0ToU1(AU1 d,AF1 i){return (d&0xffffff00u)|((min(AU1(i+128.0),255u) )&(0x000000ffu));} + AU1 ABsc1ToU1(AU1 d,AF1 i){return (d&0xffff00ffu)|((min(AU1(i+128.0),255u)<< 8)&(0x0000ff00u));} + AU1 ABsc2ToU1(AU1 d,AF1 i){return (d&0xff00ffffu)|((min(AU1(i+128.0),255u)<<16)&(0x00ff0000u));} + AU1 ABsc3ToU1(AU1 d,AF1 i){return (d&0x00ffffffu)|((min(AU1(i+128.0),255u)<<24)&(0xff000000u));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABsc0ToZbU1(AU1 d,AF1 i){return ((d&0xffffff00u)|((min(AU1(trunc(i)+128.0),255u) )&(0x000000ffu)))^0x00000080u;} + AU1 ABsc1ToZbU1(AU1 d,AF1 i){return ((d&0xffff00ffu)|((min(AU1(trunc(i)+128.0),255u)<< 8)&(0x0000ff00u)))^0x00008000u;} + AU1 ABsc2ToZbU1(AU1 d,AF1 i){return ((d&0xff00ffffu)|((min(AU1(trunc(i)+128.0),255u)<<16)&(0x00ff0000u)))^0x00800000u;} + AU1 ABsc3ToZbU1(AU1 d,AF1 i){return ((d&0x00ffffffu)|((min(AU1(trunc(i)+128.0),255u)<<24)&(0xff000000u)))^0x80000000u;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ABsc0FromU1(AU1 i){return AF1((i )&255u)-128.0;} + AF1 ABsc1FromU1(AU1 i){return AF1((i>> 8)&255u)-128.0;} + AF1 ABsc2FromU1(AU1 i){return AF1((i>>16)&255u)-128.0;} + AF1 ABsc3FromU1(AU1 i){return AF1((i>>24)&255u)-128.0;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ABsc0FromZbU1(AU1 i){return AF1(((i )&255u)^0x80u)-128.0;} + AF1 ABsc1FromZbU1(AU1 i){return AF1(((i>> 8)&255u)^0x80u)-128.0;} + AF1 ABsc2FromZbU1(AU1 i){return AF1(((i>>16)&255u)^0x80u)-128.0;} + AF1 ABsc3FromZbU1(AU1 i){return AF1(((i>>24)&255u)^0x80u)-128.0;} + #endif +//============================================================================================================================== + #ifdef A_HALF + // Takes {x0,x1} and {y0,y1} and builds {{x0,y0},{x1,y1}}. + AW2 ABsc01ToW2(AH2 x,AH2 y){x=x*AH2_(1.0/32768.0)+AH2_(0.25/32768.0);y=y*AH2_(1.0/32768.0)+AH2_(0.25/32768.0); + return AW2_AU1(APermGCEA(AU2(AU1_AW2(AW2_AH2(x)),AU1_AW2(AW2_AH2(y)))));} +//------------------------------------------------------------------------------------------------------------------------------ + AU2 ABsc0ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABsc1ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABsc2ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABsc3ToU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0))); + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU2 ABsc0ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHGFA(AU2(d.x,b)),APermHGFC(AU2(d.y,b)));} + AU2 ABsc1ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHGAE(AU2(d.x,b)),APermHGCE(AU2(d.y,b)));} + AU2 ABsc2ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermHAFE(AU2(d.x,b)),APermHCFE(AU2(d.y,b)));} + AU2 ABsc3ToZbU2(AU2 d,AH2 i){AU1 b=AU1_AW2(AW2_AH2(i*AH2_(1.0/32768.0)+AH2_(0.25/32768.0)))^0x00800080u; + return AU2(APermAGFE(AU2(d.x,b)),APermCGFE(AU2(d.y,b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 ABsc0FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc1FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc2FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc3FromU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)))*AH2_(32768.0)-AH2_(0.25);} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 ABsc0FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0E0A(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc1FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0F0B(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc2FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0G0C(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + AH2 ABsc3FromZbU2(AU2 i){return AH2_AW2(AW2_AU1(APerm0H0D(i)^0x00800080u))*AH2_(32768.0)-AH2_(0.25);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These support only positive inputs. +// Did not see value yet in specialization for range. +// Using quick testing, ended up mostly getting the same "best" approximation for various ranges. +// With hardware that can co-execute transcendentals, the value in approximations could be less than expected. +// However from a latency perspective, if execution of a transcendental is 4 clk, with no packed support, -> 8 clk total. +// And co-execution would require a compiler interleaving a lot of independent work for packed usage. +//------------------------------------------------------------------------------------------------------------------------------ +// The one Newton Raphson iteration form of rsq() was skipped (requires 6 ops total). +// Same with sqrt(), as this could be x*rsq() (7 ops). +//============================================================================================================================== + #ifdef A_HALF + // Minimize squared error across full positive range, 2 ops. + // The 0x1de2 based approximation maps {0 to 1} input maps to < 1 output. + AH1 APrxLoSqrtH1(AH1 a){return AH1_AW1((AW1_AH1(a)>>AW1_(1))+AW1_(0x1de2));} + AH2 APrxLoSqrtH2(AH2 a){return AH2_AW2((AW2_AH2(a)>>AW2_(1))+AW2_(0x1de2));} + AH3 APrxLoSqrtH3(AH3 a){return AH3_AW3((AW3_AH3(a)>>AW3_(1))+AW3_(0x1de2));} + AH4 APrxLoSqrtH4(AH4 a){return AH4_AW4((AW4_AH4(a)>>AW4_(1))+AW4_(0x1de2));} +//------------------------------------------------------------------------------------------------------------------------------ + // Lower precision estimation, 1 op. + // Minimize squared error across {smallest normal to 16384.0}. + AH1 APrxLoRcpH1(AH1 a){return AH1_AW1(AW1_(0x7784)-AW1_AH1(a));} + AH2 APrxLoRcpH2(AH2 a){return AH2_AW2(AW2_(0x7784)-AW2_AH2(a));} + AH3 APrxLoRcpH3(AH3 a){return AH3_AW3(AW3_(0x7784)-AW3_AH3(a));} + AH4 APrxLoRcpH4(AH4 a){return AH4_AW4(AW4_(0x7784)-AW4_AH4(a));} +//------------------------------------------------------------------------------------------------------------------------------ + // Medium precision estimation, one Newton Raphson iteration, 3 ops. + AH1 APrxMedRcpH1(AH1 a){AH1 b=AH1_AW1(AW1_(0x778d)-AW1_AH1(a));return b*(-b*a+AH1_(2.0));} + AH2 APrxMedRcpH2(AH2 a){AH2 b=AH2_AW2(AW2_(0x778d)-AW2_AH2(a));return b*(-b*a+AH2_(2.0));} + AH3 APrxMedRcpH3(AH3 a){AH3 b=AH3_AW3(AW3_(0x778d)-AW3_AH3(a));return b*(-b*a+AH3_(2.0));} + AH4 APrxMedRcpH4(AH4 a){AH4 b=AH4_AW4(AW4_(0x778d)-AW4_AH4(a));return b*(-b*a+AH4_(2.0));} +//------------------------------------------------------------------------------------------------------------------------------ + // Minimize squared error across {smallest normal to 16384.0}, 2 ops. + AH1 APrxLoRsqH1(AH1 a){return AH1_AW1(AW1_(0x59a3)-(AW1_AH1(a)>>AW1_(1)));} + AH2 APrxLoRsqH2(AH2 a){return AH2_AW2(AW2_(0x59a3)-(AW2_AH2(a)>>AW2_(1)));} + AH3 APrxLoRsqH3(AH3 a){return AH3_AW3(AW3_(0x59a3)-(AW3_AH3(a)>>AW3_(1)));} + AH4 APrxLoRsqH4(AH4 a){return AH4_AW4(AW4_(0x59a3)-(AW4_AH4(a)>>AW4_(1)));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// FLOAT APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// Michal Drobot has an excellent presentation on these: "Low Level Optimizations For GCN", +// - Idea dates back to SGI, then to Quake 3, etc. +// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +// - sqrt(x)=rsqrt(x)*x +// - rcp(x)=rsqrt(x)*rsqrt(x) for positive x +// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +//------------------------------------------------------------------------------------------------------------------------------ +// These below are from perhaps less complete searching for optimal. +// Used FP16 normal range for testing with +4096 32-bit step size for sampling error. +// So these match up well with the half approximations. +//============================================================================================================================== + AF1 APrxLoSqrtF1(AF1 a){return AF1_AU1((AU1_AF1(a)>>AU1_(1))+AU1_(0x1fbc4639));} + AF1 APrxLoRcpF1(AF1 a){return AF1_AU1(AU1_(0x7ef07ebb)-AU1_AF1(a));} + AF1 APrxMedRcpF1(AF1 a){AF1 b=AF1_AU1(AU1_(0x7ef19fff)-AU1_AF1(a));return b*(-b*a+AF1_(2.0));} + AF1 APrxLoRsqF1(AF1 a){return AF1_AU1(AU1_(0x5f347d74)-(AU1_AF1(a)>>AU1_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 APrxLoSqrtF2(AF2 a){return AF2_AU2((AU2_AF2(a)>>AU2_(1))+AU2_(0x1fbc4639));} + AF2 APrxLoRcpF2(AF2 a){return AF2_AU2(AU2_(0x7ef07ebb)-AU2_AF2(a));} + AF2 APrxMedRcpF2(AF2 a){AF2 b=AF2_AU2(AU2_(0x7ef19fff)-AU2_AF2(a));return b*(-b*a+AF2_(2.0));} + AF2 APrxLoRsqF2(AF2 a){return AF2_AU2(AU2_(0x5f347d74)-(AU2_AF2(a)>>AU2_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF3 APrxLoSqrtF3(AF3 a){return AF3_AU3((AU3_AF3(a)>>AU3_(1))+AU3_(0x1fbc4639));} + AF3 APrxLoRcpF3(AF3 a){return AF3_AU3(AU3_(0x7ef07ebb)-AU3_AF3(a));} + AF3 APrxMedRcpF3(AF3 a){AF3 b=AF3_AU3(AU3_(0x7ef19fff)-AU3_AF3(a));return b*(-b*a+AF3_(2.0));} + AF3 APrxLoRsqF3(AF3 a){return AF3_AU3(AU3_(0x5f347d74)-(AU3_AF3(a)>>AU3_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF4 APrxLoSqrtF4(AF4 a){return AF4_AU4((AU4_AF4(a)>>AU4_(1))+AU4_(0x1fbc4639));} + AF4 APrxLoRcpF4(AF4 a){return AF4_AU4(AU4_(0x7ef07ebb)-AU4_AF4(a));} + AF4 APrxMedRcpF4(AF4 a){AF4 b=AF4_AU4(AU4_(0x7ef19fff)-AU4_AF4(a));return b*(-b*a+AF4_(2.0));} + AF4 APrxLoRsqF4(AF4 a){return AF4_AU4(AU4_(0x5f347d74)-(AU4_AF4(a)>>AU4_(1)));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PQ APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// PQ is very close to x^(1/8). The functions below Use the fast float approximation method to do +// PQ<~>Gamma2 (4th power and fast 4th root) and PQ<~>Linear (8th power and fast 8th root). Maximum error is ~0.2%. +//============================================================================================================================== +// Helpers + AF1 Quart(AF1 a) { a = a * a; return a * a;} + AF1 Oct(AF1 a) { a = a * a; a = a * a; return a * a; } + AF2 Quart(AF2 a) { a = a * a; return a * a; } + AF2 Oct(AF2 a) { a = a * a; a = a * a; return a * a; } + AF3 Quart(AF3 a) { a = a * a; return a * a; } + AF3 Oct(AF3 a) { a = a * a; a = a * a; return a * a; } + AF4 Quart(AF4 a) { a = a * a; return a * a; } + AF4 Oct(AF4 a) { a = a * a; a = a * a; return a * a; } + //------------------------------------------------------------------------------------------------------------------------------ + AF1 APrxPQToGamma2(AF1 a) { return Quart(a); } + AF1 APrxPQToLinear(AF1 a) { return Oct(a); } + AF1 APrxLoGamma2ToPQ(AF1 a) { return AF1_AU1((AU1_AF1(a) >> AU1_(2)) + AU1_(0x2F9A4E46)); } + AF1 APrxMedGamma2ToPQ(AF1 a) { AF1 b = AF1_AU1((AU1_AF1(a) >> AU1_(2)) + AU1_(0x2F9A4E46)); AF1 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF1 APrxHighGamma2ToPQ(AF1 a) { return sqrt(sqrt(a)); } + AF1 APrxLoLinearToPQ(AF1 a) { return AF1_AU1((AU1_AF1(a) >> AU1_(3)) + AU1_(0x378D8723)); } + AF1 APrxMedLinearToPQ(AF1 a) { AF1 b = AF1_AU1((AU1_AF1(a) >> AU1_(3)) + AU1_(0x378D8723)); AF1 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF1 APrxHighLinearToPQ(AF1 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF2 APrxPQToGamma2(AF2 a) { return Quart(a); } + AF2 APrxPQToLinear(AF2 a) { return Oct(a); } + AF2 APrxLoGamma2ToPQ(AF2 a) { return AF2_AU2((AU2_AF2(a) >> AU2_(2)) + AU2_(0x2F9A4E46)); } + AF2 APrxMedGamma2ToPQ(AF2 a) { AF2 b = AF2_AU2((AU2_AF2(a) >> AU2_(2)) + AU2_(0x2F9A4E46)); AF2 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF2 APrxHighGamma2ToPQ(AF2 a) { return sqrt(sqrt(a)); } + AF2 APrxLoLinearToPQ(AF2 a) { return AF2_AU2((AU2_AF2(a) >> AU2_(3)) + AU2_(0x378D8723)); } + AF2 APrxMedLinearToPQ(AF2 a) { AF2 b = AF2_AU2((AU2_AF2(a) >> AU2_(3)) + AU2_(0x378D8723)); AF2 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF2 APrxHighLinearToPQ(AF2 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF3 APrxPQToGamma2(AF3 a) { return Quart(a); } + AF3 APrxPQToLinear(AF3 a) { return Oct(a); } + AF3 APrxLoGamma2ToPQ(AF3 a) { return AF3_AU3((AU3_AF3(a) >> AU3_(2)) + AU3_(0x2F9A4E46)); } + AF3 APrxMedGamma2ToPQ(AF3 a) { AF3 b = AF3_AU3((AU3_AF3(a) >> AU3_(2)) + AU3_(0x2F9A4E46)); AF3 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF3 APrxHighGamma2ToPQ(AF3 a) { return sqrt(sqrt(a)); } + AF3 APrxLoLinearToPQ(AF3 a) { return AF3_AU3((AU3_AF3(a) >> AU3_(3)) + AU3_(0x378D8723)); } + AF3 APrxMedLinearToPQ(AF3 a) { AF3 b = AF3_AU3((AU3_AF3(a) >> AU3_(3)) + AU3_(0x378D8723)); AF3 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF3 APrxHighLinearToPQ(AF3 a) { return sqrt(sqrt(sqrt(a))); } + //------------------------------------------------------------------------------------------------------------------------------ + AF4 APrxPQToGamma2(AF4 a) { return Quart(a); } + AF4 APrxPQToLinear(AF4 a) { return Oct(a); } + AF4 APrxLoGamma2ToPQ(AF4 a) { return AF4_AU4((AU4_AF4(a) >> AU4_(2)) + AU4_(0x2F9A4E46)); } + AF4 APrxMedGamma2ToPQ(AF4 a) { AF4 b = AF4_AU4((AU4_AF4(a) >> AU4_(2)) + AU4_(0x2F9A4E46)); AF4 b4 = Quart(b); return b - b * (b4 - a) / (AF1_(4.0) * b4); } + AF4 APrxHighGamma2ToPQ(AF4 a) { return sqrt(sqrt(a)); } + AF4 APrxLoLinearToPQ(AF4 a) { return AF4_AU4((AU4_AF4(a) >> AU4_(3)) + AU4_(0x378D8723)); } + AF4 APrxMedLinearToPQ(AF4 a) { AF4 b = AF4_AU4((AU4_AF4(a) >> AU4_(3)) + AU4_(0x378D8723)); AF4 b8 = Oct(b); return b - b * (b8 - a) / (AF1_(8.0) * b8); } + AF4 APrxHighLinearToPQ(AF4 a) { return sqrt(sqrt(sqrt(a))); } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PARABOLIC SIN & COS +//------------------------------------------------------------------------------------------------------------------------------ +// Approximate answers to transcendental questions. +//------------------------------------------------------------------------------------------------------------------------------ +//============================================================================================================================== + #if 1 + // Valid input range is {-1 to 1} representing {0 to 2 pi}. + // Output range is {-1/4 to 1/4} representing {-1 to 1}. + AF1 APSinF1(AF1 x){return x*abs(x)-x;} // MAD. + AF2 APSinF2(AF2 x){return x*abs(x)-x;} + AF1 APCosF1(AF1 x){x=AFractF1(x*AF1_(0.5)+AF1_(0.75));x=x*AF1_(2.0)-AF1_(1.0);return APSinF1(x);} // 3x MAD, FRACT + AF2 APCosF2(AF2 x){x=AFractF2(x*AF2_(0.5)+AF2_(0.75));x=x*AF2_(2.0)-AF2_(1.0);return APSinF2(x);} + AF2 APSinCosF1(AF1 x){AF1 y=AFractF1(x*AF1_(0.5)+AF1_(0.75));y=y*AF1_(2.0)-AF1_(1.0);return APSinF2(AF2(x,y));} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + // For a packed {sin,cos} pair, + // - Native takes 16 clocks and 4 issue slots (no packed transcendentals). + // - Parabolic takes 8 clocks and 8 issue slots (only fract is non-packed). + AH1 APSinH1(AH1 x){return x*abs(x)-x;} + AH2 APSinH2(AH2 x){return x*abs(x)-x;} // AND,FMA + AH1 APCosH1(AH1 x){x=AFractH1(x*AH1_(0.5)+AH1_(0.75));x=x*AH1_(2.0)-AH1_(1.0);return APSinH1(x);} + AH2 APCosH2(AH2 x){x=AFractH2(x*AH2_(0.5)+AH2_(0.75));x=x*AH2_(2.0)-AH2_(1.0);return APSinH2(x);} // 3x FMA, 2xFRACT, AND + AH2 APSinCosH1(AH1 x){AH1 y=AFractH1(x*AH1_(0.5)+AH1_(0.75));y=y*AH1_(2.0)-AH1_(1.0);return APSinH2(AH2(x,y));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// [ZOL] ZERO ONE LOGIC +//------------------------------------------------------------------------------------------------------------------------------ +// Conditional free logic designed for easy 16-bit packing, and backwards porting to 32-bit. +//------------------------------------------------------------------------------------------------------------------------------ +// 0 := false +// 1 := true +//------------------------------------------------------------------------------------------------------------------------------ +// AndNot(x,y) -> !(x&y) .... One op. +// AndOr(x,y,z) -> (x&y)|z ... One op. +// GtZero(x) -> x>0.0 ..... One op. +// Sel(x,y,z) -> x?y:z ..... Two ops, has no precision loss. +// Signed(x) -> x<0.0 ..... One op. +// ZeroPass(x,y) -> x?0:y ..... Two ops, 'y' is a pass through safe for aliasing as integer. +//------------------------------------------------------------------------------------------------------------------------------ +// OPTIMIZATION NOTES +// ================== +// - On Vega to use 2 constants in a packed op, pass in as one AW2 or one AH2 'k.xy' and use as 'k.xx' and 'k.yy'. +// For example 'a.xy*k.xx+k.yy'. +//============================================================================================================================== + #if 1 + AU1 AZolAndU1(AU1 x,AU1 y){return min(x,y);} + AU2 AZolAndU2(AU2 x,AU2 y){return min(x,y);} + AU3 AZolAndU3(AU3 x,AU3 y){return min(x,y);} + AU4 AZolAndU4(AU4 x,AU4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AZolNotU1(AU1 x){return x^AU1_(1);} + AU2 AZolNotU2(AU2 x){return x^AU2_(1);} + AU3 AZolNotU3(AU3 x){return x^AU3_(1);} + AU4 AZolNotU4(AU4 x){return x^AU4_(1);} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AZolOrU1(AU1 x,AU1 y){return max(x,y);} + AU2 AZolOrU2(AU2 x,AU2 y){return max(x,y);} + AU3 AZolOrU3(AU3 x,AU3 y){return max(x,y);} + AU4 AZolOrU4(AU4 x,AU4 y){return max(x,y);} +//============================================================================================================================== + AU1 AZolF1ToU1(AF1 x){return AU1(x);} + AU2 AZolF2ToU2(AF2 x){return AU2(x);} + AU3 AZolF3ToU3(AF3 x){return AU3(x);} + AU4 AZolF4ToU4(AF4 x){return AU4(x);} +//------------------------------------------------------------------------------------------------------------------------------ + // 2 ops, denormals don't work in 32-bit on PC (and if they are enabled, OMOD is disabled). + AU1 AZolNotF1ToU1(AF1 x){return AU1(AF1_(1.0)-x);} + AU2 AZolNotF2ToU2(AF2 x){return AU2(AF2_(1.0)-x);} + AU3 AZolNotF3ToU3(AF3 x){return AU3(AF3_(1.0)-x);} + AU4 AZolNotF4ToU4(AF4 x){return AU4(AF4_(1.0)-x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolU1ToF1(AU1 x){return AF1(x);} + AF2 AZolU2ToF2(AU2 x){return AF2(x);} + AF3 AZolU3ToF3(AU3 x){return AF3(x);} + AF4 AZolU4ToF4(AU4 x){return AF4(x);} +//============================================================================================================================== + AF1 AZolAndF1(AF1 x,AF1 y){return min(x,y);} + AF2 AZolAndF2(AF2 x,AF2 y){return min(x,y);} + AF3 AZolAndF3(AF3 x,AF3 y){return min(x,y);} + AF4 AZolAndF4(AF4 x,AF4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 ASolAndNotF1(AF1 x,AF1 y){return (-x)*y+AF1_(1.0);} + AF2 ASolAndNotF2(AF2 x,AF2 y){return (-x)*y+AF2_(1.0);} + AF3 ASolAndNotF3(AF3 x,AF3 y){return (-x)*y+AF3_(1.0);} + AF4 ASolAndNotF4(AF4 x,AF4 y){return (-x)*y+AF4_(1.0);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolAndOrF1(AF1 x,AF1 y,AF1 z){return ASatF1(x*y+z);} + AF2 AZolAndOrF2(AF2 x,AF2 y,AF2 z){return ASatF2(x*y+z);} + AF3 AZolAndOrF3(AF3 x,AF3 y,AF3 z){return ASatF3(x*y+z);} + AF4 AZolAndOrF4(AF4 x,AF4 y,AF4 z){return ASatF4(x*y+z);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolGtZeroF1(AF1 x){return ASatF1(x*AF1_(A_INFP_F));} + AF2 AZolGtZeroF2(AF2 x){return ASatF2(x*AF2_(A_INFP_F));} + AF3 AZolGtZeroF3(AF3 x){return ASatF3(x*AF3_(A_INFP_F));} + AF4 AZolGtZeroF4(AF4 x){return ASatF4(x*AF4_(A_INFP_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolNotF1(AF1 x){return AF1_(1.0)-x;} + AF2 AZolNotF2(AF2 x){return AF2_(1.0)-x;} + AF3 AZolNotF3(AF3 x){return AF3_(1.0)-x;} + AF4 AZolNotF4(AF4 x){return AF4_(1.0)-x;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolOrF1(AF1 x,AF1 y){return max(x,y);} + AF2 AZolOrF2(AF2 x,AF2 y){return max(x,y);} + AF3 AZolOrF3(AF3 x,AF3 y){return max(x,y);} + AF4 AZolOrF4(AF4 x,AF4 y){return max(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolSelF1(AF1 x,AF1 y,AF1 z){AF1 r=(-x)*z+z;return x*y+r;} + AF2 AZolSelF2(AF2 x,AF2 y,AF2 z){AF2 r=(-x)*z+z;return x*y+r;} + AF3 AZolSelF3(AF3 x,AF3 y,AF3 z){AF3 r=(-x)*z+z;return x*y+r;} + AF4 AZolSelF4(AF4 x,AF4 y,AF4 z){AF4 r=(-x)*z+z;return x*y+r;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolSignedF1(AF1 x){return ASatF1(x*AF1_(A_INFN_F));} + AF2 AZolSignedF2(AF2 x){return ASatF2(x*AF2_(A_INFN_F));} + AF3 AZolSignedF3(AF3 x){return ASatF3(x*AF3_(A_INFN_F));} + AF4 AZolSignedF4(AF4 x){return ASatF4(x*AF4_(A_INFN_F));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AZolZeroPassF1(AF1 x,AF1 y){return AF1_AU1((AU1_AF1(x)!=AU1_(0))?AU1_(0):AU1_AF1(y));} + AF2 AZolZeroPassF2(AF2 x,AF2 y){return AF2_AU2((AU2_AF2(x)!=AU2_(0))?AU2_(0):AU2_AF2(y));} + AF3 AZolZeroPassF3(AF3 x,AF3 y){return AF3_AU3((AU3_AF3(x)!=AU3_(0))?AU3_(0):AU3_AF3(y));} + AF4 AZolZeroPassF4(AF4 x,AF4 y){return AF4_AU4((AU4_AF4(x)!=AU4_(0))?AU4_(0):AU4_AF4(y));} + #endif +//============================================================================================================================== + #ifdef A_HALF + AW1 AZolAndW1(AW1 x,AW1 y){return min(x,y);} + AW2 AZolAndW2(AW2 x,AW2 y){return min(x,y);} + AW3 AZolAndW3(AW3 x,AW3 y){return min(x,y);} + AW4 AZolAndW4(AW4 x,AW4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AZolNotW1(AW1 x){return x^AW1_(1);} + AW2 AZolNotW2(AW2 x){return x^AW2_(1);} + AW3 AZolNotW3(AW3 x){return x^AW3_(1);} + AW4 AZolNotW4(AW4 x){return x^AW4_(1);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AZolOrW1(AW1 x,AW1 y){return max(x,y);} + AW2 AZolOrW2(AW2 x,AW2 y){return max(x,y);} + AW3 AZolOrW3(AW3 x,AW3 y){return max(x,y);} + AW4 AZolOrW4(AW4 x,AW4 y){return max(x,y);} +//============================================================================================================================== + // Uses denormal trick. + AW1 AZolH1ToW1(AH1 x){return AW1_AH1(x*AH1_AW1(AW1_(1)));} + AW2 AZolH2ToW2(AH2 x){return AW2_AH2(x*AH2_AW2(AW2_(1)));} + AW3 AZolH3ToW3(AH3 x){return AW3_AH3(x*AH3_AW3(AW3_(1)));} + AW4 AZolH4ToW4(AH4 x){return AW4_AH4(x*AH4_AW4(AW4_(1)));} +//------------------------------------------------------------------------------------------------------------------------------ + // AMD arch lacks a packed conversion opcode. + AH1 AZolW1ToH1(AW1 x){return AH1_AW1(x*AW1_AH1(AH1_(1.0)));} + AH2 AZolW2ToH2(AW2 x){return AH2_AW2(x*AW2_AH2(AH2_(1.0)));} + AH3 AZolW1ToH3(AW3 x){return AH3_AW3(x*AW3_AH3(AH3_(1.0)));} + AH4 AZolW2ToH4(AW4 x){return AH4_AW4(x*AW4_AH4(AH4_(1.0)));} +//============================================================================================================================== + AH1 AZolAndH1(AH1 x,AH1 y){return min(x,y);} + AH2 AZolAndH2(AH2 x,AH2 y){return min(x,y);} + AH3 AZolAndH3(AH3 x,AH3 y){return min(x,y);} + AH4 AZolAndH4(AH4 x,AH4 y){return min(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASolAndNotH1(AH1 x,AH1 y){return (-x)*y+AH1_(1.0);} + AH2 ASolAndNotH2(AH2 x,AH2 y){return (-x)*y+AH2_(1.0);} + AH3 ASolAndNotH3(AH3 x,AH3 y){return (-x)*y+AH3_(1.0);} + AH4 ASolAndNotH4(AH4 x,AH4 y){return (-x)*y+AH4_(1.0);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolAndOrH1(AH1 x,AH1 y,AH1 z){return ASatH1(x*y+z);} + AH2 AZolAndOrH2(AH2 x,AH2 y,AH2 z){return ASatH2(x*y+z);} + AH3 AZolAndOrH3(AH3 x,AH3 y,AH3 z){return ASatH3(x*y+z);} + AH4 AZolAndOrH4(AH4 x,AH4 y,AH4 z){return ASatH4(x*y+z);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolGtZeroH1(AH1 x){return ASatH1(x*AH1_(A_INFP_H));} + AH2 AZolGtZeroH2(AH2 x){return ASatH2(x*AH2_(A_INFP_H));} + AH3 AZolGtZeroH3(AH3 x){return ASatH3(x*AH3_(A_INFP_H));} + AH4 AZolGtZeroH4(AH4 x){return ASatH4(x*AH4_(A_INFP_H));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolNotH1(AH1 x){return AH1_(1.0)-x;} + AH2 AZolNotH2(AH2 x){return AH2_(1.0)-x;} + AH3 AZolNotH3(AH3 x){return AH3_(1.0)-x;} + AH4 AZolNotH4(AH4 x){return AH4_(1.0)-x;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolOrH1(AH1 x,AH1 y){return max(x,y);} + AH2 AZolOrH2(AH2 x,AH2 y){return max(x,y);} + AH3 AZolOrH3(AH3 x,AH3 y){return max(x,y);} + AH4 AZolOrH4(AH4 x,AH4 y){return max(x,y);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolSelH1(AH1 x,AH1 y,AH1 z){AH1 r=(-x)*z+z;return x*y+r;} + AH2 AZolSelH2(AH2 x,AH2 y,AH2 z){AH2 r=(-x)*z+z;return x*y+r;} + AH3 AZolSelH3(AH3 x,AH3 y,AH3 z){AH3 r=(-x)*z+z;return x*y+r;} + AH4 AZolSelH4(AH4 x,AH4 y,AH4 z){AH4 r=(-x)*z+z;return x*y+r;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AZolSignedH1(AH1 x){return ASatH1(x*AH1_(A_INFN_H));} + AH2 AZolSignedH2(AH2 x){return ASatH2(x*AH2_(A_INFN_H));} + AH3 AZolSignedH3(AH3 x){return ASatH3(x*AH3_(A_INFN_H));} + AH4 AZolSignedH4(AH4 x){return ASatH4(x*AH4_(A_INFN_H));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// COLOR CONVERSIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These are all linear to/from some other space (where 'linear' has been shortened out of the function name). +// So 'ToGamma' is 'LinearToGamma', and 'FromGamma' is 'LinearFromGamma'. +// These are branch free implementations. +// The AToSrgbF1() function is useful for stores for compute shaders for GPUs without hardware linear->sRGB store conversion. +//------------------------------------------------------------------------------------------------------------------------------ +// TRANSFER FUNCTIONS +// ================== +// 709 ..... Rec709 used for some HDTVs +// Gamma ... Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native +// Pq ...... PQ native for HDR10 +// Srgb .... The sRGB output, typical of PC displays, useful for 10-bit output, or storing to 8-bit UNORM without SRGB type +// Two ..... Gamma 2.0, fastest conversion (useful for intermediate pass approximations) +// Three ... Gamma 3.0, less fast, but good for HDR. +//------------------------------------------------------------------------------------------------------------------------------ +// KEEPING TO SPEC +// =============== +// Both Rec.709 and sRGB have a linear segment which as spec'ed would intersect the curved segment 2 times. +// (a.) For 8-bit sRGB, steps {0 to 10.3} are in the linear region (4% of the encoding range). +// (b.) For 8-bit 709, steps {0 to 20.7} are in the linear region (8% of the encoding range). +// Also there is a slight step in the transition regions. +// Precision of the coefficients in the spec being the likely cause. +// Main usage case of the sRGB code is to do the linear->sRGB converstion in a compute shader before store. +// This is to work around lack of hardware (typically only ROP does the conversion for free). +// To "correct" the linear segment, would be to introduce error, because hardware decode of sRGB->linear is fixed (and free). +// So this header keeps with the spec. +// For linear->sRGB transforms, the linear segment in some respects reduces error, because rounding in that region is linear. +// Rounding in the curved region in hardware (and fast software code) introduces error due to rounding in non-linear. +//------------------------------------------------------------------------------------------------------------------------------ +// FOR PQ +// ====== +// Both input and output is {0.0-1.0}, and where output 1.0 represents 10000.0 cd/m^2. +// All constants are only specified to FP32 precision. +// External PQ source reference, +// - https://github.com/ampas/aces-dev/blob/master/transforms/ctl/utilities/ACESlib.Utilities_Color.a1.0.1.ctl +//------------------------------------------------------------------------------------------------------------------------------ +// PACKED VERSIONS +// =============== +// These are the A*H2() functions. +// There is no PQ functions as FP16 seemed to not have enough precision for the conversion. +// The remaining functions are "good enough" for 8-bit, and maybe 10-bit if not concerned about a few 1-bit errors. +// Precision is lowest in the 709 conversion, higher in sRGB, higher still in Two and Gamma (when using 2.2 at least). +//------------------------------------------------------------------------------------------------------------------------------ +// NOTES +// ===== +// Could be faster for PQ conversions to be in ALU or a texture lookup depending on usage case. +//============================================================================================================================== + #if 1 + AF1 ATo709F1(AF1 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AF2 ATo709F2(AF2 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AF3 ATo709F3(AF3 c){AF3 j=AF3(0.018*4.5,4.5,0.45);AF2 k=AF2(1.099,-0.099); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + // Note 'rcpX' is '1/x', where the 'x' is what would be used in AFromGamma(). + AF1 AToGammaF1(AF1 c,AF1 rcpX){return pow(c,AF1_(rcpX));} + AF2 AToGammaF2(AF2 c,AF1 rcpX){return pow(c,AF2_(rcpX));} + AF3 AToGammaF3(AF3 c,AF1 rcpX){return pow(c,AF3_(rcpX));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToPqF1(AF1 x){AF1 p=pow(x,AF1_(0.159302)); + return pow((AF1_(0.835938)+AF1_(18.8516)*p)/(AF1_(1.0)+AF1_(18.6875)*p),AF1_(78.8438));} + AF2 AToPqF1(AF2 x){AF2 p=pow(x,AF2_(0.159302)); + return pow((AF2_(0.835938)+AF2_(18.8516)*p)/(AF2_(1.0)+AF2_(18.6875)*p),AF2_(78.8438));} + AF3 AToPqF1(AF3 x){AF3 p=pow(x,AF3_(0.159302)); + return pow((AF3_(0.835938)+AF3_(18.8516)*p)/(AF3_(1.0)+AF3_(18.6875)*p),AF3_(78.8438));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToSrgbF1(AF1 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AF2 AToSrgbF2(AF2 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AF3 AToSrgbF3(AF3 c){AF3 j=AF3(0.0031308*12.92,12.92,1.0/2.4);AF2 k=AF2(1.055,-0.055); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToTwoF1(AF1 c){return sqrt(c);} + AF2 AToTwoF2(AF2 c){return sqrt(c);} + AF3 AToTwoF3(AF3 c){return sqrt(c);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToThreeF1(AF1 c){return pow(c,AF1_(1.0/3.0));} + AF2 AToThreeF2(AF2 c){return pow(c,AF2_(1.0/3.0));} + AF3 AToThreeF3(AF3 c){return pow(c,AF3_(1.0/3.0));} + #endif +//============================================================================================================================== + #if 1 + // Unfortunately median won't work here. + AF1 AFrom709F1(AF1 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF1(AZolSignedF1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AF2 AFrom709F2(AF2 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF2(AZolSignedF2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AF3 AFrom709F3(AF3 c){AF3 j=AF3(0.081/4.5,1.0/4.5,1.0/0.45);AF2 k=AF2(1.0/1.099,0.099/1.099); + return AZolSelF3(AZolSignedF3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromGammaF1(AF1 c,AF1 x){return pow(c,AF1_(x));} + AF2 AFromGammaF2(AF2 c,AF1 x){return pow(c,AF2_(x));} + AF3 AFromGammaF3(AF3 c,AF1 x){return pow(c,AF3_(x));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromPqF1(AF1 x){AF1 p=pow(x,AF1_(0.0126833)); + return pow(ASatF1(p-AF1_(0.835938))/(AF1_(18.8516)-AF1_(18.6875)*p),AF1_(6.27739));} + AF2 AFromPqF1(AF2 x){AF2 p=pow(x,AF2_(0.0126833)); + return pow(ASatF2(p-AF2_(0.835938))/(AF2_(18.8516)-AF2_(18.6875)*p),AF2_(6.27739));} + AF3 AFromPqF1(AF3 x){AF3 p=pow(x,AF3_(0.0126833)); + return pow(ASatF3(p-AF3_(0.835938))/(AF3_(18.8516)-AF3_(18.6875)*p),AF3_(6.27739));} +//------------------------------------------------------------------------------------------------------------------------------ + // Unfortunately median won't work here. + AF1 AFromSrgbF1(AF1 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF1(AZolSignedF1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AF2 AFromSrgbF2(AF2 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF2(AZolSignedF2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AF3 AFromSrgbF3(AF3 c){AF3 j=AF3(0.04045/12.92,1.0/12.92,2.4);AF2 k=AF2(1.0/1.055,0.055/1.055); + return AZolSelF3(AZolSignedF3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromTwoF1(AF1 c){return c*c;} + AF2 AFromTwoF2(AF2 c){return c*c;} + AF3 AFromTwoF3(AF3 c){return c*c;} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromThreeF1(AF1 c){return c*c*c;} + AF2 AFromThreeF2(AF2 c){return c*c*c;} + AF3 AFromThreeF3(AF3 c){return c*c*c;} + #endif +//============================================================================================================================== + #ifdef A_HALF + AH1 ATo709H1(AH1 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AH2 ATo709H2(AH2 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AH3 ATo709H3(AH3 c){AH3 j=AH3(0.018*4.5,4.5,0.45);AH2 k=AH2(1.099,-0.099); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToGammaH1(AH1 c,AH1 rcpX){return pow(c,AH1_(rcpX));} + AH2 AToGammaH2(AH2 c,AH1 rcpX){return pow(c,AH2_(rcpX));} + AH3 AToGammaH3(AH3 c,AH1 rcpX){return pow(c,AH3_(rcpX));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToSrgbH1(AH1 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.x ,c*j.y ,pow(c,j.z )*k.x +k.y );} + AH2 AToSrgbH2(AH2 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.xx ,c*j.yy ,pow(c,j.zz )*k.xx +k.yy );} + AH3 AToSrgbH3(AH3 c){AH3 j=AH3(0.0031308*12.92,12.92,1.0/2.4);AH2 k=AH2(1.055,-0.055); + return clamp(j.xxx,c*j.yyy,pow(c,j.zzz)*k.xxx+k.yyy);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToTwoH1(AH1 c){return sqrt(c);} + AH2 AToTwoH2(AH2 c){return sqrt(c);} + AH3 AToTwoH3(AH3 c){return sqrt(c);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AToThreeF1(AH1 c){return pow(c,AH1_(1.0/3.0));} + AH2 AToThreeF2(AH2 c){return pow(c,AH2_(1.0/3.0));} + AH3 AToThreeF3(AH3 c){return pow(c,AH3_(1.0/3.0));} + #endif +//============================================================================================================================== + #ifdef A_HALF + AH1 AFrom709H1(AH1 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH1(AZolSignedH1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AH2 AFrom709H2(AH2 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH2(AZolSignedH2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AH3 AFrom709H3(AH3 c){AH3 j=AH3(0.081/4.5,1.0/4.5,1.0/0.45);AH2 k=AH2(1.0/1.099,0.099/1.099); + return AZolSelH3(AZolSignedH3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromGammaH1(AH1 c,AH1 x){return pow(c,AH1_(x));} + AH2 AFromGammaH2(AH2 c,AH1 x){return pow(c,AH2_(x));} + AH3 AFromGammaH3(AH3 c,AH1 x){return pow(c,AH3_(x));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AHromSrgbF1(AH1 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH1(AZolSignedH1(c-j.x ),c*j.y ,pow(c*k.x +k.y ,j.z ));} + AH2 AHromSrgbF2(AH2 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH2(AZolSignedH2(c-j.xx ),c*j.yy ,pow(c*k.xx +k.yy ,j.zz ));} + AH3 AHromSrgbF3(AH3 c){AH3 j=AH3(0.04045/12.92,1.0/12.92,2.4);AH2 k=AH2(1.0/1.055,0.055/1.055); + return AZolSelH3(AZolSignedH3(c-j.xxx),c*j.yyy,pow(c*k.xxx+k.yyy,j.zzz));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromTwoH1(AH1 c){return c*c;} + AH2 AFromTwoH2(AH2 c){return c*c;} + AH3 AFromTwoH3(AH3 c){return c*c;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFromThreeH1(AH1 c){return c*c*c;} + AH2 AFromThreeH2(AH2 c){return c*c*c;} + AH3 AFromThreeH3(AH3 c){return c*c*c;} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CS REMAP +//============================================================================================================================== + // Simple remap 64x1 to 8x8 with rotated 2x2 pixel quads in quad linear. + // 543210 + // ====== + // ..xxx. + // yy...y + AU2 ARmp8x8(AU1 a){return AU2(ABfe(a,1u,3u),ABfiM(ABfe(a,3u,3u),a,1u));} +//============================================================================================================================== + // More complex remap 64x1 to 8x8 which is necessary for 2D wave reductions. + // 543210 + // ====== + // .xx..x + // y..yy. + // Details, + // LANE TO 8x8 MAPPING + // =================== + // 00 01 08 09 10 11 18 19 + // 02 03 0a 0b 12 13 1a 1b + // 04 05 0c 0d 14 15 1c 1d + // 06 07 0e 0f 16 17 1e 1f + // 20 21 28 29 30 31 38 39 + // 22 23 2a 2b 32 33 3a 3b + // 24 25 2c 2d 34 35 3c 3d + // 26 27 2e 2f 36 37 3e 3f + AU2 ARmpRed8x8(AU1 a){return AU2(ABfiM(ABfe(a,2u,3u),a,1u),ABfiM(ABfe(a,3u,3u),ABfe(a,1u,2u),2u));} +//============================================================================================================================== + #ifdef A_HALF + AW2 ARmp8x8H(AU1 a){return AW2(ABfe(a,1u,3u),ABfiM(ABfe(a,3u,3u),a,1u));} + AW2 ARmpRed8x8H(AU1 a){return AW2(ABfiM(ABfe(a,2u,3u),a,1u),ABfiM(ABfe(a,3u,3u),ABfe(a,1u,2u),2u));} + #endif +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// REFERENCE +// +//------------------------------------------------------------------------------------------------------------------------------ +// IEEE FLOAT RULES +// ================ +// - saturate(NaN)=0, saturate(-INF)=0, saturate(+INF)=1 +// - {+/-}0 * {+/-}INF = NaN +// - -INF + (+INF) = NaN +// - {+/-}0 / {+/-}0 = NaN +// - {+/-}INF / {+/-}INF = NaN +// - a<(-0) := sqrt(a) = NaN (a=-0.0 won't NaN) +// - 0 == -0 +// - 4/0 = +INF +// - 4/-0 = -INF +// - 4+INF = +INF +// - 4-INF = -INF +// - 4*(+INF) = +INF +// - 4*(-INF) = -INF +// - -4*(+INF) = -INF +// - sqrt(+INF) = +INF +//------------------------------------------------------------------------------------------------------------------------------ +// FP16 ENCODING +// ============= +// fedcba9876543210 +// ---------------- +// ......mmmmmmmmmm 10-bit mantissa (encodes 11-bit 0.5 to 1.0 except for denormals) +// .eeeee.......... 5-bit exponent +// .00000.......... denormals +// .00001.......... -14 exponent +// .11110.......... 15 exponent +// .111110000000000 infinity +// .11111nnnnnnnnnn NaN with n!=0 +// s............... sign +//------------------------------------------------------------------------------------------------------------------------------ +// FP16/INT16 ALIASING DENORMAL +// ============================ +// 11-bit unsigned integers alias with half float denormal/normal values, +// 1 = 2^(-24) = 1/16777216 ....................... first denormal value +// 2 = 2^(-23) +// ... +// 1023 = 2^(-14)*(1-2^(-10)) = 2^(-14)*(1-1/1024) ... last denormal value +// 1024 = 2^(-14) = 1/16384 .......................... first normal value that still maps to integers +// 2047 .............................................. last normal value that still maps to integers +// Scaling limits, +// 2^15 = 32768 ...................................... largest power of 2 scaling +// Largest pow2 conversion mapping is at *32768, +// 1 : 2^(-9) = 1/512 +// 2 : 1/256 +// 4 : 1/128 +// 8 : 1/64 +// 16 : 1/32 +// 32 : 1/16 +// 64 : 1/8 +// 128 : 1/4 +// 256 : 1/2 +// 512 : 1 +// 1024 : 2 +// 2047 : a little less than 4 +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// GPU/CPU PORTABILITY +// +// +//------------------------------------------------------------------------------------------------------------------------------ +// This is the GPU implementation. +// See the CPU implementation for docs. +//============================================================================================================================== +#ifdef A_GPU + #define A_TRUE true + #define A_FALSE false + #define A_STATIC +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD2 + #define retAD3 AD3 + #define retAD4 AD4 + #define retAF2 AF2 + #define retAF3 AF3 + #define retAF4 AF4 + #define retAL2 AL2 + #define retAL3 AL3 + #define retAL4 AL4 + #define retAU2 AU2 + #define retAU3 AU3 + #define retAU4 AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 in AD2 + #define inAD3 in AD3 + #define inAD4 in AD4 + #define inAF2 in AF2 + #define inAF3 in AF3 + #define inAF4 in AF4 + #define inAL2 in AL2 + #define inAL3 in AL3 + #define inAL4 in AL4 + #define inAU2 in AU2 + #define inAU3 in AU3 + #define inAU4 in AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 inout AD2 + #define inoutAD3 inout AD3 + #define inoutAD4 inout AD4 + #define inoutAF2 inout AF2 + #define inoutAF3 inout AF3 + #define inoutAF4 inout AF4 + #define inoutAL2 inout AL2 + #define inoutAL3 inout AL3 + #define inoutAL4 inout AL4 + #define inoutAU2 inout AU2 + #define inoutAU3 inout AU3 + #define inoutAU4 inout AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 out AD2 + #define outAD3 out AD3 + #define outAD4 out AD4 + #define outAF2 out AF2 + #define outAF3 out AF3 + #define outAF4 out AF4 + #define outAL2 out AL2 + #define outAL3 out AL3 + #define outAL4 out AL4 + #define outAU2 out AU2 + #define outAU3 out AU3 + #define outAU4 out AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD2 x + #define varAD3(x) AD3 x + #define varAD4(x) AD4 x + #define varAF2(x) AF2 x + #define varAF3(x) AF3 x + #define varAF4(x) AF4 x + #define varAL2(x) AL2 x + #define varAL3(x) AL3 x + #define varAL4(x) AL4 x + #define varAU2(x) AU2 x + #define varAU3(x) AU3 x + #define varAU4(x) AU4 x +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) AD2(x,y) + #define initAD3(x,y,z) AD3(x,y,z) + #define initAD4(x,y,z,w) AD4(x,y,z,w) + #define initAF2(x,y) AF2(x,y) + #define initAF3(x,y,z) AF3(x,y,z) + #define initAF4(x,y,z,w) AF4(x,y,z,w) + #define initAL2(x,y) AL2(x,y) + #define initAL3(x,y,z) AL3(x,y,z) + #define initAL4(x,y,z,w) AL4(x,y,z,w) + #define initAU2(x,y) AU2(x,y) + #define initAU3(x,y,z) AU3(x,y,z) + #define initAU4(x,y,z,w) AU4(x,y,z,w) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//============================================================================================================================== + #define AAbsD1(a) abs(AD1(a)) + #define AAbsF1(a) abs(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ACosD1(a) cos(AD1(a)) + #define ACosF1(a) cos(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ADotD2(a,b) dot(AD2(a),AD2(b)) + #define ADotD3(a,b) dot(AD3(a),AD3(b)) + #define ADotD4(a,b) dot(AD4(a),AD4(b)) + #define ADotF2(a,b) dot(AF2(a),AF2(b)) + #define ADotF3(a,b) dot(AF3(a),AF3(b)) + #define ADotF4(a,b) dot(AF4(a),AF4(b)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AExp2D1(a) exp2(AD1(a)) + #define AExp2F1(a) exp2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AFloorD1(a) floor(AD1(a)) + #define AFloorF1(a) floor(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ALog2D1(a) log2(AD1(a)) + #define ALog2F1(a) log2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMaxD1(a,b) max(a,b) + #define AMaxF1(a,b) max(a,b) + #define AMaxL1(a,b) max(a,b) + #define AMaxU1(a,b) max(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMinD1(a,b) min(a,b) + #define AMinF1(a,b) min(a,b) + #define AMinL1(a,b) min(a,b) + #define AMinU1(a,b) min(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASinD1(a) sin(AD1(a)) + #define ASinF1(a) sin(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASqrtD1(a) sqrt(AD1(a)) + #define ASqrtF1(a) sqrt(AF1(a)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + #define APowD1(a,b) pow(AD1(a),AF1(b)) + #define APowF1(a,b) pow(AF1(a),AF1(b)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + #ifdef A_DUBL + AD2 opAAbsD2(outAD2 d,inAD2 a){d=abs(a);return d;} + AD3 opAAbsD3(outAD3 d,inAD3 a){d=abs(a);return d;} + AD4 opAAbsD4(outAD4 d,inAD4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d=a+b;return d;} + AD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d=a+b;return d;} + AD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAAddOneD2(outAD2 d,inAD2 a,AD1 b){d=a+AD2_(b);return d;} + AD3 opAAddOneD3(outAD3 d,inAD3 a,AD1 b){d=a+AD3_(b);return d;} + AD4 opAAddOneD4(outAD4 d,inAD4 a,AD1 b){d=a+AD4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opACpyD2(outAD2 d,inAD2 a){d=a;return d;} + AD3 opACpyD3(outAD3 d,inAD3 a){d=a;return d;} + AD4 opACpyD4(outAD4 d,inAD4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d=ALerpD2(a,b,c);return d;} + AD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d=ALerpD3(a,b,c);return d;} + AD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d=ALerpD4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d=ALerpD2(a,b,AD2_(c));return d;} + AD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d=ALerpD3(a,b,AD3_(c));return d;} + AD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d=ALerpD4(a,b,AD4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d=max(a,b);return d;} + AD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d=max(a,b);return d;} + AD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d=min(a,b);return d;} + AD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d=min(a,b);return d;} + AD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d=a*b;return d;} + AD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d=a*b;return d;} + AD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d=a*AD2_(b);return d;} + AD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d=a*AD3_(b);return d;} + AD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d=a*AD4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opANegD2(outAD2 d,inAD2 a){d=-a;return d;} + AD3 opANegD3(outAD3 d,inAD3 a){d=-a;return d;} + AD4 opANegD4(outAD4 d,inAD4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opARcpD2(outAD2 d,inAD2 a){d=ARcpD2(a);return d;} + AD3 opARcpD3(outAD3 d,inAD3 a){d=ARcpD3(a);return d;} + AD4 opARcpD4(outAD4 d,inAD4 a){d=ARcpD4(a);return d;} + #endif +//============================================================================================================================== + AF2 opAAbsF2(outAF2 d,inAF2 a){d=abs(a);return d;} + AF3 opAAbsF3(outAF3 d,inAF3 a){d=abs(a);return d;} + AF4 opAAbsF4(outAF4 d,inAF4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d=a+b;return d;} + AF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d=a+b;return d;} + AF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAAddOneF2(outAF2 d,inAF2 a,AF1 b){d=a+AF2_(b);return d;} + AF3 opAAddOneF3(outAF3 d,inAF3 a,AF1 b){d=a+AF3_(b);return d;} + AF4 opAAddOneF4(outAF4 d,inAF4 a,AF1 b){d=a+AF4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opACpyF2(outAF2 d,inAF2 a){d=a;return d;} + AF3 opACpyF3(outAF3 d,inAF3 a){d=a;return d;} + AF4 opACpyF4(outAF4 d,inAF4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d=ALerpF2(a,b,c);return d;} + AF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d=ALerpF3(a,b,c);return d;} + AF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d=ALerpF4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d=ALerpF2(a,b,AF2_(c));return d;} + AF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d=ALerpF3(a,b,AF3_(c));return d;} + AF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d=ALerpF4(a,b,AF4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d=max(a,b);return d;} + AF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d=max(a,b);return d;} + AF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d=min(a,b);return d;} + AF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d=min(a,b);return d;} + AF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d=a*b;return d;} + AF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d=a*b;return d;} + AF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d=a*AF2_(b);return d;} + AF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d=a*AF3_(b);return d;} + AF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d=a*AF4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opANegF2(outAF2 d,inAF2 a){d=-a;return d;} + AF3 opANegF3(outAF3 d,inAF3 a){d=-a;return d;} + AF4 opANegF4(outAF4 d,inAF4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opARcpF2(outAF2 d,inAF2 a){d=ARcpF2(a);return d;} + AF3 opARcpF3(outAF3 d,inAF3 a){d=ARcpF3(a);return d;} + AF4 opARcpF4(outAF4 d,inAF4 a){d=ARcpF4(a);return d;} +#endif diff --git a/src/video_core/host_shaders/fsr/ffx_fsr1.h b/src/video_core/host_shaders/fsr/ffx_fsr1.h new file mode 100644 index 000000000..b0fe75ea9 --- /dev/null +++ b/src/video_core/host_shaders/fsr/ffx_fsr1.h @@ -0,0 +1,1200 @@ +// clang-format off +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// +// AMD FidelityFX SUPER RESOLUTION [FSR 1] ::: SPATIAL SCALING & EXTRAS - v1.20210629 +// +// +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// FidelityFX Super Resolution Sample +// +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// ABOUT +// ===== +// FSR is a collection of algorithms relating to generating a higher resolution image. +// This specific header focuses on single-image non-temporal image scaling, and related tools. +// +// The core functions are EASU and RCAS: +// [EASU] Edge Adaptive Spatial Upsampling ....... 1x to 4x area range spatial scaling, clamped adaptive elliptical filter. +// [RCAS] Robust Contrast Adaptive Sharpening .... A non-scaling variation on CAS. +// RCAS needs to be applied after EASU as a separate pass. +// +// Optional utility functions are: +// [LFGA] Linear Film Grain Applicator ........... Tool to apply film grain after scaling. +// [SRTM] Simple Reversible Tone-Mapper .......... Linear HDR {0 to FP16_MAX} to {0 to 1} and back. +// [TEPD] Temporal Energy Preserving Dither ...... Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. +// See each individual sub-section for inline documentation. +//------------------------------------------------------------------------------------------------------------------------------ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//------------------------------------------------------------------------------------------------------------------------------ +// FUNCTION PERMUTATIONS +// ===================== +// *F() ..... Single item computation with 32-bit. +// *H() ..... Single item computation with 16-bit, with packing (aka two 16-bit ops in parallel) when possible. +// *Hx2() ... Processing two items in parallel with 16-bit, easier packing. +// Not all interfaces in this file have a *Hx2() form. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [EASU] EDGE ADAPTIVE SPATIAL UPSAMPLING +// +//------------------------------------------------------------------------------------------------------------------------------ +// EASU provides a high quality spatial-only scaling at relatively low cost. +// Meaning EASU is appropiate for laptops and other low-end GPUs. +// Quality from 1x to 4x area scaling is good. +//------------------------------------------------------------------------------------------------------------------------------ +// The scalar uses a modified fast approximation to the standard lanczos(size=2) kernel. +// EASU runs in a single pass, so it applies a directionally and anisotropically adaptive radial lanczos. +// This is also kept as simple as possible to have minimum runtime. +//------------------------------------------------------------------------------------------------------------------------------ +// The lanzcos filter has negative lobes, so by itself it will introduce ringing. +// To remove all ringing, the algorithm uses the nearest 2x2 input texels as a neighborhood, +// and limits output to the minimum and maximum of that neighborhood. +//------------------------------------------------------------------------------------------------------------------------------ +// Input image requirements: +// +// Color needs to be encoded as 3 channel[red, green, blue](e.g.XYZ not supported) +// Each channel needs to be in the range[0, 1] +// Any color primaries are supported +// Display / tonemapping curve needs to be as if presenting to sRGB display or similar(e.g.Gamma 2.0) +// There should be no banding in the input +// There should be no high amplitude noise in the input +// There should be no noise in the input that is not at input pixel granularity +// For performance purposes, use 32bpp formats +//------------------------------------------------------------------------------------------------------------------------------ +// Best to apply EASU at the end of the frame after tonemapping +// but before film grain or composite of the UI. +//------------------------------------------------------------------------------------------------------------------------------ +// Example of including this header for D3D HLSL : +// +// #define A_GPU 1 +// #define A_HLSL 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of including this header for Vulkan GLSL : +// +// #define A_GPU 1 +// #define A_GLSL 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of including this header for Vulkan HLSL : +// +// #define A_GPU 1 +// #define A_HLSL 1 +// #define A_HLSL_6_2 1 +// #define A_NO_16_BIT_CAST 1 +// #define A_HALF 1 +// #include "ffx_a.h" +// #define FSR_EASU_H 1 +// #define FSR_RCAS_H 1 +// //declare input callbacks +// #include "ffx_fsr1.h" +// +// Example of declaring the required input callbacks for GLSL : +// The callbacks need to gather4 for each color channel using the specified texture coordinate 'p'. +// EASU uses gather4 to reduce position computation logic and for free Arrays of Structures to Structures of Arrays conversion. +// +// AH4 FsrEasuRH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,0));} +// AH4 FsrEasuGH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,1));} +// AH4 FsrEasuBH(AF2 p){return AH4(textureGather(sampler2D(tex,sam),p,2));} +// ... +// The FsrEasuCon function needs to be called from the CPU or GPU to set up constants. +// The difference in viewport and input image size is there to support Dynamic Resolution Scaling. +// To use FsrEasuCon() on the CPU, define A_CPU before including ffx_a and ffx_fsr1. +// Including a GPU example here, the 'con0' through 'con3' values would be stored out to a constant buffer. +// AU4 con0,con1,con2,con3; +// FsrEasuCon(con0,con1,con2,con3, +// 1920.0,1080.0, // Viewport size (top left aligned) in the input image which is to be scaled. +// 3840.0,2160.0, // The size of the input image. +// 2560.0,1440.0); // The output resolution. +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CONSTANT SETUP +//============================================================================================================================== +// Call to setup required constant values (works on CPU or GPU). +A_STATIC void FsrEasuCon( +outAU4 con0, +outAU4 con1, +outAU4 con2, +outAU4 con3, +// This the rendered image resolution being upscaled +AF1 inputViewportInPixelsX, +AF1 inputViewportInPixelsY, +// This is the resolution of the resource containing the input image (useful for dynamic resolution) +AF1 inputSizeInPixelsX, +AF1 inputSizeInPixelsY, +// This is the display resolution which the input image gets upscaled to +AF1 outputSizeInPixelsX, +AF1 outputSizeInPixelsY){ + // Output integer position to a pixel position in viewport. + con0[0]=AU1_AF1(inputViewportInPixelsX*ARcpF1(outputSizeInPixelsX)); + con0[1]=AU1_AF1(inputViewportInPixelsY*ARcpF1(outputSizeInPixelsY)); + con0[2]=AU1_AF1(AF1_(0.5)*inputViewportInPixelsX*ARcpF1(outputSizeInPixelsX)-AF1_(0.5)); + con0[3]=AU1_AF1(AF1_(0.5)*inputViewportInPixelsY*ARcpF1(outputSizeInPixelsY)-AF1_(0.5)); + // Viewport pixel position to normalized image space. + // This is used to get upper-left of 'F' tap. + con1[0]=AU1_AF1(ARcpF1(inputSizeInPixelsX)); + con1[1]=AU1_AF1(ARcpF1(inputSizeInPixelsY)); + // Centers of gather4, first offset from upper-left of 'F'. + // +---+---+ + // | | | + // +--(0)--+ + // | b | c | + // +---F---+---+---+ + // | e | f | g | h | + // +--(1)--+--(2)--+ + // | i | j | k | l | + // +---+---+---+---+ + // | n | o | + // +--(3)--+ + // | | | + // +---+---+ + con1[2]=AU1_AF1(AF1_( 1.0)*ARcpF1(inputSizeInPixelsX)); + con1[3]=AU1_AF1(AF1_(-1.0)*ARcpF1(inputSizeInPixelsY)); + // These are from (0) instead of 'F'. + con2[0]=AU1_AF1(AF1_(-1.0)*ARcpF1(inputSizeInPixelsX)); + con2[1]=AU1_AF1(AF1_( 2.0)*ARcpF1(inputSizeInPixelsY)); + con2[2]=AU1_AF1(AF1_( 1.0)*ARcpF1(inputSizeInPixelsX)); + con2[3]=AU1_AF1(AF1_( 2.0)*ARcpF1(inputSizeInPixelsY)); + con3[0]=AU1_AF1(AF1_( 0.0)*ARcpF1(inputSizeInPixelsX)); + con3[1]=AU1_AF1(AF1_( 4.0)*ARcpF1(inputSizeInPixelsY)); + con3[2]=con3[3]=0;} + +//If the an offset into the input image resource +A_STATIC void FsrEasuConOffset( + outAU4 con0, + outAU4 con1, + outAU4 con2, + outAU4 con3, + // This the rendered image resolution being upscaled + AF1 inputViewportInPixelsX, + AF1 inputViewportInPixelsY, + // This is the resolution of the resource containing the input image (useful for dynamic resolution) + AF1 inputSizeInPixelsX, + AF1 inputSizeInPixelsY, + // This is the display resolution which the input image gets upscaled to + AF1 outputSizeInPixelsX, + AF1 outputSizeInPixelsY, + // This is the input image offset into the resource containing it (useful for dynamic resolution) + AF1 inputOffsetInPixelsX, + AF1 inputOffsetInPixelsY) { + FsrEasuCon(con0, con1, con2, con3, inputViewportInPixelsX, inputViewportInPixelsY, inputSizeInPixelsX, inputSizeInPixelsY, outputSizeInPixelsX, outputSizeInPixelsY); + con0[2] = AU1_AF1(AF1_(0.5) * inputViewportInPixelsX * ARcpF1(outputSizeInPixelsX) - AF1_(0.5) + inputOffsetInPixelsX); + con0[3] = AU1_AF1(AF1_(0.5) * inputViewportInPixelsY * ARcpF1(outputSizeInPixelsY) - AF1_(0.5) + inputOffsetInPixelsY); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 32-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(FSR_EASU_F) + // Input callback prototypes, need to be implemented by calling shader + AF4 FsrEasuRF(AF2 p); + AF4 FsrEasuGF(AF2 p); + AF4 FsrEasuBF(AF2 p); +//------------------------------------------------------------------------------------------------------------------------------ + // Filtering for a given tap for the scalar. + void FsrEasuTapF( + inout AF3 aC, // Accumulated color, with negative lobe. + inout AF1 aW, // Accumulated weight. + AF2 off, // Pixel offset from resolve position to tap. + AF2 dir, // Gradient direction. + AF2 len, // Length. + AF1 lob, // Negative lobe strength. + AF1 clp, // Clipping point. + AF3 c){ // Tap color. + // Rotate offset by direction. + AF2 v; + v.x=(off.x*( dir.x))+(off.y*dir.y); + v.y=(off.x*(-dir.y))+(off.y*dir.x); + // Anisotropy. + v*=len; + // Compute distance^2. + AF1 d2=v.x*v.x+v.y*v.y; + // Limit to the window as at corner, 2 taps can easily be outside. + d2=min(d2,clp); + // Approximation of lancos2 without sin() or rcp(), or sqrt() to get x. + // (25/16 * (2/5 * x^2 - 1)^2 - (25/16 - 1)) * (1/4 * x^2 - 1)^2 + // |_______________________________________| |_______________| + // base window + // The general form of the 'base' is, + // (a*(b*x^2-1)^2-(a-1)) + // Where 'a=1/(2*b-b^2)' and 'b' moves around the negative lobe. + AF1 wB=AF1_(2.0/5.0)*d2+AF1_(-1.0); + AF1 wA=lob*d2+AF1_(-1.0); + wB*=wB; + wA*=wA; + wB=AF1_(25.0/16.0)*wB+AF1_(-(25.0/16.0-1.0)); + AF1 w=wB*wA; + // Do weighted average. + aC+=c*w;aW+=w;} +//------------------------------------------------------------------------------------------------------------------------------ + // Accumulate direction and length. + void FsrEasuSetF( + inout AF2 dir, + inout AF1 len, + AF2 pp, + AP1 biS,AP1 biT,AP1 biU,AP1 biV, + AF1 lA,AF1 lB,AF1 lC,AF1 lD,AF1 lE){ + // Compute bilinear weight, branches factor out as predicates are compiler time immediates. + // s t + // u v + AF1 w = AF1_(0.0); + if(biS)w=(AF1_(1.0)-pp.x)*(AF1_(1.0)-pp.y); + if(biT)w= pp.x *(AF1_(1.0)-pp.y); + if(biU)w=(AF1_(1.0)-pp.x)* pp.y ; + if(biV)w= pp.x * pp.y ; + // Direction is the '+' diff. + // a + // b c d + // e + // Then takes magnitude from abs average of both sides of 'c'. + // Length converts gradient reversal to 0, smoothly to non-reversal at 1, shaped, then adding horz and vert terms. + AF1 dc=lD-lC; + AF1 cb=lC-lB; + AF1 lenX=max(abs(dc),abs(cb)); + lenX=APrxLoRcpF1(lenX); + AF1 dirX=lD-lB; + dir.x+=dirX*w; + lenX=ASatF1(abs(dirX)*lenX); + lenX*=lenX; + len+=lenX*w; + // Repeat for the y axis. + AF1 ec=lE-lC; + AF1 ca=lC-lA; + AF1 lenY=max(abs(ec),abs(ca)); + lenY=APrxLoRcpF1(lenY); + AF1 dirY=lE-lA; + dir.y+=dirY*w; + lenY=ASatF1(abs(dirY)*lenY); + lenY*=lenY; + len+=lenY*w;} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrEasuF( + out AF3 pix, + AU2 ip, // Integer pixel position in output. + AU4 con0, // Constants generated by FsrEasuCon(). + AU4 con1, + AU4 con2, + AU4 con3){ +//------------------------------------------------------------------------------------------------------------------------------ + // Get position of 'f'. + AF2 pp=AF2(ip)*AF2_AU2(con0.xy)+AF2_AU2(con0.zw); + AF2 fp=floor(pp); + pp-=fp; +//------------------------------------------------------------------------------------------------------------------------------ + // 12-tap kernel. + // b c + // e f g h + // i j k l + // n o + // Gather 4 ordering. + // a b + // r g + // For packed FP16, need either {rg} or {ab} so using the following setup for gather in all versions, + // a b <- unused (z) + // r g + // a b a b + // r g r g + // a b + // r g <- unused (z) + // Allowing dead-code removal to remove the 'z's. + AF2 p0=fp*AF2_AU2(con1.xy)+AF2_AU2(con1.zw); + // These are from p0 to avoid pulling two constants on pre-Navi hardware. + AF2 p1=p0+AF2_AU2(con2.xy); + AF2 p2=p0+AF2_AU2(con2.zw); + AF2 p3=p0+AF2_AU2(con3.xy); + AF4 bczzR=FsrEasuRF(p0); + AF4 bczzG=FsrEasuGF(p0); + AF4 bczzB=FsrEasuBF(p0); + AF4 ijfeR=FsrEasuRF(p1); + AF4 ijfeG=FsrEasuGF(p1); + AF4 ijfeB=FsrEasuBF(p1); + AF4 klhgR=FsrEasuRF(p2); + AF4 klhgG=FsrEasuGF(p2); + AF4 klhgB=FsrEasuBF(p2); + AF4 zzonR=FsrEasuRF(p3); + AF4 zzonG=FsrEasuGF(p3); + AF4 zzonB=FsrEasuBF(p3); +//------------------------------------------------------------------------------------------------------------------------------ + // Simplest multi-channel approximate luma possible (luma times 2, in 2 FMA/MAD). + AF4 bczzL=bczzB*AF4_(0.5)+(bczzR*AF4_(0.5)+bczzG); + AF4 ijfeL=ijfeB*AF4_(0.5)+(ijfeR*AF4_(0.5)+ijfeG); + AF4 klhgL=klhgB*AF4_(0.5)+(klhgR*AF4_(0.5)+klhgG); + AF4 zzonL=zzonB*AF4_(0.5)+(zzonR*AF4_(0.5)+zzonG); + // Rename. + AF1 bL=bczzL.x; + AF1 cL=bczzL.y; + AF1 iL=ijfeL.x; + AF1 jL=ijfeL.y; + AF1 fL=ijfeL.z; + AF1 eL=ijfeL.w; + AF1 kL=klhgL.x; + AF1 lL=klhgL.y; + AF1 hL=klhgL.z; + AF1 gL=klhgL.w; + AF1 oL=zzonL.z; + AF1 nL=zzonL.w; + // Accumulate for bilinear interpolation. + AF2 dir=AF2_(0.0); + AF1 len=AF1_(0.0); + FsrEasuSetF(dir,len,pp,true, false,false,false,bL,eL,fL,gL,jL); + FsrEasuSetF(dir,len,pp,false,true ,false,false,cL,fL,gL,hL,kL); + FsrEasuSetF(dir,len,pp,false,false,true ,false,fL,iL,jL,kL,nL); + FsrEasuSetF(dir,len,pp,false,false,false,true ,gL,jL,kL,lL,oL); +//------------------------------------------------------------------------------------------------------------------------------ + // Normalize with approximation, and cleanup close to zero. + AF2 dir2=dir*dir; + AF1 dirR=dir2.x+dir2.y; + AP1 zro=dirR w = -m/(n+e+w+s) +// 1 == (w*(n+e+w+s)+m)/(4*w+1) -> w = (1-m)/(n+e+w+s-4*1) +// Then chooses the 'w' which results in no clipping, limits 'w', and multiplies by the 'sharp' amount. +// This solution above has issues with MSAA input as the steps along the gradient cause edge detection issues. +// So RCAS uses 4x the maximum and 4x the minimum (depending on equation)in place of the individual taps. +// As well as switching from 'm' to either the minimum or maximum (depending on side), to help in energy conservation. +// This stabilizes RCAS. +// RCAS does a simple highpass which is normalized against the local contrast then shaped, +// 0.25 +// 0.25 -1 0.25 +// 0.25 +// This is used as a noise detection filter, to reduce the effect of RCAS on grain, and focus on real edges. +// +// GLSL example for the required callbacks : +// +// AH4 FsrRcasLoadH(ASW2 p){return AH4(imageLoad(imgSrc,ASU2(p)));} +// void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b) +// { +// //do any simple input color conversions here or leave empty if none needed +// } +// +// FsrRcasCon need to be called from the CPU or GPU to set up constants. +// Including a GPU example here, the 'con' value would be stored out to a constant buffer. +// +// AU4 con; +// FsrRcasCon(con, +// 0.0); // The scale is {0.0 := maximum sharpness, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. +// --------------- +// RCAS sharpening supports a CAS-like pass-through alpha via, +// #define FSR_RCAS_PASSTHROUGH_ALPHA 1 +// RCAS also supports a define to enable a more expensive path to avoid some sharpening of noise. +// Would suggest it is better to apply film grain after RCAS sharpening (and after scaling) instead of using this define, +// #define FSR_RCAS_DENOISE 1 +//============================================================================================================================== +// This is set at the limit of providing unnatural results for sharpening. +#define FSR_RCAS_LIMIT (0.25-(1.0/16.0)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CONSTANT SETUP +//============================================================================================================================== +// Call to setup required constant values (works on CPU or GPU). +A_STATIC void FsrRcasCon( +outAU4 con, +// The scale is {0.0 := maximum, to N>0, where N is the number of stops (halving) of the reduction of sharpness}. +AF1 sharpness){ + // Transform from stops to linear value. + sharpness=AExp2F1(-sharpness); + varAF2(hSharp)=initAF2(sharpness,sharpness); + con[0]=AU1_AF1(sharpness); + con[1]=AU1_AH2_AF2(hSharp); + con[2]=0; + con[3]=0;} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 32-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(FSR_RCAS_F) + // Input callback prototypes that need to be implemented by calling shader + AF4 FsrRcasLoadF(ASU2 p); + void FsrRcasInputF(inout AF1 r,inout AF1 g,inout AF1 b); +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasF( + out AF1 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. + out AF1 pixG, + out AF1 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AF1 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // Algorithm uses minimal 3x3 pixel neighborhood. + // b + // d e f + // h + ASU2 sp=ASU2(ip); + AF3 b=FsrRcasLoadF(sp+ASU2( 0,-1)).rgb; + AF3 d=FsrRcasLoadF(sp+ASU2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AF4 ee=FsrRcasLoadF(sp); + AF3 e=ee.rgb;pixA=ee.a; + #else + AF3 e=FsrRcasLoadF(sp).rgb; + #endif + AF3 f=FsrRcasLoadF(sp+ASU2( 1, 0)).rgb; + AF3 h=FsrRcasLoadF(sp+ASU2( 0, 1)).rgb; + // Rename (32-bit) or regroup (16-bit). + AF1 bR=b.r; + AF1 bG=b.g; + AF1 bB=b.b; + AF1 dR=d.r; + AF1 dG=d.g; + AF1 dB=d.b; + AF1 eR=e.r; + AF1 eG=e.g; + AF1 eB=e.b; + AF1 fR=f.r; + AF1 fG=f.g; + AF1 fB=f.b; + AF1 hR=h.r; + AF1 hG=h.g; + AF1 hB=h.b; + // Run optional input transform. + FsrRcasInputF(bR,bG,bB); + FsrRcasInputF(dR,dG,dB); + FsrRcasInputF(eR,eG,eB); + FsrRcasInputF(fR,fG,fB); + FsrRcasInputF(hR,hG,hB); + // Luma times 2. + AF1 bL=bB*AF1_(0.5)+(bR*AF1_(0.5)+bG); + AF1 dL=dB*AF1_(0.5)+(dR*AF1_(0.5)+dG); + AF1 eL=eB*AF1_(0.5)+(eR*AF1_(0.5)+eG); + AF1 fL=fB*AF1_(0.5)+(fR*AF1_(0.5)+fG); + AF1 hL=hB*AF1_(0.5)+(hR*AF1_(0.5)+hG); + // Noise detection. + AF1 nz=AF1_(0.25)*bL+AF1_(0.25)*dL+AF1_(0.25)*fL+AF1_(0.25)*hL-eL; + nz=ASatF1(abs(nz)*APrxMedRcpF1(AMax3F1(AMax3F1(bL,dL,eL),fL,hL)-AMin3F1(AMin3F1(bL,dL,eL),fL,hL))); + nz=AF1_(-0.5)*nz+AF1_(1.0); + // Min and max of ring. + AF1 mn4R=min(AMin3F1(bR,dR,fR),hR); + AF1 mn4G=min(AMin3F1(bG,dG,fG),hG); + AF1 mn4B=min(AMin3F1(bB,dB,fB),hB); + AF1 mx4R=max(AMax3F1(bR,dR,fR),hR); + AF1 mx4G=max(AMax3F1(bG,dG,fG),hG); + AF1 mx4B=max(AMax3F1(bB,dB,fB),hB); + // Immediate constants for peak range. + AF2 peakC=AF2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AF1 hitMinR=min(mn4R,eR)*ARcpF1(AF1_(4.0)*mx4R); + AF1 hitMinG=min(mn4G,eG)*ARcpF1(AF1_(4.0)*mx4G); + AF1 hitMinB=min(mn4B,eB)*ARcpF1(AF1_(4.0)*mx4B); + AF1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpF1(AF1_(4.0)*mn4R+peakC.y); + AF1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpF1(AF1_(4.0)*mn4G+peakC.y); + AF1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpF1(AF1_(4.0)*mn4B+peakC.y); + AF1 lobeR=max(-hitMinR,hitMaxR); + AF1 lobeG=max(-hitMinG,hitMaxG); + AF1 lobeB=max(-hitMinB,hitMaxB); + AF1 lobe=max(AF1_(-FSR_RCAS_LIMIT),min(AMax3F1(lobeR,lobeG,lobeB),AF1_(0.0)))*AF1_AU1(con.x); + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AF1 rcpL=APrxMedRcpF1(AF1_(4.0)*lobe+AF1_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL; + return;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// NON-PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_RCAS_H) + // Input callback prototypes that need to be implemented by calling shader + AH4 FsrRcasLoadH(ASW2 p); + void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b); +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasH( + out AH1 pixR, // Output values, non-vector so port between RcasFilter() and RcasFilterH() is easy. + out AH1 pixG, + out AH1 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AH1 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // Sharpening algorithm uses minimal 3x3 pixel neighborhood. + // b + // d e f + // h + ASW2 sp=ASW2(ip); + AH3 b=FsrRcasLoadH(sp+ASW2( 0,-1)).rgb; + AH3 d=FsrRcasLoadH(sp+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee=FsrRcasLoadH(sp); + AH3 e=ee.rgb;pixA=ee.a; + #else + AH3 e=FsrRcasLoadH(sp).rgb; + #endif + AH3 f=FsrRcasLoadH(sp+ASW2( 1, 0)).rgb; + AH3 h=FsrRcasLoadH(sp+ASW2( 0, 1)).rgb; + // Rename (32-bit) or regroup (16-bit). + AH1 bR=b.r; + AH1 bG=b.g; + AH1 bB=b.b; + AH1 dR=d.r; + AH1 dG=d.g; + AH1 dB=d.b; + AH1 eR=e.r; + AH1 eG=e.g; + AH1 eB=e.b; + AH1 fR=f.r; + AH1 fG=f.g; + AH1 fB=f.b; + AH1 hR=h.r; + AH1 hG=h.g; + AH1 hB=h.b; + // Run optional input transform. + FsrRcasInputH(bR,bG,bB); + FsrRcasInputH(dR,dG,dB); + FsrRcasInputH(eR,eG,eB); + FsrRcasInputH(fR,fG,fB); + FsrRcasInputH(hR,hG,hB); + // Luma times 2. + AH1 bL=bB*AH1_(0.5)+(bR*AH1_(0.5)+bG); + AH1 dL=dB*AH1_(0.5)+(dR*AH1_(0.5)+dG); + AH1 eL=eB*AH1_(0.5)+(eR*AH1_(0.5)+eG); + AH1 fL=fB*AH1_(0.5)+(fR*AH1_(0.5)+fG); + AH1 hL=hB*AH1_(0.5)+(hR*AH1_(0.5)+hG); + // Noise detection. + AH1 nz=AH1_(0.25)*bL+AH1_(0.25)*dL+AH1_(0.25)*fL+AH1_(0.25)*hL-eL; + nz=ASatH1(abs(nz)*APrxMedRcpH1(AMax3H1(AMax3H1(bL,dL,eL),fL,hL)-AMin3H1(AMin3H1(bL,dL,eL),fL,hL))); + nz=AH1_(-0.5)*nz+AH1_(1.0); + // Min and max of ring. + AH1 mn4R=min(AMin3H1(bR,dR,fR),hR); + AH1 mn4G=min(AMin3H1(bG,dG,fG),hG); + AH1 mn4B=min(AMin3H1(bB,dB,fB),hB); + AH1 mx4R=max(AMax3H1(bR,dR,fR),hR); + AH1 mx4G=max(AMax3H1(bG,dG,fG),hG); + AH1 mx4B=max(AMax3H1(bB,dB,fB),hB); + // Immediate constants for peak range. + AH2 peakC=AH2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AH1 hitMinR=min(mn4R,eR)*ARcpH1(AH1_(4.0)*mx4R); + AH1 hitMinG=min(mn4G,eG)*ARcpH1(AH1_(4.0)*mx4G); + AH1 hitMinB=min(mn4B,eB)*ARcpH1(AH1_(4.0)*mx4B); + AH1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH1(AH1_(4.0)*mn4R+peakC.y); + AH1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH1(AH1_(4.0)*mn4G+peakC.y); + AH1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH1(AH1_(4.0)*mn4B+peakC.y); + AH1 lobeR=max(-hitMinR,hitMaxR); + AH1 lobeG=max(-hitMinG,hitMaxG); + AH1 lobeB=max(-hitMinB,hitMaxB); + AH1 lobe=max(AH1_(-FSR_RCAS_LIMIT),min(AMax3H1(lobeR,lobeG,lobeB),AH1_(0.0)))*AH2_AU1(con.y).x; + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AH1 rcpL=APrxMedRcpH1(AH1_(4.0)*lobe+AH1_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PACKED 16-BIT VERSION +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF)&&defined(FSR_RCAS_HX2) + // Input callback prototypes that need to be implemented by the calling shader + AH4 FsrRcasLoadHx2(ASW2 p); + void FsrRcasInputHx2(inout AH2 r,inout AH2 g,inout AH2 b); +//------------------------------------------------------------------------------------------------------------------------------ + // Can be used to convert from packed Structures of Arrays to Arrays of Structures for store. + void FsrRcasDepackHx2(out AH4 pix0,out AH4 pix1,AH2 pixR,AH2 pixG,AH2 pixB){ + #ifdef A_HLSL + // Invoke a slower path for DX only, since it won't allow uninitialized values. + pix0.a=pix1.a=0.0; + #endif + pix0.rgb=AH3(pixR.x,pixG.x,pixB.x); + pix1.rgb=AH3(pixR.y,pixG.y,pixB.y);} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrRcasHx2( + // Output values are for 2 8x8 tiles in a 16x8 region. + // pix.x = left 8x8 tile + // pix.y = right 8x8 tile + // This enables later processing to easily be packed as well. + out AH2 pixR, + out AH2 pixG, + out AH2 pixB, + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + out AH2 pixA, + #endif + AU2 ip, // Integer pixel position in output. + AU4 con){ // Constant generated by RcasSetup(). + // No scaling algorithm uses minimal 3x3 pixel neighborhood. + ASW2 sp0=ASW2(ip); + AH3 b0=FsrRcasLoadHx2(sp0+ASW2( 0,-1)).rgb; + AH3 d0=FsrRcasLoadHx2(sp0+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee0=FsrRcasLoadHx2(sp0); + AH3 e0=ee0.rgb;pixA.r=ee0.a; + #else + AH3 e0=FsrRcasLoadHx2(sp0).rgb; + #endif + AH3 f0=FsrRcasLoadHx2(sp0+ASW2( 1, 0)).rgb; + AH3 h0=FsrRcasLoadHx2(sp0+ASW2( 0, 1)).rgb; + ASW2 sp1=sp0+ASW2(8,0); + AH3 b1=FsrRcasLoadHx2(sp1+ASW2( 0,-1)).rgb; + AH3 d1=FsrRcasLoadHx2(sp1+ASW2(-1, 0)).rgb; + #ifdef FSR_RCAS_PASSTHROUGH_ALPHA + AH4 ee1=FsrRcasLoadHx2(sp1); + AH3 e1=ee1.rgb;pixA.g=ee1.a; + #else + AH3 e1=FsrRcasLoadHx2(sp1).rgb; + #endif + AH3 f1=FsrRcasLoadHx2(sp1+ASW2( 1, 0)).rgb; + AH3 h1=FsrRcasLoadHx2(sp1+ASW2( 0, 1)).rgb; + // Arrays of Structures to Structures of Arrays conversion. + AH2 bR=AH2(b0.r,b1.r); + AH2 bG=AH2(b0.g,b1.g); + AH2 bB=AH2(b0.b,b1.b); + AH2 dR=AH2(d0.r,d1.r); + AH2 dG=AH2(d0.g,d1.g); + AH2 dB=AH2(d0.b,d1.b); + AH2 eR=AH2(e0.r,e1.r); + AH2 eG=AH2(e0.g,e1.g); + AH2 eB=AH2(e0.b,e1.b); + AH2 fR=AH2(f0.r,f1.r); + AH2 fG=AH2(f0.g,f1.g); + AH2 fB=AH2(f0.b,f1.b); + AH2 hR=AH2(h0.r,h1.r); + AH2 hG=AH2(h0.g,h1.g); + AH2 hB=AH2(h0.b,h1.b); + // Run optional input transform. + FsrRcasInputHx2(bR,bG,bB); + FsrRcasInputHx2(dR,dG,dB); + FsrRcasInputHx2(eR,eG,eB); + FsrRcasInputHx2(fR,fG,fB); + FsrRcasInputHx2(hR,hG,hB); + // Luma times 2. + AH2 bL=bB*AH2_(0.5)+(bR*AH2_(0.5)+bG); + AH2 dL=dB*AH2_(0.5)+(dR*AH2_(0.5)+dG); + AH2 eL=eB*AH2_(0.5)+(eR*AH2_(0.5)+eG); + AH2 fL=fB*AH2_(0.5)+(fR*AH2_(0.5)+fG); + AH2 hL=hB*AH2_(0.5)+(hR*AH2_(0.5)+hG); + // Noise detection. + AH2 nz=AH2_(0.25)*bL+AH2_(0.25)*dL+AH2_(0.25)*fL+AH2_(0.25)*hL-eL; + nz=ASatH2(abs(nz)*APrxMedRcpH2(AMax3H2(AMax3H2(bL,dL,eL),fL,hL)-AMin3H2(AMin3H2(bL,dL,eL),fL,hL))); + nz=AH2_(-0.5)*nz+AH2_(1.0); + // Min and max of ring. + AH2 mn4R=min(AMin3H2(bR,dR,fR),hR); + AH2 mn4G=min(AMin3H2(bG,dG,fG),hG); + AH2 mn4B=min(AMin3H2(bB,dB,fB),hB); + AH2 mx4R=max(AMax3H2(bR,dR,fR),hR); + AH2 mx4G=max(AMax3H2(bG,dG,fG),hG); + AH2 mx4B=max(AMax3H2(bB,dB,fB),hB); + // Immediate constants for peak range. + AH2 peakC=AH2(1.0,-1.0*4.0); + // Limiters, these need to be high precision RCPs. + AH2 hitMinR=min(mn4R,eR)*ARcpH2(AH2_(4.0)*mx4R); + AH2 hitMinG=min(mn4G,eG)*ARcpH2(AH2_(4.0)*mx4G); + AH2 hitMinB=min(mn4B,eB)*ARcpH2(AH2_(4.0)*mx4B); + AH2 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH2(AH2_(4.0)*mn4R+peakC.y); + AH2 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH2(AH2_(4.0)*mn4G+peakC.y); + AH2 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH2(AH2_(4.0)*mn4B+peakC.y); + AH2 lobeR=max(-hitMinR,hitMaxR); + AH2 lobeG=max(-hitMinG,hitMaxG); + AH2 lobeB=max(-hitMinB,hitMaxB); + AH2 lobe=max(AH2_(-FSR_RCAS_LIMIT),min(AMax3H2(lobeR,lobeG,lobeB),AH2_(0.0)))*AH2_(AH2_AU1(con.y).x); + // Apply noise removal. + #ifdef FSR_RCAS_DENOISE + lobe*=nz; + #endif + // Resolve, which needs the medium precision rcp approximation to avoid visible tonality changes. + AH2 rcpL=APrxMedRcpH2(AH2_(4.0)*lobe+AH2_(1.0)); + pixR=(lobe*bR+lobe*dR+lobe*hR+lobe*fR+eR)*rcpL; + pixG=(lobe*bG+lobe*dG+lobe*hG+lobe*fG+eG)*rcpL; + pixB=(lobe*bB+lobe*dB+lobe*hB+lobe*fB+eB)*rcpL;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [LFGA] LINEAR FILM GRAIN APPLICATOR +// +//------------------------------------------------------------------------------------------------------------------------------ +// Adding output-resolution film grain after scaling is a good way to mask both rendering and scaling artifacts. +// Suggest using tiled blue noise as film grain input, with peak noise frequency set for a specific look and feel. +// The 'Lfga*()' functions provide a convenient way to introduce grain. +// These functions limit grain based on distance to signal limits. +// This is done so that the grain is temporally energy preserving, and thus won't modify image tonality. +// Grain application should be done in a linear colorspace. +// The grain should be temporally changing, but have a temporal sum per pixel that adds to zero (non-biased). +//------------------------------------------------------------------------------------------------------------------------------ +// Usage, +// FsrLfga*( +// color, // In/out linear colorspace color {0 to 1} ranged. +// grain, // Per pixel grain texture value {-0.5 to 0.5} ranged, input is 3-channel to support colored grain. +// amount); // Amount of grain (0 to 1} ranged. +//------------------------------------------------------------------------------------------------------------------------------ +// Example if grain texture is monochrome: 'FsrLfgaF(color,AF3_(grain),amount)' +//============================================================================================================================== +#if defined(A_GPU) + // Maximum grain is the minimum distance to the signal limit. + void FsrLfgaF(inout AF3 c,AF3 t,AF1 a){c+=(t*AF3_(a))*min(AF3_(1.0)-c,c);} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + // Half precision version (slower). + void FsrLfgaH(inout AH3 c,AH3 t,AH1 a){c+=(t*AH3_(a))*min(AH3_(1.0)-c,c);} +//------------------------------------------------------------------------------------------------------------------------------ + // Packed half precision version (faster). + void FsrLfgaHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 tR,AH2 tG,AH2 tB,AH1 a){ + cR+=(tR*AH2_(a))*min(AH2_(1.0)-cR,cR);cG+=(tG*AH2_(a))*min(AH2_(1.0)-cG,cG);cB+=(tB*AH2_(a))*min(AH2_(1.0)-cB,cB);} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [SRTM] SIMPLE REVERSIBLE TONE-MAPPER +// +//------------------------------------------------------------------------------------------------------------------------------ +// This provides a way to take linear HDR color {0 to FP16_MAX} and convert it into a temporary {0 to 1} ranged post-tonemapped linear. +// The tonemapper preserves RGB ratio, which helps maintain HDR color bleed during filtering. +//------------------------------------------------------------------------------------------------------------------------------ +// Reversible tonemapper usage, +// FsrSrtm*(color); // {0 to FP16_MAX} converted to {0 to 1}. +// FsrSrtmInv*(color); // {0 to 1} converted into {0 to 32768, output peak safe for FP16}. +//============================================================================================================================== +#if defined(A_GPU) + void FsrSrtmF(inout AF3 c){c*=AF3_(ARcpF1(AMax3F1(c.r,c.g,c.b)+AF1_(1.0)));} + // The extra max solves the c=1.0 case (which is a /0). + void FsrSrtmInvF(inout AF3 c){c*=AF3_(ARcpF1(max(AF1_(1.0/32768.0),AF1_(1.0)-AMax3F1(c.r,c.g,c.b))));} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + void FsrSrtmH(inout AH3 c){c*=AH3_(ARcpH1(AMax3H1(c.r,c.g,c.b)+AH1_(1.0)));} + void FsrSrtmInvH(inout AH3 c){c*=AH3_(ARcpH1(max(AH1_(1.0/32768.0),AH1_(1.0)-AMax3H1(c.r,c.g,c.b))));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrSrtmHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB){ + AH2 rcp=ARcpH2(AMax3H2(cR,cG,cB)+AH2_(1.0));cR*=rcp;cG*=rcp;cB*=rcp;} + void FsrSrtmInvHx2(inout AH2 cR,inout AH2 cG,inout AH2 cB){ + AH2 rcp=ARcpH2(max(AH2_(1.0/32768.0),AH2_(1.0)-AMax3H2(cR,cG,cB)));cR*=rcp;cG*=rcp;cB*=rcp;} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// FSR - [TEPD] TEMPORAL ENERGY PRESERVING DITHER +// +//------------------------------------------------------------------------------------------------------------------------------ +// Temporally energy preserving dithered {0 to 1} linear to gamma 2.0 conversion. +// Gamma 2.0 is used so that the conversion back to linear is just to square the color. +// The conversion comes in 8-bit and 10-bit modes, designed for output to 8-bit UNORM or 10:10:10:2 respectively. +// Given good non-biased temporal blue noise as dither input, +// the output dither will temporally conserve energy. +// This is done by choosing the linear nearest step point instead of perceptual nearest. +// See code below for details. +//------------------------------------------------------------------------------------------------------------------------------ +// DX SPEC RULES FOR FLOAT->UNORM 8-BIT CONVERSION +// =============================================== +// - Output is 'uint(floor(saturate(n)*255.0+0.5))'. +// - Thus rounding is to nearest. +// - NaN gets converted to zero. +// - INF is clamped to {0.0 to 1.0}. +//============================================================================================================================== +#if defined(A_GPU) + // Hand tuned integer position to dither value, with more values than simple checkerboard. + // Only 32-bit has enough precision for this compddation. + // Output is {0 to <1}. + AF1 FsrTepdDitF(AU2 p,AU1 f){ + AF1 x=AF1_(p.x+f); + AF1 y=AF1_(p.y); + // The 1.61803 golden ratio. + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + // Number designed to provide a good visual pattern. + AF1 b=AF1_(1.0/3.69); + x=x*a+(y*b); + return AFractF1(x);} +//------------------------------------------------------------------------------------------------------------------------------ + // This version is 8-bit gamma 2.0. + // The 'c' input is {0 to 1}. + // Output is {0 to 1} ready for image store. + void FsrTepdC8F(inout AF3 c,AF1 dit){ + AF3 n=sqrt(c); + n=floor(n*AF3_(255.0))*AF3_(1.0/255.0); + AF3 a=n*n; + AF3 b=n+AF3_(1.0/255.0);b=b*b; + // Ratio of 'a' to 'b' required to produce 'c'. + // APrxLoRcpF1() won't work here (at least for very high dynamic ranges). + // APrxMedRcpF1() is an IADD,FMA,MUL. + AF3 r=(c-b)*APrxMedRcpF3(a-b); + // Use the ratio as a cutoff to choose 'a' or 'b'. + // AGtZeroF1() is a MUL. + c=ASatF3(n+AGtZeroF3(AF3_(dit)-r)*AF3_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + // This version is 10-bit gamma 2.0. + // The 'c' input is {0 to 1}. + // Output is {0 to 1} ready for image store. + void FsrTepdC10F(inout AF3 c,AF1 dit){ + AF3 n=sqrt(c); + n=floor(n*AF3_(1023.0))*AF3_(1.0/1023.0); + AF3 a=n*n; + AF3 b=n+AF3_(1.0/1023.0);b=b*b; + AF3 r=(c-b)*APrxMedRcpF3(a-b); + c=ASatF3(n+AGtZeroF3(AF3_(dit)-r)*AF3_(1.0/1023.0));} +#endif +//============================================================================================================================== +#if defined(A_GPU)&&defined(A_HALF) + AH1 FsrTepdDitH(AU2 p,AU1 f){ + AF1 x=AF1_(p.x+f); + AF1 y=AF1_(p.y); + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + AF1 b=AF1_(1.0/3.69); + x=x*a+(y*b); + return AH1(AFractF1(x));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC8H(inout AH3 c,AH1 dit){ + AH3 n=sqrt(c); + n=floor(n*AH3_(255.0))*AH3_(1.0/255.0); + AH3 a=n*n; + AH3 b=n+AH3_(1.0/255.0);b=b*b; + AH3 r=(c-b)*APrxMedRcpH3(a-b); + c=ASatH3(n+AGtZeroH3(AH3_(dit)-r)*AH3_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC10H(inout AH3 c,AH1 dit){ + AH3 n=sqrt(c); + n=floor(n*AH3_(1023.0))*AH3_(1.0/1023.0); + AH3 a=n*n; + AH3 b=n+AH3_(1.0/1023.0);b=b*b; + AH3 r=(c-b)*APrxMedRcpH3(a-b); + c=ASatH3(n+AGtZeroH3(AH3_(dit)-r)*AH3_(1.0/1023.0));} +//============================================================================================================================== + // This computes dither for positions 'p' and 'p+{8,0}'. + AH2 FsrTepdDitHx2(AU2 p,AU1 f){ + AF2 x; + x.x=AF1_(p.x+f); + x.y=x.x+AF1_(8.0); + AF1 y=AF1_(p.y); + AF1 a=AF1_((1.0+sqrt(5.0))/2.0); + AF1 b=AF1_(1.0/3.69); + x=x*AF2_(a)+AF2_(y*b); + return AH2(AFractF2(x));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC8Hx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 dit){ + AH2 nR=sqrt(cR); + AH2 nG=sqrt(cG); + AH2 nB=sqrt(cB); + nR=floor(nR*AH2_(255.0))*AH2_(1.0/255.0); + nG=floor(nG*AH2_(255.0))*AH2_(1.0/255.0); + nB=floor(nB*AH2_(255.0))*AH2_(1.0/255.0); + AH2 aR=nR*nR; + AH2 aG=nG*nG; + AH2 aB=nB*nB; + AH2 bR=nR+AH2_(1.0/255.0);bR=bR*bR; + AH2 bG=nG+AH2_(1.0/255.0);bG=bG*bG; + AH2 bB=nB+AH2_(1.0/255.0);bB=bB*bB; + AH2 rR=(cR-bR)*APrxMedRcpH2(aR-bR); + AH2 rG=(cG-bG)*APrxMedRcpH2(aG-bG); + AH2 rB=(cB-bB)*APrxMedRcpH2(aB-bB); + cR=ASatH2(nR+AGtZeroH2(dit-rR)*AH2_(1.0/255.0)); + cG=ASatH2(nG+AGtZeroH2(dit-rG)*AH2_(1.0/255.0)); + cB=ASatH2(nB+AGtZeroH2(dit-rB)*AH2_(1.0/255.0));} +//------------------------------------------------------------------------------------------------------------------------------ + void FsrTepdC10Hx2(inout AH2 cR,inout AH2 cG,inout AH2 cB,AH2 dit){ + AH2 nR=sqrt(cR); + AH2 nG=sqrt(cG); + AH2 nB=sqrt(cB); + nR=floor(nR*AH2_(1023.0))*AH2_(1.0/1023.0); + nG=floor(nG*AH2_(1023.0))*AH2_(1.0/1023.0); + nB=floor(nB*AH2_(1023.0))*AH2_(1.0/1023.0); + AH2 aR=nR*nR; + AH2 aG=nG*nG; + AH2 aB=nB*nB; + AH2 bR=nR+AH2_(1.0/1023.0);bR=bR*bR; + AH2 bG=nG+AH2_(1.0/1023.0);bG=bG*bG; + AH2 bB=nB+AH2_(1.0/1023.0);bB=bB*bB; + AH2 rR=(cR-bR)*APrxMedRcpH2(aR-bR); + AH2 rG=(cG-bG)*APrxMedRcpH2(aG-bG); + AH2 rB=(cB-bB)*APrxMedRcpH2(aB-bB); + cR=ASatH2(nR+AGtZeroH2(dit-rR)*AH2_(1.0/1023.0)); + cG=ASatH2(nG+AGtZeroH2(dit-rG)*AH2_(1.0/1023.0)); + cB=ASatH2(nB+AGtZeroH2(dit-rB)*AH2_(1.0/1023.0));} +#endif diff --git a/src/video_core/host_shaders/post_process.frag b/src/video_core/host_shaders/post_process.frag index d222d070c..fb0917528 100644 --- a/src/video_core/host_shaders/post_process.frag +++ b/src/video_core/host_shaders/post_process.frag @@ -8,7 +8,7 @@ layout (location = 0) out vec4 color; layout (binding = 0) uniform sampler2D texSampler; -layout(push_constant) uniform settings { +layout (push_constant) uniform settings { float gamma; bool hdr; } pp; diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in index 43bf5b0c5..3472b2837 100644 --- a/src/video_core/host_shaders/source_shader.h.in +++ b/src/video_core/host_shaders/source_shader.h.in @@ -7,8 +7,8 @@ namespace HostShaders { -constexpr std::string_view @CONTENTS_NAME@ = { +constexpr std::string_view @CONTENTS_NAME@ = R"shader_src( @CONTENTS@ -}; +)shader_src"; } // namespace HostShaders diff --git a/src/video_core/renderer_vulkan/host_passes/fsr_pass.cpp b/src/video_core/renderer_vulkan/host_passes/fsr_pass.cpp new file mode 100644 index 000000000..1c54207e0 --- /dev/null +++ b/src/video_core/renderer_vulkan/host_passes/fsr_pass.cpp @@ -0,0 +1,445 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "fsr_pass.h" + +#include "common/assert.h" +#include "video_core/host_shaders/fsr_comp.h" +#include "video_core/renderer_vulkan/vk_platform.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" + +#define A_CPU +#include "core/debug_state.h" +#include "video_core/host_shaders/fsr/ffx_a.h" +#include "video_core/host_shaders/fsr/ffx_fsr1.h" + +typedef u32 uvec4[4]; + +struct FSRConstants { + uvec4 Const0; + uvec4 Const1; + uvec4 Const2; + uvec4 Const3; + uvec4 Sample; +}; + +namespace Vulkan::HostPasses { + +void FsrPass::Create(vk::Device device, VmaAllocator allocator, u32 num_images) { + this->device = device; + this->num_images = num_images; + + sampler = Check<"create upscaling sampler">(device.createSamplerUnique(vk::SamplerCreateInfo{ + .magFilter = vk::Filter::eLinear, + .minFilter = vk::Filter::eLinear, + .mipmapMode = vk::SamplerMipmapMode::eNearest, + .addressModeU = vk::SamplerAddressMode::eClampToEdge, + .addressModeV = vk::SamplerAddressMode::eClampToEdge, + .addressModeW = vk::SamplerAddressMode::eClampToEdge, + .maxAnisotropy = 1.0f, + .minLod = -1000.0f, + .maxLod = 1000.0f, + })); + + std::array layoutBindings{{ + { + .binding = 0, + .descriptorType = vk::DescriptorType::eSampledImage, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute, + }, + { + .binding = 1, + .descriptorType = vk::DescriptorType::eStorageImage, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute, + }, + { + .binding = 2, + .descriptorType = vk::DescriptorType::eSampler, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute, + .pImmutableSamplers = &sampler.get(), + }, + }}; + + descriptor_set_layout = + Check<"create fsr descriptor set layout">(device.createDescriptorSetLayoutUnique({ + .flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptor, + .bindingCount = layoutBindings.size(), + .pBindings = layoutBindings.data(), + })); + + const vk::PushConstantRange push_constants{ + .stageFlags = vk::ShaderStageFlagBits::eCompute, + .offset = 0, + .size = sizeof(FSRConstants), + }; + + const auto& cs_easu_module = + Compile(HostShaders::FSR_COMP, vk::ShaderStageFlagBits::eCompute, device, + { + "SAMPLE_EASU=1", + }); + ASSERT(cs_easu_module); + SetObjectName(device, cs_easu_module, "fsr.comp [EASU]"); + + const auto& cs_rcas_module = + Compile(HostShaders::FSR_COMP, vk::ShaderStageFlagBits::eCompute, device, + { + "SAMPLE_RCAS=1", + }); + ASSERT(cs_rcas_module); + SetObjectName(device, cs_rcas_module, "fsr.comp [RCAS]"); + + pipeline_layout = Check<"fsp pipeline layout">(device.createPipelineLayoutUnique({ + .setLayoutCount = 1, + .pSetLayouts = &descriptor_set_layout.get(), + .pushConstantRangeCount = 1, + .pPushConstantRanges = &push_constants, + })); + SetObjectName(device, pipeline_layout.get(), "fsr pipeline layout"); + + const vk::ComputePipelineCreateInfo easu_pinfo{ + .stage{ + .stage = vk::ShaderStageFlagBits::eCompute, + .module = cs_easu_module, + .pName = "main", + }, + .layout = pipeline_layout.get(), + }; + easu_pipeline = + Check<"fsp easu compute pipelines">(device.createComputePipelineUnique({}, easu_pinfo)); + SetObjectName(device, easu_pipeline.get(), "fsr easu pipeline"); + + const vk::ComputePipelineCreateInfo rcas_pinfo{ + .stage{ + .stage = vk::ShaderStageFlagBits::eCompute, + .module = cs_rcas_module, + .pName = "main", + }, + .layout = pipeline_layout.get(), + }; + rcas_pipeline = + Check<"fsp rcas compute pipelines">(device.createComputePipelineUnique({}, rcas_pinfo)); + SetObjectName(device, rcas_pipeline.get(), "fsr rcas pipeline"); + + device.destroyShaderModule(cs_easu_module); + device.destroyShaderModule(cs_rcas_module); + + available_imgs.resize(num_images); + for (int i = 0; i < num_images; ++i) { + auto& img = available_imgs[i]; + img.id = i; + img.intermediary_image = VideoCore::UniqueImage(device, allocator); + img.output_image = VideoCore::UniqueImage(device, allocator); + } +} + +vk::ImageView FsrPass::Render(vk::CommandBuffer cmdbuf, vk::ImageView input, + vk::Extent2D input_size, vk::Extent2D output_size, Settings settings, + bool hdr) { + if (!settings.enable) { + DebugState.is_using_fsr = false; + return input; + } + if (input_size.width >= output_size.width && input_size.height >= output_size.height) { + DebugState.is_using_fsr = false; + return input; + } + + DebugState.is_using_fsr = true; + + if (output_size != cur_size) { + ResizeAndInvalidate(output_size.width, output_size.height); + } + auto [width, height] = cur_size; + + auto& img = available_imgs[cur_image]; + if (++cur_image >= available_imgs.size()) { + cur_image = 0; + } + + if (img.dirty) { + CreateImages(img); + } + + static const int thread_group_work_region_dim = 16; + int dispatch_x = (width + (thread_group_work_region_dim - 1)) / thread_group_work_region_dim; + int dispatch_y = (height + (thread_group_work_region_dim - 1)) / thread_group_work_region_dim; + + constexpr vk::ImageSubresourceRange simple_subresource = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .levelCount = 1, + .layerCount = 1, + }; + const std::array enter_barrier{ + vk::ImageMemoryBarrier2{ + .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .srcAccessMask = vk::AccessFlagBits2::eShaderRead, + .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .dstAccessMask = vk::AccessFlagBits2::eShaderWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eGeneral, + .image = img.intermediary_image, + .subresourceRange = simple_subresource, + }, + }; + cmdbuf.pipelineBarrier2({ + .imageMemoryBarrierCount = enter_barrier.size(), + .pImageMemoryBarriers = enter_barrier.data(), + }); + + FSRConstants consts{}; + FsrEasuCon(reinterpret_cast(&consts.Const0), reinterpret_cast(&consts.Const1), + reinterpret_cast(&consts.Const2), reinterpret_cast(&consts.Const3), + static_cast(input_size.width), static_cast(input_size.height), + static_cast(input_size.width), static_cast(input_size.height), (AF1)width, + (AF1)height); + consts.Sample[0] = hdr && !settings.use_rcas ? 1 : 0; + + if (settings.use_rcas) { + + { // easu + std::array img_info{{ + { + .imageView = input, + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }, + { + .imageView = img.intermediary_image_view.get(), + .imageLayout = vk::ImageLayout::eGeneral, + }, + { + .sampler = sampler.get(), + }, + }}; + + std::array set_writes{{ + { + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampledImage, + .pImageInfo = &img_info[0], + }, + { + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageImage, + .pImageInfo = &img_info[1], + }, + { + .dstBinding = 2, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampler, + .pImageInfo = &img_info[2], + }, + }}; + + cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, easu_pipeline.get()); + cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, pipeline_layout.get(), 0, + set_writes); + cmdbuf.pushConstants(pipeline_layout.get(), vk::ShaderStageFlagBits::eCompute, 0, + sizeof(FSRConstants), &consts); + cmdbuf.dispatch(dispatch_x, dispatch_y, 1); + } + + std::array img_barrier{ + vk::ImageMemoryBarrier2{ + .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .dstAccessMask = vk::AccessFlagBits2::eShaderRead, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + .image = img.intermediary_image, + .subresourceRange = simple_subresource, + }, + vk::ImageMemoryBarrier2{ + .srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe, + .srcAccessMask = vk::AccessFlagBits2::eNone, + .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .dstAccessMask = vk::AccessFlagBits2::eShaderStorageWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eGeneral, + .image = img.output_image, + .subresourceRange = simple_subresource, + }, + }; + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .imageMemoryBarrierCount = img_barrier.size(), + .pImageMemoryBarriers = img_barrier.data(), + }); + + { // rcas + consts = {}; + FsrRcasCon(reinterpret_cast(&consts.Const0), settings.rcas_attenuation); + consts.Sample[0] = hdr ? 1 : 0; + + std::array img_info{{ + { + .imageView = img.intermediary_image_view.get(), + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }, + { + .imageView = img.output_image_view.get(), + .imageLayout = vk::ImageLayout::eGeneral, + }, + { + .sampler = sampler.get(), + }, + }}; + + std::array set_writes{{ + { + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampledImage, + .pImageInfo = &img_info[0], + }, + { + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageImage, + .pImageInfo = &img_info[1], + }, + { + .dstBinding = 2, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampler, + .pImageInfo = &img_info[2], + }, + }}; + + cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, rcas_pipeline.get()); + cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, pipeline_layout.get(), 0, + set_writes); + cmdbuf.pushConstants(pipeline_layout.get(), vk::ShaderStageFlagBits::eCompute, 0, + sizeof(FSRConstants), &consts); + cmdbuf.dispatch(dispatch_x, dispatch_y, 1); + } + + } else { + // only easu + std::array img_info{{ + { + .imageView = input, + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }, + { + .imageView = img.output_image_view.get(), + .imageLayout = vk::ImageLayout::eGeneral, + }, + { + .sampler = sampler.get(), + }, + }}; + + std::array set_writes{{ + { + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampledImage, + .pImageInfo = &img_info[0], + }, + { + .dstBinding = 1, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageImage, + .pImageInfo = &img_info[1], + }, + { + .dstBinding = 2, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eSampler, + .pImageInfo = &img_info[2], + }, + }}; + + cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, easu_pipeline.get()); + cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, pipeline_layout.get(), 0, + set_writes); + cmdbuf.pushConstants(pipeline_layout.get(), vk::ShaderStageFlagBits::eCompute, 0, + sizeof(FSRConstants), &consts); + cmdbuf.dispatch(dispatch_x, dispatch_y, 1); + } + + const std::array return_barrier{ + vk::ImageMemoryBarrier2{ + .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eShaderRead, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + .image = img.output_image, + .subresourceRange = simple_subresource, + }, + }; + cmdbuf.pipelineBarrier2({ + .imageMemoryBarrierCount = return_barrier.size(), + .pImageMemoryBarriers = return_barrier.data(), + }); + + return img.output_image_view.get(); +} + +void FsrPass::ResizeAndInvalidate(u32 width, u32 height) { + this->cur_size = vk::Extent2D{ + .width = width, + .height = height, + }; + for (int i = 0; i < num_images; ++i) { + available_imgs[i].dirty = true; + } +} + +void FsrPass::CreateImages(Img& img) const { + img.dirty = false; + + vk::ImageCreateInfo image_create_info{ + .imageType = vk::ImageType::e2D, + .format = vk::Format::eR16G16B16A16Sfloat, + .extent{ + .width = cur_size.width, + .height = cur_size.height, + .depth = 1, + }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = vk::SampleCountFlagBits::e1, + // .tiling = vk::ImageTiling::eOptimal, + .usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eStorage, + .initialLayout = vk::ImageLayout::eUndefined, + }; + img.intermediary_image.Create(image_create_info); + SetObjectName(device, static_cast(img.intermediary_image), + "FSR Intermediary Image #{}", img.id); + image_create_info.usage = vk::ImageUsageFlagBits::eTransferSrc | + vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eStorage | + vk::ImageUsageFlagBits::eColorAttachment; + img.output_image.Create(image_create_info); + SetObjectName(device, static_cast(img.output_image), "FSR Output Image #{}", img.id); + + vk::ImageViewCreateInfo image_view_create_info{ + .image = img.intermediary_image, + .viewType = vk::ImageViewType::e2D, + .format = vk::Format::eR16G16B16A16Sfloat, + .subresourceRange{ + .aspectMask = vk::ImageAspectFlagBits::eColor, + .levelCount = 1, + .layerCount = 1, + }, + }; + img.intermediary_image_view = Check<"create fsr intermediary image view">( + device.createImageViewUnique(image_view_create_info)); + SetObjectName(device, img.intermediary_image_view.get(), "FSR Intermediary ImageView #{}", + img.id); + + image_view_create_info.image = img.output_image; + img.output_image_view = + Check<"create fsr output image view">(device.createImageViewUnique(image_view_create_info)); + SetObjectName(device, img.output_image_view.get(), "FSR Output ImageView #{}", img.id); +} + +} // namespace Vulkan::HostPasses \ No newline at end of file diff --git a/src/video_core/renderer_vulkan/host_passes/fsr_pass.h b/src/video_core/renderer_vulkan/host_passes/fsr_pass.h new file mode 100644 index 000000000..3d48d85be --- /dev/null +++ b/src/video_core/renderer_vulkan/host_passes/fsr_pass.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" +#include "video_core/renderer_vulkan/vk_common.h" +#include "video_core/texture_cache/image.h" + +namespace Vulkan::HostPasses { + +class FsrPass { +public: + struct Settings { + bool enable{true}; + bool use_rcas{true}; + float rcas_attenuation{0.25f}; + }; + + void Create(vk::Device device, VmaAllocator allocator, u32 num_images); + + vk::ImageView Render(vk::CommandBuffer cmdbuf, vk::ImageView input, vk::Extent2D input_size, + vk::Extent2D output_size, Settings settings, bool hdr); + +private: + struct Img { + u8 id{}; + bool dirty{true}; + + VideoCore::UniqueImage intermediary_image; + vk::UniqueImageView intermediary_image_view; + + VideoCore::UniqueImage output_image; + vk::UniqueImageView output_image_view; + }; + + void ResizeAndInvalidate(u32 width, u32 height); + void CreateImages(Img& img) const; + + vk::Device device{}; + u32 num_images{}; + + vk::UniqueDescriptorSetLayout descriptor_set_layout{}; + vk::UniqueDescriptorSet easu_descriptor_set{}; + vk::UniqueDescriptorSet rcas_descriptor_set{}; + vk::UniqueSampler sampler{}; + vk::UniquePipelineLayout pipeline_layout{}; + vk::UniquePipeline easu_pipeline{}; + vk::UniquePipeline rcas_pipeline{}; + + vk::Extent2D cur_size{}; + u32 cur_image{}; + std::vector available_imgs; +}; + +} // namespace Vulkan::HostPasses diff --git a/src/video_core/renderer_vulkan/host_passes/pp_pass.cpp b/src/video_core/renderer_vulkan/host_passes/pp_pass.cpp new file mode 100644 index 000000000..c854e124f --- /dev/null +++ b/src/video_core/renderer_vulkan/host_passes/pp_pass.cpp @@ -0,0 +1,255 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "pp_pass.h" + +#include "common/assert.h" +#include "video_core/host_shaders/fs_tri_vert.h" +#include "video_core/host_shaders/post_process_frag.h" +#include "video_core/renderer_vulkan/vk_platform.h" +#include "video_core/renderer_vulkan/vk_presenter.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" + +#include + +namespace Vulkan::HostPasses { + +void PostProcessingPass::Create(vk::Device device) { + static const std::array pp_shaders{ + HostShaders::FS_TRI_VERT, + HostShaders::POST_PROCESS_FRAG, + }; + + boost::container::static_vector bindings{ + { + .binding = 0, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eFragment, + }, + }; + + const vk::DescriptorSetLayoutCreateInfo desc_layout_ci{ + .flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR, + .bindingCount = static_cast(bindings.size()), + .pBindings = bindings.data(), + }; + + desc_set_layout = Check<"create pp descriptor set layout">( + device.createDescriptorSetLayoutUnique(desc_layout_ci)); + + const vk::PushConstantRange push_constants{ + .stageFlags = vk::ShaderStageFlagBits::eFragment, + .offset = 0, + .size = sizeof(Settings), + }; + + const auto& vs_module = Compile(pp_shaders[0], vk::ShaderStageFlagBits::eVertex, device); + ASSERT(vs_module); + SetObjectName(device, vs_module, "fs_tri.vert"); + + const auto& fs_module = Compile(pp_shaders[1], vk::ShaderStageFlagBits::eFragment, device); + ASSERT(fs_module); + SetObjectName(device, fs_module, "post_process.frag"); + + const std::array shaders_ci{ + vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eVertex, + .module = vs_module, + .pName = "main", + }, + vk::PipelineShaderStageCreateInfo{ + .stage = vk::ShaderStageFlagBits::eFragment, + .module = fs_module, + .pName = "main", + }, + }; + + const vk::PipelineLayoutCreateInfo layout_info{ + .setLayoutCount = 1U, + .pSetLayouts = &*desc_set_layout, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &push_constants, + }; + + pipeline_layout = + Check<"create pp pipeline layout">(device.createPipelineLayoutUnique(layout_info)); + + const std::array pp_color_formats{ + vk::Format::eB8G8R8A8Unorm, // swapchain.GetSurfaceFormat().format, + }; + const vk::PipelineRenderingCreateInfoKHR pipeline_rendering_ci{ + .colorAttachmentCount = pp_color_formats.size(), + .pColorAttachmentFormats = pp_color_formats.data(), + }; + + const vk::PipelineVertexInputStateCreateInfo vertex_input_info{ + .vertexBindingDescriptionCount = 0u, + .vertexAttributeDescriptionCount = 0u, + }; + + const vk::PipelineInputAssemblyStateCreateInfo input_assembly{ + .topology = vk::PrimitiveTopology::eTriangleList, + }; + + const vk::Viewport viewport{ + .x = 0.0f, + .y = 0.0f, + .width = 1.0f, + .height = 1.0f, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + + const vk::Rect2D scissor = { + .offset = {0, 0}, + .extent = {1, 1}, + }; + + const vk::PipelineViewportStateCreateInfo viewport_info{ + .viewportCount = 1, + .pViewports = &viewport, + .scissorCount = 1, + .pScissors = &scissor, + }; + + const vk::PipelineRasterizationStateCreateInfo raster_state{ + .depthClampEnable = false, + .rasterizerDiscardEnable = false, + .polygonMode = vk::PolygonMode::eFill, + .cullMode = vk::CullModeFlagBits::eBack, + .frontFace = vk::FrontFace::eClockwise, + .depthBiasEnable = false, + .lineWidth = 1.0f, + }; + + const vk::PipelineMultisampleStateCreateInfo multisampling{ + .rasterizationSamples = vk::SampleCountFlagBits::e1, + }; + + const std::array attachments{ + vk::PipelineColorBlendAttachmentState{ + .blendEnable = false, + .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, + }, + }; + + const vk::PipelineColorBlendStateCreateInfo color_blending{ + .logicOpEnable = false, + .logicOp = vk::LogicOp::eCopy, + .attachmentCount = attachments.size(), + .pAttachments = attachments.data(), + .blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}, + }; + + const std::array dynamic_states{ + vk::DynamicState::eViewport, + vk::DynamicState::eScissor, + }; + + const vk::PipelineDynamicStateCreateInfo dynamic_info{ + .dynamicStateCount = dynamic_states.size(), + .pDynamicStates = dynamic_states.data(), + }; + + const vk::GraphicsPipelineCreateInfo pipeline_info{ + .pNext = &pipeline_rendering_ci, + .stageCount = shaders_ci.size(), + .pStages = shaders_ci.data(), + .pVertexInputState = &vertex_input_info, + .pInputAssemblyState = &input_assembly, + .pViewportState = &viewport_info, + .pRasterizationState = &raster_state, + .pMultisampleState = &multisampling, + .pColorBlendState = &color_blending, + .pDynamicState = &dynamic_info, + .layout = *pipeline_layout, + }; + + pipeline = Check<"create post process pipeline">(device.createGraphicsPipelineUnique( + /*pipeline_cache*/ {}, pipeline_info)); + + // Once pipeline is compiled, we don't need the shader module anymore + device.destroyShaderModule(vs_module); + device.destroyShaderModule(fs_module); + + // Create sampler resource + const vk::SamplerCreateInfo sampler_ci{ + .magFilter = vk::Filter::eLinear, + .minFilter = vk::Filter::eLinear, + .mipmapMode = vk::SamplerMipmapMode::eNearest, + .addressModeU = vk::SamplerAddressMode::eClampToEdge, + .addressModeV = vk::SamplerAddressMode::eClampToEdge, + }; + sampler = Check<"create pp sampler">(device.createSamplerUnique(sampler_ci)); +} + +void PostProcessingPass::Render(vk::CommandBuffer cmdbuf, vk::ImageView input, + vk::Extent2D input_size, Frame& frame, Settings settings) { + const std::array attachments{{ + { + .imageView = frame.image_view, + .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, + .loadOp = vk::AttachmentLoadOp::eClear, + .storeOp = vk::AttachmentStoreOp::eStore, + }, + }}; + const vk::RenderingInfo rendering_info{ + .renderArea{ + .extent{ + .width = frame.width, + .height = frame.height, + }, + }, + .layerCount = 1, + .colorAttachmentCount = attachments.size(), + .pColorAttachments = attachments.data(), + }; + + vk::DescriptorImageInfo image_info{ + .sampler = *sampler, + .imageView = input, + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }; + + const std::array set_writes{ + vk::WriteDescriptorSet{ + .dstSet = VK_NULL_HANDLE, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &image_info, + }, + }; + + cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, *pipeline); + + const std::array viewports = { + vk::Viewport{ + .width = static_cast(frame.width), + .height = static_cast(frame.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }, + }; + + cmdbuf.setViewport(0, viewports); + cmdbuf.setScissor(0, vk::Rect2D{ + .extent{ + .width = frame.width, + .height = frame.height, + }, + }); + + cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, set_writes); + cmdbuf.pushConstants(*pipeline_layout, vk::ShaderStageFlagBits::eFragment, 0, sizeof(Settings), + &settings); + + cmdbuf.beginRendering(rendering_info); + cmdbuf.draw(3, 1, 0, 0); + cmdbuf.endRendering(); +} + +} // namespace Vulkan::HostPasses \ No newline at end of file diff --git a/src/video_core/renderer_vulkan/host_passes/pp_pass.h b/src/video_core/renderer_vulkan/host_passes/pp_pass.h new file mode 100644 index 000000000..6127bb5c1 --- /dev/null +++ b/src/video_core/renderer_vulkan/host_passes/pp_pass.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" +#include "video_core/renderer_vulkan/vk_common.h" + +namespace Vulkan { +class Frame; +} + +namespace Vulkan::HostPasses { + +class PostProcessingPass { +public: + struct Settings { + float gamma = 1.0f; + u32 hdr = 0; + }; + + void Create(vk::Device device); + + void Render(vk::CommandBuffer cmdbuf, vk::ImageView input, vk::Extent2D input_size, + Frame& output, Settings settings); + +private: + vk::UniquePipeline pipeline{}; + vk::UniquePipelineLayout pipeline_layout{}; + vk::UniqueDescriptorSetLayout desc_set_layout{}; + vk::UniqueSampler sampler{}; +}; + +} // namespace Vulkan::HostPasses diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index a9fcd03a9..42da7aa06 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -104,7 +104,7 @@ static inline vk::Format PromoteFormatToDepth(vk::Format fmt) { } else if (fmt == vk::Format::eR16Unorm) { return vk::Format::eD16Unorm; } - UNREACHABLE(); + UNREACHABLE_MSG("Unexpected depth format {}", vk::to_string(fmt)); } } // namespace Vulkan::LiverpoolToVK diff --git a/src/video_core/renderer_vulkan/vk_platform.h b/src/video_core/renderer_vulkan/vk_platform.h index 0f70312ed..6a6ebeb15 100644 --- a/src/video_core/renderer_vulkan/vk_platform.h +++ b/src/video_core/renderer_vulkan/vk_platform.h @@ -5,7 +5,9 @@ #include +#include "common/assert.h" #include "common/logging/log.h" +#include "common/string_literal.h" #include "common/types.h" #include "video_core/renderer_vulkan/vk_common.h" @@ -48,4 +50,25 @@ void SetObjectName(vk::Device device, const HandleType& handle, const char* form SetObjectName(device, handle, debug_name); } +template +static void Check(vk::Result r) { + if constexpr (msg.len <= 1) { + ASSERT_MSG(r == vk::Result::eSuccess, "vk::Result={}", vk::to_string(r)); + } else { + ASSERT_MSG(r == vk::Result::eSuccess, "Failed to {}: vk::Result={}", msg.value, + vk::to_string(r)); + } +} + +template +static T Check(vk::ResultValue r) { + if constexpr (msg.len <= 1) { + ASSERT_MSG(r.result == vk::Result::eSuccess, "vk::Result={}", vk::to_string(r.result)); + } else { + ASSERT_MSG(r.result == vk::Result::eSuccess, "Failed to {}: vk::Result={}", msg.value, + vk::to_string(r.result)); + } + return std::move(r.value); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 04d0e7ac9..4a6a5c7c2 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -6,21 +6,22 @@ #include "common/singleton.h" #include "core/debug_state.h" #include "core/devtools/layer.h" -#include "core/file_format/splash.h" #include "core/libraries/system/systemservice.h" #include "imgui/renderer/imgui_core.h" #include "sdl_window.h" +#include "video_core/renderer_vulkan/vk_platform.h" #include "video_core/renderer_vulkan/vk_presenter.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/texture_cache/image.h" #include "video_core/host_shaders/fs_tri_vert.h" -#include "video_core/host_shaders/post_process_frag.h" #include #include + +#include "common/elf_info.h" #include "imgui/renderer/imgui_impl_vulkan.h" namespace Vulkan { @@ -106,191 +107,6 @@ static vk::Rect2D FitImage(s32 frame_width, s32 frame_height, s32 swapchain_widt dst_rect.offset.x, dst_rect.offset.y); } -void Presenter::CreatePostProcessPipeline() { - static const std::array pp_shaders{ - HostShaders::FS_TRI_VERT, - HostShaders::POST_PROCESS_FRAG, - }; - - boost::container::static_vector bindings{ - { - .binding = 0, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .descriptorCount = 1, - .stageFlags = vk::ShaderStageFlagBits::eFragment, - }, - }; - - const vk::DescriptorSetLayoutCreateInfo desc_layout_ci = { - .flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR, - .bindingCount = static_cast(bindings.size()), - .pBindings = bindings.data(), - }; - auto desc_layout_result = instance.GetDevice().createDescriptorSetLayoutUnique(desc_layout_ci); - ASSERT_MSG(desc_layout_result.result == vk::Result::eSuccess, - "Failed to create descriptor set layout: {}", - vk::to_string(desc_layout_result.result)); - pp_desc_set_layout = std::move(desc_layout_result.value); - - const vk::PushConstantRange push_constants = { - .stageFlags = vk::ShaderStageFlagBits::eFragment, - .offset = 0, - .size = sizeof(PostProcessSettings), - }; - - const auto& vs_module = - Vulkan::Compile(pp_shaders[0], vk::ShaderStageFlagBits::eVertex, instance.GetDevice()); - ASSERT(vs_module); - Vulkan::SetObjectName(instance.GetDevice(), vs_module, "fs_tri.vert"); - - const auto& fs_module = - Vulkan::Compile(pp_shaders[1], vk::ShaderStageFlagBits::eFragment, instance.GetDevice()); - ASSERT(fs_module); - Vulkan::SetObjectName(instance.GetDevice(), fs_module, "post_process.frag"); - - const std::array shaders_ci{ - vk::PipelineShaderStageCreateInfo{ - .stage = vk::ShaderStageFlagBits::eVertex, - .module = vs_module, - .pName = "main", - }, - vk::PipelineShaderStageCreateInfo{ - .stage = vk::ShaderStageFlagBits::eFragment, - .module = fs_module, - .pName = "main", - }, - }; - - const vk::DescriptorSetLayout set_layout = *pp_desc_set_layout; - const vk::PipelineLayoutCreateInfo layout_info = { - .setLayoutCount = 1U, - .pSetLayouts = &set_layout, - .pushConstantRangeCount = 1, - .pPushConstantRanges = &push_constants, - }; - auto [layout_result, layout] = instance.GetDevice().createPipelineLayoutUnique(layout_info); - ASSERT_MSG(layout_result == vk::Result::eSuccess, "Failed to create pipeline layout: {}", - vk::to_string(layout_result)); - pp_pipeline_layout = std::move(layout); - - const std::array pp_color_formats{ - vk::Format::eB8G8R8A8Unorm, // swapchain.GetSurfaceFormat().format, - }; - const vk::PipelineRenderingCreateInfoKHR pipeline_rendering_ci = { - .colorAttachmentCount = 1u, - .pColorAttachmentFormats = pp_color_formats.data(), - }; - - const vk::PipelineVertexInputStateCreateInfo vertex_input_info = { - .vertexBindingDescriptionCount = 0u, - .vertexAttributeDescriptionCount = 0u, - }; - - const vk::PipelineInputAssemblyStateCreateInfo input_assembly = { - .topology = vk::PrimitiveTopology::eTriangleList, - }; - - const vk::Viewport viewport = { - .x = 0.0f, - .y = 0.0f, - .width = 1.0f, - .height = 1.0f, - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - - const vk::Rect2D scissor = { - .offset = {0, 0}, - .extent = {1, 1}, - }; - - const vk::PipelineViewportStateCreateInfo viewport_info = { - .viewportCount = 1, - .pViewports = &viewport, - .scissorCount = 1, - .pScissors = &scissor, - }; - - const vk::PipelineRasterizationStateCreateInfo raster_state = { - .depthClampEnable = false, - .rasterizerDiscardEnable = false, - .polygonMode = vk::PolygonMode::eFill, - .cullMode = vk::CullModeFlagBits::eBack, - .frontFace = vk::FrontFace::eClockwise, - .depthBiasEnable = false, - .lineWidth = 1.0f, - }; - - const vk::PipelineMultisampleStateCreateInfo multisampling = { - .rasterizationSamples = vk::SampleCountFlagBits::e1, - }; - - const std::array attachments{ - vk::PipelineColorBlendAttachmentState{ - .blendEnable = false, - .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | - vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, - }, - }; - - const vk::PipelineColorBlendStateCreateInfo color_blending = { - .logicOpEnable = false, - .logicOp = vk::LogicOp::eCopy, - .attachmentCount = attachments.size(), - .pAttachments = attachments.data(), - .blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f}, - }; - - const std::array dynamic_states = { - vk::DynamicState::eViewport, - vk::DynamicState::eScissor, - }; - - const vk::PipelineDynamicStateCreateInfo dynamic_info = { - .dynamicStateCount = static_cast(dynamic_states.size()), - .pDynamicStates = dynamic_states.data(), - }; - - const vk::GraphicsPipelineCreateInfo pipeline_info = { - .pNext = &pipeline_rendering_ci, - .stageCount = static_cast(shaders_ci.size()), - .pStages = shaders_ci.data(), - .pVertexInputState = &vertex_input_info, - .pInputAssemblyState = &input_assembly, - .pViewportState = &viewport_info, - .pRasterizationState = &raster_state, - .pMultisampleState = &multisampling, - .pColorBlendState = &color_blending, - .pDynamicState = &dynamic_info, - .layout = *pp_pipeline_layout, - }; - - auto result = instance.GetDevice().createGraphicsPipelineUnique( - /*pipeline_cache*/ {}, pipeline_info); - if (result.result == vk::Result::eSuccess) { - pp_pipeline = std::move(result.value); - } else { - UNREACHABLE_MSG("Post process pipeline creation failed!"); - } - - // Once pipeline is compiled, we don't need the shader module anymore - instance.GetDevice().destroyShaderModule(vs_module); - instance.GetDevice().destroyShaderModule(fs_module); - - // Create sampler resource - const vk::SamplerCreateInfo sampler_ci = { - .magFilter = vk::Filter::eLinear, - .minFilter = vk::Filter::eLinear, - .mipmapMode = vk::SamplerMipmapMode::eNearest, - .addressModeU = vk::SamplerAddressMode::eClampToEdge, - .addressModeV = vk::SamplerAddressMode::eClampToEdge, - }; - auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci); - ASSERT_MSG(sampler_result == vk::Result::eSuccess, "Failed to create sampler: {}", - vk::to_string(sampler_result)); - pp_sampler = std::move(smplr); -} - Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_) : window{window_}, liverpool{liverpool_}, instance{window, Config::getGpuId(), Config::vkValidationEnabled(), @@ -306,15 +122,15 @@ Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_ present_frames.resize(num_images); for (u32 i = 0; i < num_images; i++) { Frame& frame = present_frames[i]; - auto [fence_result, fence] = - device.createFence({.flags = vk::FenceCreateFlagBits::eSignaled}); - ASSERT_MSG(fence_result == vk::Result::eSuccess, "Failed to create present done fence: {}", - vk::to_string(fence_result)); + frame.id = i; + auto fence = Check<"create present done fence">( + device.createFence({.flags = vk::FenceCreateFlagBits::eSignaled})); frame.present_done = fence; free_queue.push(&frame); } - CreatePostProcessPipeline(); + fsr_pass.Create(device, instance.GetAllocator(), num_images); + pp_pass.Create(device); ImGui::Layer::AddLayer(Common::Singleton::Instance()); } @@ -376,6 +192,7 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { UNREACHABLE(); } frame->image = vk::Image{unsafe_image}; + SetObjectName(device, frame->image, "Frame image #{}", frame->id); const vk::ImageViewCreateInfo view_info = { .image = frame->image, @@ -389,9 +206,7 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { .layerCount = 1, }, }; - auto [view_result, view] = device.createImageView(view_info); - ASSERT_MSG(view_result == vk::Result::eSuccess, "Failed to create frame image view: {}", - vk::to_string(view_result)); + auto view = Check<"create frame image view">(device.createImageView(view_info)); frame->image_view = view; frame->width = width; frame->height = height; @@ -455,127 +270,19 @@ Frame* Presenter::PrepareLastFrame() { return frame; } -bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { - const auto* splash = Common::Singleton::Instance(); - if (splash->GetImageData().empty()) { - return false; - } - - if (!Libraries::SystemService::IsSplashVisible()) { - return false; - } - - draw_scheduler.EndRendering(); - const auto cmdbuf = draw_scheduler.CommandBuffer(); - - if (Config::getVkHostMarkersEnabled()) { - cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ - .pLabelName = "ShowSplash", - }); - } - - if (!frame) { - if (!splash_img.has_value()) { - VideoCore::ImageInfo info{}; - info.pixel_format = vk::Format::eR8G8B8A8Unorm; - info.type = vk::ImageType::e2D; - info.size = - VideoCore::Extent3D{splash->GetImageInfo().width, splash->GetImageInfo().height, 1}; - info.pitch = splash->GetImageInfo().width; - info.guest_address = VAddr(splash->GetImageData().data()); - info.guest_size = splash->GetImageData().size(); - info.mips_layout.emplace_back(splash->GetImageData().size(), - splash->GetImageInfo().width, - splash->GetImageInfo().height, 0); - splash_img.emplace(instance, present_scheduler, info); - splash_img->flags &= ~VideoCore::GpuDirty; - texture_cache.RefreshImage(*splash_img); - - splash_img->Transit(vk::ImageLayout::eTransferSrcOptimal, - vk::AccessFlagBits2::eTransferRead, {}, cmdbuf); - } - - frame = GetRenderFrame(); - } - - const auto frame_subresources = vk::ImageSubresourceRange{ - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }; - - const auto pre_barrier = - vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferRead, - .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, - .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, - .oldLayout = vk::ImageLayout::eUndefined, - .newLayout = vk::ImageLayout::eTransferDstOptimal, - .image = frame->image, - .subresourceRange{frame_subresources}}; - - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &pre_barrier, - }); - - cmdbuf.blitImage(splash_img->image, vk::ImageLayout::eTransferSrcOptimal, frame->image, - vk::ImageLayout::eTransferDstOptimal, - MakeImageBlitFit(splash->GetImageInfo().width, splash->GetImageInfo().height, - frame->width, frame->height), - vk::Filter::eLinear); - - const auto post_barrier = - vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, - .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, - .oldLayout = vk::ImageLayout::eTransferDstOptimal, - .newLayout = vk::ImageLayout::eGeneral, - .image = frame->image, - .subresourceRange{frame_subresources}}; - - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &post_barrier, - }); - - if (Config::getVkHostMarkersEnabled()) { - cmdbuf.endDebugUtilsLabelEXT(); - } - - // Flush frame creation commands. - frame->ready_semaphore = draw_scheduler.GetMasterSemaphore()->Handle(); - frame->ready_tick = draw_scheduler.CurrentTick(); - SubmitInfo info{}; - draw_scheduler.Flush(info); - - Present(frame); - return true; -} - Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) { // Request a free presentation frame. Frame* frame = GetRenderFrame(); - if (image_id != VideoCore::NULL_IMAGE_ID) { - const auto& image = texture_cache.GetImage(image_id); - const auto extent = image.info.size; - if (frame->width != extent.width || frame->height != extent.height || - frame->is_hdr != swapchain.GetHDR()) { - RecreateFrame(frame, extent.width, extent.height); - } - } - // EOP flips are triggered from GPU thread so use the drawing scheduler to record // commands. Otherwise we are dealing with a CPU flip which could have arrived // from any guest thread. Use a separate scheduler for that. auto& scheduler = is_eop ? draw_scheduler : flip_scheduler; scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); - if (Config::getVkHostMarkersEnabled()) { + + bool vk_host_markers_enabled = Config::getVkHostMarkersEnabled(); + if (vk_host_markers_enabled) { const auto label = fmt::format("PrepareFrameInternal:{}", image_id.index); cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ .pLabelName = label.c_str(), @@ -605,33 +312,17 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) .pImageMemoryBarriers = &pre_barrier, }); - const std::array attachments = {vk::RenderingAttachmentInfo{ - .imageView = frame->image_view, - .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, - .loadOp = vk::AttachmentLoadOp::eClear, - .storeOp = vk::AttachmentStoreOp::eStore, - }}; - const vk::RenderingInfo rendering_info{ - .renderArea = - vk::Rect2D{ - .offset = {0, 0}, - .extent = {frame->width, frame->height}, - }, - .layerCount = 1, - .colorAttachmentCount = attachments.size(), - .pColorAttachments = attachments.data(), - }; - if (image_id != VideoCore::NULL_IMAGE_ID) { auto& image = texture_cache.GetImage(image_id); + vk::Extent2D image_size = {image.info.size.width, image.info.size.height}; + float ratio = (float)image_size.width / (float)image_size.height; + if (ratio != expected_ratio) { + expected_ratio = ratio; + } + image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {}, cmdbuf); - static vk::DescriptorImageInfo image_info{ - .sampler = *pp_sampler, - .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, - }; - VideoCore::ImageViewInfo info{}; info.format = image.info.pixel_format; // Exclude alpha from output frame to avoid blending with UI. @@ -641,52 +332,53 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) .b = vk::ComponentSwizzle::eIdentity, .a = vk::ComponentSwizzle::eOne, }; + vk::ImageView imageView; if (auto view = image.FindView(info)) { - image_info.imageView = *texture_cache.GetImageView(view).image_view; + imageView = *texture_cache.GetImageView(view).image_view; } else { - image_info.imageView = *texture_cache.RegisterImageView(image_id, info).image_view; + imageView = *texture_cache.RegisterImageView(image_id, info).image_view; } - static const std::array set_writes{ - vk::WriteDescriptorSet{ - .dstSet = VK_NULL_HANDLE, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .pImageInfo = &image_info, - }, - }; + if (vk_host_markers_enabled) { + cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ + .pLabelName = "Host/FSR", + }); + } - cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, *pp_pipeline); + imageView = fsr_pass.Render(cmdbuf, imageView, image_size, {frame->width, frame->height}, + fsr_settings, frame->is_hdr); - const auto& dst_rect = - FitImage(image.info.size.width, image.info.size.height, frame->width, frame->height); + if (vk_host_markers_enabled) { + cmdbuf.endDebugUtilsLabelEXT(); + cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ + .pLabelName = "Host/Post processing", + }); + } + pp_pass.Render(cmdbuf, imageView, image_size, *frame, pp_settings); + if (vk_host_markers_enabled) { + cmdbuf.endDebugUtilsLabelEXT(); + } - const std::array viewports = { - vk::Viewport{ - .x = 1.0f * dst_rect.offset.x, - .y = 1.0f * dst_rect.offset.y, - .width = 1.0f * dst_rect.extent.width, - .height = 1.0f * dst_rect.extent.height, - .minDepth = 0.0f, - .maxDepth = 1.0f, - }, - }; - - cmdbuf.setViewport(0, viewports); - cmdbuf.setScissor(0, {dst_rect}); - - cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, *pp_pipeline_layout, 0, - set_writes); - cmdbuf.pushConstants(*pp_pipeline_layout, vk::ShaderStageFlagBits::eFragment, 0, - sizeof(PostProcessSettings), &pp_settings); - - cmdbuf.beginRendering(rendering_info); - cmdbuf.draw(3, 1, 0, 0); - cmdbuf.endRendering(); + DebugState.game_resolution = {image_size.width, image_size.height}; + DebugState.output_resolution = {frame->width, frame->height}; } else { // Fix display of garbage images on startup on some drivers + const std::array attachments = {{ + { + .imageView = frame->image_view, + .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, + .loadOp = vk::AttachmentLoadOp::eClear, + .storeOp = vk::AttachmentStoreOp::eStore, + }, + }}; + const vk::RenderingInfo rendering_info{ + .renderArea{ + .extent{frame->width, frame->height}, + }, + .layerCount = 1, + .colorAttachmentCount = attachments.size(), + .pColorAttachments = attachments.data(), + }; cmdbuf.beginRendering(rendering_info); cmdbuf.endRendering(); } @@ -706,7 +398,7 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) .pImageMemoryBarriers = &post_barrier, }); - if (Config::getVkHostMarkersEnabled()) { + if (vk_host_markers_enabled) { cmdbuf.endDebugUtilsLabelEXT(); } @@ -833,17 +525,43 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) { ImGui::SetNextWindowDockID(dockId, ImGuiCond_Once); ImGui::Begin("Display##game_display", nullptr, ImGuiWindowFlags_NoNav); + auto game_texture = frame->imgui_texture; + auto game_width = frame->width; + auto game_height = frame->height; + + if (Libraries::SystemService::IsSplashVisible()) { // draw splash + if (!splash_img.has_value()) { + splash_img.emplace(); + auto splash_path = Common::ElfInfo::Instance().GetSplashPath(); + if (!splash_path.empty()) { + splash_img = ImGui::RefCountedTexture::DecodePngFile(splash_path); + } + } + if (auto& splash_image = this->splash_img.value()) { + auto [im_id, width, height] = splash_image.GetTexture(); + game_texture = im_id; + game_width = width; + game_height = height; + } + } + ImVec2 contentArea = ImGui::GetContentRegionAvail(); - const vk::Rect2D imgRect = - FitImage(frame->width, frame->height, (s32)contentArea.x, (s32)contentArea.y); - ImGui::SetCursorPos(ImGui::GetCursorStartPos() + ImVec2{ - (float)imgRect.offset.x, - (float)imgRect.offset.y, - }); - ImGui::Image(frame->imgui_texture, { - static_cast(imgRect.extent.width), - static_cast(imgRect.extent.height), - }); + SetExpectedGameSize((s32)contentArea.x, (s32)contentArea.y); + + const auto imgRect = + FitImage(game_width, game_height, (s32)contentArea.x, (s32)contentArea.y); + ImVec2 offset{ + static_cast(imgRect.offset.x), + static_cast(imgRect.offset.y), + }; + ImVec2 size{ + static_cast(imgRect.extent.width), + static_cast(imgRect.extent.height), + }; + + ImGui::SetCursorPos(ImGui::GetCursorStartPos() + offset); + ImGui::Image(game_texture, size); + ImGui::End(); ImGui::PopStyleVar(3); ImGui::PopStyleColor(); @@ -914,12 +632,24 @@ Frame* Presenter::GetRenderFrame() { } } - // Initialize default frame image - if (frame->width == 0 || frame->height == 0 || frame->is_hdr != swapchain.GetHDR()) { - RecreateFrame(frame, Config::getScreenWidth(), Config::getScreenHeight()); + if (frame->width != expected_frame_width || frame->height != expected_frame_height || + frame->is_hdr != swapchain.GetHDR()) { + RecreateFrame(frame, expected_frame_width, expected_frame_height); } return frame; } +void Presenter::SetExpectedGameSize(s32 width, s32 height) { + const float ratio = (float)width / (float)height; + + expected_frame_height = height; + expected_frame_width = width; + if (ratio > expected_ratio) { + expected_frame_width = static_cast(height * expected_ratio); + } else { + expected_frame_height = static_cast(width / expected_ratio); + } +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_presenter.h b/src/video_core/renderer_vulkan/vk_presenter.h index 2bfe6e66c..ad2708474 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -6,7 +6,10 @@ #include #include "imgui/imgui_config.h" +#include "imgui/imgui_texture.h" #include "video_core/amdgpu/liverpool.h" +#include "video_core/renderer_vulkan/host_passes/fsr_pass.h" +#include "video_core/renderer_vulkan/host_passes/pp_pass.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_swapchain.h" @@ -32,6 +35,7 @@ struct Frame { vk::Semaphore ready_semaphore; u64 ready_tick; bool is_hdr{false}; + u8 id{}; ImTextureID imgui_texture; }; @@ -45,17 +49,16 @@ enum SchedulerType { class Rasterizer; class Presenter { - struct PostProcessSettings { - float gamma = 1.0f; - u32 hdr = 0; - }; - public: Presenter(Frontend::WindowSDL& window, AmdGpu::Liverpool* liverpool); ~Presenter(); - float& GetGammaRef() { - return pp_settings.gamma; + HostPasses::PostProcessingPass::Settings& GetPPSettingsRef() { + return pp_settings; + } + + HostPasses::FsrPass::Settings& GetFsrSettingsRef() { + return fsr_settings; } Frontend::WindowSDL& GetWindow() const { @@ -90,7 +93,6 @@ public: }) != vo_buffers_addr.cend(); } - bool ShowSplash(Frame* frame = nullptr); void Present(Frame* frame, bool is_reusing_frame = false); void RecreateFrame(Frame* frame, u32 width, u32 height); Frame* PrepareLastFrame(); @@ -117,16 +119,20 @@ public: } private: - void CreatePostProcessPipeline(); Frame* PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop = true); Frame* GetRenderFrame(); + void SetExpectedGameSize(s32 width, s32 height); + private: - PostProcessSettings pp_settings{}; - vk::UniquePipeline pp_pipeline{}; - vk::UniquePipelineLayout pp_pipeline_layout{}; - vk::UniqueDescriptorSetLayout pp_desc_set_layout{}; - vk::UniqueSampler pp_sampler{}; + float expected_ratio{1920.0 / 1080.0f}; + u32 expected_frame_width{1920}; + u32 expected_frame_height{1080}; + + HostPasses::FsrPass fsr_pass; + HostPasses::FsrPass::Settings fsr_settings{}; + HostPasses::PostProcessingPass::Settings pp_settings{}; + HostPasses::PostProcessingPass pp_pass; Frontend::WindowSDL& window; AmdGpu::Liverpool* liverpool; Instance instance; @@ -143,7 +149,7 @@ private: std::mutex free_mutex; std::condition_variable free_cv; std::condition_variable_any frame_cv; - std::optional splash_img; + std::optional splash_img; std::vector vo_buffers_addr; }; diff --git a/src/video_core/renderer_vulkan/vk_shader_util.cpp b/src/video_core/renderer_vulkan/vk_shader_util.cpp index 08703c3de..1eb9b27c6 100644 --- a/src/video_core/renderer_vulkan/vk_shader_util.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_util.cpp @@ -159,7 +159,8 @@ bool InitializeCompiler() { } } // Anonymous namespace -vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device) { +vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device, + std::vector defines) { if (!InitializeCompiler()) { return {}; } @@ -178,12 +179,46 @@ vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, v glslang::EShTargetLanguageVersion::EShTargetSpv_1_3); shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1); + std::string preambleString; + std::vector processes; + + for (auto& def : defines) { + processes.push_back("define-macro "); + processes.back().append(def); + + preambleString.append("#define "); + if (const size_t equal = def.find_first_of("="); equal != def.npos) { + def[equal] = ' '; + } + preambleString.append(def); + preambleString.append("\n"); + } + + shader->setPreamble(preambleString.c_str()); + shader->addProcesses(processes); + glslang::TShader::ForbidIncluder includer; + + std::string preprocessedStr; + if (!shader->preprocess(&DefaultTBuiltInResource, default_version, profile, false, true, + messages, &preprocessedStr, includer)) [[unlikely]] { + LOG_ERROR(Render_Vulkan, + "Shader preprocess error\n" + "Shader Info Log:\n" + "{}\n{}", + shader->getInfoLog(), shader->getInfoDebugLog()); + LOG_ERROR(Render_Vulkan, "Shader Source:\n{}", code); + return {}; + } + if (!shader->parse(&DefaultTBuiltInResource, default_version, profile, false, true, messages, includer)) [[unlikely]] { - LOG_INFO(Render_Vulkan, "Shader Info Log:\n{}\n{}", shader->getInfoLog(), - shader->getInfoDebugLog()); - LOG_INFO(Render_Vulkan, "Shader Source:\n{}", code); + LOG_ERROR(Render_Vulkan, + "Shader parse error\n" + "Shader Info Log:\n" + "{}\n{}", + shader->getInfoLog(), shader->getInfoDebugLog()); + LOG_ERROR(Render_Vulkan, "Shader Source:\n{}", code); return {}; } @@ -191,8 +226,11 @@ vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, v auto program = std::make_unique(); program->addShader(shader.get()); if (!program->link(messages)) { - LOG_INFO(Render_Vulkan, "Program Info Log:\n{}\n{}", program->getInfoLog(), - program->getInfoDebugLog()); + LOG_ERROR(Render_Vulkan, + "Shader link error\n" + "Program Info Log:\n" + "{}\n{}", + program->getInfoLog(), program->getInfoDebugLog()); return {}; } diff --git a/src/video_core/renderer_vulkan/vk_shader_util.h b/src/video_core/renderer_vulkan/vk_shader_util.h index 3a86acf2b..14b929782 100644 --- a/src/video_core/renderer_vulkan/vk_shader_util.h +++ b/src/video_core/renderer_vulkan/vk_shader_util.h @@ -16,7 +16,8 @@ namespace Vulkan { * @param stage The pipeline stage the shader will be used in. * @param device The vulkan device handle. */ -vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device); +vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device, + std::vector defines = {}); /** * @brief Creates a vulkan shader module from SPIR-V bytecode. diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index 3c85c451c..522e6fd5b 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -113,6 +113,8 @@ static vk::FormatFeatureFlags2 FormatFeatureFlags(const vk::ImageUsageFlags usag return feature_flags; } +UniqueImage::UniqueImage() {} + UniqueImage::UniqueImage(vk::Device device_, VmaAllocator allocator_) : device{device_}, allocator{allocator_} {} @@ -123,6 +125,9 @@ UniqueImage::~UniqueImage() { } void UniqueImage::Create(const vk::ImageCreateInfo& image_ci) { + if (image) { + vmaDestroyImage(allocator, image, allocation); + } const VmaAllocationCreateInfo alloc_info = { .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT, .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, diff --git a/src/video_core/texture_cache/image.h b/src/video_core/texture_cache/image.h index 66d65ceec..404e25e88 100644 --- a/src/video_core/texture_cache/image.h +++ b/src/video_core/texture_cache/image.h @@ -35,6 +35,7 @@ enum ImageFlagBits : u32 { DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) struct UniqueImage { + explicit UniqueImage(); explicit UniqueImage(vk::Device device, VmaAllocator allocator); ~UniqueImage();